fix(db): close round-7 review evasions in writer-coverage assertion
ci/woodpecker/pr/ci Pipeline was successful

The text-only template-key rule fires on any computed member — access or
call — not only the call form, so template-keyed write targets, export
expressions, and receivers fail closed at their origin. Declaration
exports and derivations tolerate a type annotation and prior declarators
via shared TYPE_ANN/DECL_LIST shapes at all five declarator sites.
Invoking a write/exec verb through .apply/.call/.bind fails closed
anywhere (both member names statically visible, unlike the
method-extraction residual). The evasion test counts violations across
the whole synthetic chain, since some routes fail closed at the helper.
Controls E82-E92; clean control pinning the interpolated-key
discriminator.
This commit is contained in:
fred
2026-08-27 18:37:53 -05:00
parent 4cad960796
commit f9a05bba92
+163 -23
View File
@@ -15,7 +15,10 @@
* (`export { companies as c } from`), an export of a locally bound
* alias (`import { companies }; export { companies as co }`), and a
* binding derived from a namespace (`export const co = ns.companies`)
* all propagate symbol identity to the consumer. The DEFAULT-export
* all propagate symbol identity to the consumer. Declaration exports
* tolerate a type annotation and prior declarators
* (`export const co: typeof companies = companies`,
* `export const dummy = 0, co = companies`). The DEFAULT-export
* slot is an export name like any other (`export { companies as
* default } from`, `export default companies`), so a bare default
* import of a conduit resolves against the map too; namespace-member
@@ -79,12 +82,15 @@
* residual is accepted and reviews of DI provider modules carry it.
* - Computed member access with a NON-literal name (obj[verb]()) is not
* statically resolvable; literal computed access (obj['insert']()) is
* flagged, and a computed member CALL whose key is a text-only template
* literal (obj[`insert`]()) fails closed anywhere — template text never
* reaches the lexer's code output, so such a call is indistinguishable
* from a runtime-constructed verb (a template key WITH interpolation is
* a non-literal computed member, above). Constructing the verb at
* runtime is adjacent to eval and is expected to be caught in review.
* flagged, and a computed member — access or call, in any position —
* whose key is a text-only template literal (obj[`insert`](),
* ns[`companies`]) fails closed anywhere — template text never reaches
* the lexer's code output, so such a member is indistinguishable from a
* runtime-constructed one (a template key WITH interpolation is a
* non-literal computed member, above). Invoking a write/exec verb via
* `.apply`/`.call`/`.bind` likewise fails closed anywhere. Constructing
* the member at runtime is adjacent to eval and is expected to be caught
* in review.
* - The DB_FACTORY_IMPORTERS enumeration counts the import edges
* hasSymbolImportEdge can see (named import, literal dynamic package
* import with symbol use, namespace member use). Destructuring a factory
@@ -492,6 +498,19 @@ function stripExprDressing(raw: string): string {
/** Call-open shape tolerant of the optional-call form: `f(…)` or `f?.(…)`. */
const CALL_OPEN = `\\s*(?:\\?\\.)?\\s*\\(`;
/**
* Declaration-head shapes: a declarator's initializer may sit behind a type
* annotation (`export const co: typeof companies = companies`) or behind
* prior declarators (`export const dummy = 0, co = companies`). TYPE_ANN
* admits `=>` inside the type text but stops at a bare `=` (the
* initializer); DECL_LIST skips prior declarators whose initializers are
* comma-free. Both are approximations of the declarator grammar — exotic
* prior initializers (an array or call containing a comma) fall to the
* value-flow residual.
*/
const TYPE_ANN = `(?:\\s*:\\s*(?:[^=;\\n]|=>)*?)?`;
const DECL_LIST = `(?:[\\w$]+${TYPE_ANN}\\s*=\\s*[^,;\\n]*,\\s*)*`;
interface FileFacts {
rel: string;
code: string;
@@ -591,7 +610,7 @@ function computeSchemaConduits(files: FileFacts[], fileSet: Set<string>): Schema
for (const ns of aliases.namespaces) {
for (const m of f.code.matchAll(
new RegExp(
`export\\s+(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${ns}${MEMBER_SEG}*${memberTail(CLASS_SYMBOLS.join('|'))}`,
`export\\s+(?:const|let|var)\\s+${DECL_LIST}(\\w+)${TYPE_ANN}\\s*=\\s*\\(*\\s*${ns}${MEMBER_SEG}*${memberTail(CLASS_SYMBOLS.join('|'))}`,
'g',
),
)) {
@@ -600,7 +619,10 @@ function computeSchemaConduits(files: FileFacts[], fileSet: Set<string>): Schema
}
for (const local of aliases.named) {
for (const m of f.code.matchAll(
new RegExp(`export\\s+(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${local}\\b`, 'g'),
new RegExp(
`export\\s+(?:const|let|var)\\s+${DECL_LIST}(\\w+)${TYPE_ANN}\\s*=\\s*\\(*\\s*${local}\\b`,
'g',
),
)) {
mine.named.add(m[1]!);
}
@@ -734,7 +756,7 @@ function classAliases(f: FileFacts, conduits: SchemaConduits, fileSet: Set<strin
}
for (const m of f.code.matchAll(
new RegExp(
`(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${ns}${MEMBER_SEG}*(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
`(?:const|let|var)\\s+${DECL_LIST}(\\w+)${TYPE_ANN}\\s*=\\s*\\(*\\s*${ns}${MEMBER_SEG}*(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
'g',
),
)) {
@@ -851,7 +873,7 @@ function computeCapabilityConduits(
for (const [ns, src] of nss) {
for (const am of f.code.matchAll(
new RegExp(
`(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${ns}(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
`(?:const|let|var)\\s+${DECL_LIST}(\\w+)${TYPE_ANN}\\s*=\\s*\\(*\\s*${ns}(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
'g',
),
)) {
@@ -868,13 +890,20 @@ function computeCapabilityConduits(
}
}
for (const local of locals) {
if (new RegExp(`export\\s+(?:const|let|var|function)\\s+${local}\\b`).test(f.code)) {
if (
new RegExp(`export\\s+(?:const|let|var|function)\\s+${DECL_LIST}${local}\\b`).test(
f.code,
)
) {
mine.add(local);
}
// A derived binding exported under a NEW name re-exports the
// capability under that name: `export const mk2 = (mk);`
for (const dm of f.code.matchAll(
new RegExp(`export\\s+(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${local}\\b`, 'g'),
new RegExp(
`export\\s+(?:const|let|var)\\s+${DECL_LIST}(\\w+)${TYPE_ANN}\\s*=\\s*\\(*\\s*${local}\\b`,
'g',
),
)) {
mine.add(dm[1]!);
}
@@ -958,18 +987,41 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
) {
violations.push({ file: rel, prong: 'code-construction', detail: 'eval/new Function' });
}
// A computed member CALL whose key is a text-only template literal is
// invisible to every verb matcher (the lexer routes template text to
// spans, so the key survives in code as two ADJACENT backticks — the
// discriminator against backticks inside quoted-string prose, whose text
// stays in code). Runtime code construction's sibling: fail-closed
// anywhere. A template key WITH interpolation is a non-literal computed
// member (documented residual above).
if (new RegExp(`\\[\\s*\`\`\\s*\\]${CALL_OPEN}`).test(code)) {
// A computed member — access or call — whose key is a text-only template
// literal is invisible to every member and verb matcher (the lexer routes
// template text to spans, so the key survives in code as two ADJACENT
// backticks — the discriminator against backticks inside quoted-string
// prose, whose text stays in code, and against interpolated keys, whose
// `${…}` expression stays in code between the backticks). Runtime code
// construction's sibling: fail-closed anywhere, in any position (write
// target, export expression, receiver, call). A template key WITH
// interpolation is a non-literal computed member (documented residual
// above).
if (new RegExp(`\\[\\s*\`\`\\s*\\]`).test(code)) {
violations.push({
file: rel,
prong: 'code-construction',
detail: 'template-literal computed member call',
detail: 'template-literal computed member',
});
}
// Invoking a write/exec verb through Function.prototype indirection
// (`db.insert.apply(db, [companies])`, `d.execute.call(d, s)`,
// `db.insert.bind(db)`) hides the argument shape from every verb matcher
// while both member names stay statically visible — unlike the
// method-EXTRACTION residual, where the verb never appears as a member.
// Fail-closed anywhere: no legitimate site invokes a builder verb this way
// (calibrated clean over the production tree).
if (
new RegExp(
`(?:${DOT}(?:insert|update|delete|execute|query|unsafe|raw)\\b|` +
`\\[\\s*['"](?:insert|update|delete|execute|query|unsafe|raw)['"]\\s*\\])` +
`${DOT}(?:apply|call|bind)${CALL_OPEN}`,
).test(code)
) {
violations.push({
file: rel,
prong: 'code-construction',
detail: 'verb apply/call/bind indirection',
});
}
// Dynamic import whose specifier is not a single string literal: the
@@ -1900,6 +1952,86 @@ describe('hierarchy writer coverage (contract 1 §6.3b)', () => {
name: 'E81 template-literal computed member call',
src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await db[\`insert\`](companies).values({}); }`,
},
{
name: 'E82 template-literal computed member as write target',
src: `import * as ns from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await db.insert(ns[\`companies\`]).values({}); }`,
},
{
name: 'E83 template-literal computed member in schema default export',
src: `import c from './evasion-mid35.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(c).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid35.ts',
src: `import * as ns from '@mosaicstack/db';\nexport default ns[\`companies\`];`,
},
],
},
{
name: 'E84 template-literal computed member in factory extraction',
src: `import { mk } from './evasion-mid36.js';\nexport async function f(t: string) { await mk('u').execute('DELETE FROM ' + t); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid36.ts',
src: `import * as ns from '@mosaicstack/db';\nexport const mk = ns[\`createDb\`];`,
},
],
},
{
name: 'E85 type-annotated re-export of schema binding',
src: `import { co } from './evasion-mid37.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(co).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid37.ts',
src: `import { companies } from '@mosaicstack/db';\nexport const co: typeof companies = companies;`,
},
],
},
{
name: 'E86 second-declarator re-export of schema binding',
src: `import { co } from './evasion-mid38.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(co).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid38.ts',
src: `import { companies } from '@mosaicstack/db';\nexport const dummy = 0,\n co = companies;`,
},
],
},
{
name: 'E87 type-annotated re-export of factory binding',
src: `import { mk } from './evasion-mid39.js';\nexport async function f(t: string) { await mk('u').execute('DELETE FROM ' + t); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid39.ts',
src: `import { createDb } from '@mosaicstack/db';\nexport const mk: typeof createDb = createDb;`,
},
],
},
{
name: 'E88 second-declarator re-export of factory binding',
src: `import { mk } from './evasion-mid40.js';\nexport async function f(t: string) { await mk('u').execute('DELETE FROM ' + t); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid40.ts',
src: `import { createDb } from '@mosaicstack/db';\nexport const d0 = 0,\n mk = createDb;`,
},
],
},
{
name: 'E89 apply-invoked write verb',
src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await db.insert.apply(db, [companies]).values({}); }`,
},
{
name: 'E90 call-invoked write verb',
src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await db.insert.call(db, companies).values({}); }`,
},
{
name: 'E91 apply-invoked bracket verb in capability-free file',
src: `export class R { constructor(private pool: { execute(s: string): Promise<unknown> }) {}\n async f(t: string) { await this.pool['execute'].apply(this.pool, ['TRUNCATE ' + t]); } }`,
},
{
name: 'E92 template-keyed receiver verb in capability-free file',
src: `export class R { constructor(private pool: { execute(s: string): Promise<unknown> }) {}\n async f(t: string) { await this.pool[\`execute\`].apply(this.pool, ['TRUNCATE ' + t]); } }`,
},
];
const CLEAN_CONTROLS: Array<{ name: string; src: string }> = [
{
@@ -1910,6 +2042,10 @@ describe('hierarchy writer coverage (contract 1 §6.3b)', () => {
name: 'clean: comment mentioning a table is not SQL',
src: `// syncs hierarchy_grants downstream\nexport const n = 1;`,
},
{
name: 'clean: interpolated template key is a non-literal computed member, not a text-only key',
src: `export function g(o: Record<string, () => void>, k: string) { o[\`\${k}\`](); }`,
},
];
it('analyzer flags every known evasion form', () => {
@@ -1934,7 +2070,11 @@ describe('hierarchy writer coverage (contract 1 §6.3b)', () => {
driverConduits: synthCap.driver,
factoryConduits: synthCap.factory,
};
const v = analyzeFile(synthetic, synthCtx);
// An evasion is caught when ANY file in the chain is flagged: some
// laundering routes fail closed at the HELPER (origin), not the
// consumer — e.g. a template-keyed conduit export — which keeps the
// chain out of the tree just as effectively.
const v = [synthetic, ...extraFacts].flatMap((ff) => analyzeFile(ff, synthCtx));
expect(v.length, `evasion not caught: ${e.name}`).toBeGreaterThan(0);
}
});