Summary
extensions(...) are assigned onto the scoped DB object with Object.assign(...). An extension can accidentally or intentionally overwrite guarded core methods/properties such as select, insert, update, delete, query, transaction, or the unsafe/raw escape property. That can silently bypass the scoped facade.
Evidence
Relevant code: src/scoped-db.ts:
if (options.extensions) {
Object.assign(scoped, options.extensions(options.scopeValue, options.scopeName));
}
No reserved-key check is performed before assignment.
Risk example
const scopedDb = createScopedDb(db, {
scopeName: "workspace",
scopeValue: workspaceId,
rules: [scopeByColumn(projects, projects.workspaceId)],
extensions: () => ({
select: db.select.bind(db), // accidental name collision or attempted helper
}),
});
// Now this may call raw Drizzle select instead of the scoped select facade.
await scopedDb.select().from(projects);
Expected behavior
Extension keys should not be allowed to overwrite built-in scoped API methods or configured escape properties.
Suggested fix
Before assigning extensions, validate keys against a reserved set, including at least:
select
selectDistinct
selectDistinctOn
insert
update
delete
query
transaction
toJSON
- configured
unscopedDbPropertyName
- configured
scopeValueProperty, if present
If any extension key collides, throw a clear error at createScopedDb(...) time.
Also consider using Object.defineProperty for core methods as non-writable, but explicit validation provides better user errors.
Tests
- Unit test that
extensions: () => ({ select: () => ... }) throws a clear reserved-key error.
- Repeat for
query, transaction, custom unscopedDbPropertyName, and custom scopeValueProperty.
- Assert non-conflicting extension methods still work on root wrappers and transaction wrappers.
Summary
extensions(...)are assigned onto the scoped DB object withObject.assign(...). An extension can accidentally or intentionally overwrite guarded core methods/properties such asselect,insert,update,delete,query,transaction, or the unsafe/raw escape property. That can silently bypass the scoped facade.Evidence
Relevant code:
src/scoped-db.ts:No reserved-key check is performed before assignment.
Risk example
Expected behavior
Extension keys should not be allowed to overwrite built-in scoped API methods or configured escape properties.
Suggested fix
Before assigning extensions, validate keys against a reserved set, including at least:
selectselectDistinctselectDistinctOninsertupdatedeletequerytransactiontoJSONunscopedDbPropertyNamescopeValueProperty, if presentIf any extension key collides, throw a clear error at
createScopedDb(...)time.Also consider using
Object.definePropertyfor core methods as non-writable, but explicit validation provides better user errors.Tests
extensions: () => ({ select: () => ... })throws a clear reserved-key error.query,transaction, customunscopedDbPropertyName, and customscopeValueProperty.