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

stripExprDressing also strips trailing non-null assertions and satisfies
clauses, so export-default dressing (companies!, createDb satisfies T,
(x as unknown)!) resolves at both ends. The three ns-member right-hand
sides tolerate parenthesization. Every verb matcher admits the optional-
call form (?.() via a shared CALL_OPEN shape. A computed member call
whose key is a text-only template literal fails closed (its text never
reaches lexed code); interpolated keys stay under the non-literal
residual. Document the DB_FACTORY_IMPORTERS ns-destructure enumeration
blind spot as a residual. Controls E69-E81.
This commit is contained in:
fred
2026-08-27 18:12:45 -05:00
parent 743d884bfc
commit 4cad960796
+174 -23
View File
@@ -79,8 +79,20 @@
* 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. Constructing the verb at runtime is adjacent to eval and is
* expected to be caught in review.
* 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.
* - 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
* out of a schema namespace (`const { createDb } = dbns`) confers
* capability for prong (iii) but is NOT visible to the enumeration, so a
* file on that route joins the importer set only via review; prong (iii)
* still flags any execution in it. The enumeration is a measured set,
* not a soundness claim.
* - General VALUE FLOW is not modeled. The analyzer tracks import edges,
* re-export chains, and single-step alias/destructure derivations from a
* namespace binding (to depth 2) — not arbitrary assignment chains,
@@ -456,9 +468,11 @@ const MEMBER_SEG = `(?:${DOT}\\w+|${BRACKET_OPEN}\\s*['"\`]\\w+['"\`]\\s*\\])`;
const memberTail = (alt: string): string =>
`(?:${DOT}(?:${alt})\\b|${BRACKET_OPEN}\\s*['"\`](?:${alt})['"\`]\\s*\\])`;
/**
* Reduce an exported expression to its core: strip parenthesization and
* trailing type assertions (`(companies)`, `companies as unknown as object`)
* so `export default <expr>` passes see the binding under the dressing.
* Reduce an exported expression to its core: strip parenthesization,
* trailing type assertions (`as …`, `satisfies …`), and trailing non-null
* assertions (`(companies)`, `companies as unknown as object`,
* `companies satisfies object`, `companies!`, `(companies as any)!`) so
* `export default <expr>` passes see the binding under the dressing.
*/
function stripExprDressing(raw: string): string {
let expr = raw.trim();
@@ -468,11 +482,16 @@ function stripExprDressing(raw: string): string {
.replace(/^\(\s*/, '')
.replace(/\s*\)$/, '')
.replace(/\s+as\s+[^()]+$/, '')
.replace(/\s+satisfies\s+[^()]+$/, '')
.replace(/\s*!+$/, '')
.trim();
}
return expr;
}
/** Call-open shape tolerant of the optional-call form: `f(…)` or `f?.(…)`. */
const CALL_OPEN = `\\s*(?:\\?\\.)?\\s*\\(`;
interface FileFacts {
rel: string;
code: string;
@@ -572,7 +591,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*${ns}${MEMBER_SEG}*${memberTail(CLASS_SYMBOLS.join('|'))}`,
`export\\s+(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${ns}${MEMBER_SEG}*${memberTail(CLASS_SYMBOLS.join('|'))}`,
'g',
),
)) {
@@ -587,8 +606,9 @@ function computeSchemaConduits(files: FileFacts[], fileSet: Set<string>): Schema
}
}
// The default slot is an export name like any other, whatever the
// expression dressing: bare local, parenthesized, type-asserted, or
// a namespace member chain in dot or bracket form, semicolon or not.
// expression dressing: bare local, parenthesized, type-asserted
// (`as`/`satisfies`), non-null-asserted (`companies!`), or a
// namespace member chain in dot or bracket form, semicolon or not.
for (const m of f.code.matchAll(/export\s+default\s+([^;\n]+)/g)) {
const expr = stripExprDressing(m[1]!);
if (/^\w+$/.test(expr)) {
@@ -714,7 +734,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*${ns}${MEMBER_SEG}*(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
`(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${ns}${MEMBER_SEG}*(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
'g',
),
)) {
@@ -831,7 +851,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*${ns}(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
`(?:const|let|var)\\s+(\\w+)\\s*=\\s*\\(*\\s*${ns}(?:${DOT}(\\w+)\\b|${BRACKET_OPEN}\\s*['"\`](\\w+)['"\`]\\s*\\])`,
'g',
),
)) {
@@ -928,11 +948,30 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
const inRegister = INFRA_REGISTER.includes(rel);
const isSchemaDefinition = rel === 'packages/db/src/schema.ts';
// Runtime code construction: fails anywhere. Covers direct calls and
// literal computed access (window['eval'], globalThis['Function']).
if (/\beval\s*\(|\bnew\s+Function\s*\(|\[\s*['"](eval|Function)['"]\s*\]/.test(code)) {
// Runtime code construction: fails anywhere. Covers direct calls
// (optional-call form included), and literal computed access
// (window['eval'], globalThis['Function']).
if (
new RegExp(
`\\beval${CALL_OPEN}|\\bnew\\s+Function\\s*\\(|\\[\\s*['"](eval|Function)['"]\\s*\\]`,
).test(code)
) {
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)) {
violations.push({
file: rel,
prong: 'code-construction',
detail: 'template-literal computed member call',
});
}
// Dynamic import whose specifier is not a single string literal: the
// import graph becomes unanalyzable. Checked per call site, so a literal
// first fragment ('x' + y) does not slip past. A LITERAL import of a
@@ -975,7 +1014,7 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
// from a dynamic module import).
if (!CREATE_REQUIRE_REGISTER.includes(rel)) {
const crNames = new Set<string>();
if (/\bcreateRequire\s*\(/.test(code)) crNames.add('createRequire');
if (new RegExp(`\\bcreateRequire${CALL_OPEN}`).test(code)) crNames.add('createRequire');
for (const m of code.matchAll(/import\s*\{([^}]*)\}\s*from\s*['"](?:node:)?module['"]/g)) {
const alias = /\bcreateRequire\s+as\s+(\w+)/.exec(m[1]!);
if (alias) crNames.add(alias[1]!);
@@ -1006,7 +1045,7 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
}
}
for (const n of crNames) {
if (n === 'createRequire' || new RegExp(`\\b${n}\\s*\\(`).test(code)) {
if (n === 'createRequire' || new RegExp(`\\b${n}${CALL_OPEN}`).test(code)) {
violations.push({ file: rel, prong: 'dynamic-import', detail: 'createRequire()' });
break;
}
@@ -1033,7 +1072,7 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
// The argument prefix tolerates parenthesization, spread, and array
// wrapping: .insert((companies)), .insert(...[companies]).
const writeRe = new RegExp(
`\\.\\s*(insert|update|delete)\\s*\\(\\s*(?:(?:\\(|\\[|\\.\\.\\.)\\s*)*(${targets.join('|')})`,
`\\.\\s*(insert|update|delete)${CALL_OPEN}\\s*(?:(?:\\(|\\[|\\.\\.\\.)\\s*)*(${targets.join('|')})`,
'g',
);
for (const m of code.matchAll(writeRe)) {
@@ -1051,7 +1090,10 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
// form (measured), so it fails outright. Write verbs fail outside the
// allowlist; execution verbs fail outside allowlist register.
if (!inAllowlist) {
const writeBracket = /\[\s*['"](insert|update|delete)['"]\s*\]\s*\(/g;
const writeBracket = new RegExp(
`\\[\\s*['"](insert|update|delete)['"]\\s*\\]${CALL_OPEN}`,
'g',
);
for (const m of code.matchAll(writeBracket)) {
violations.push({
file: rel,
@@ -1060,7 +1102,9 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
});
}
if (!inRegister) {
for (const m of code.matchAll(/\[\s*['"](execute|query|unsafe)['"]\s*\]\s*\(/g)) {
for (const m of code.matchAll(
new RegExp(`\\[\\s*['"](execute|query|unsafe)['"]\\s*\\]${CALL_OPEN}`, 'g'),
)) {
violations.push({
file: rel,
prong: 'iii-raw-execution',
@@ -1142,8 +1186,9 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
if (driverImport) {
violations.push({ file: rel, prong: 'iii-raw-execution', detail: 'direct driver import' });
// A driver client exposes query/execute/unsafe as raw primitives:
// flag them all on any receiver, any argument, spaced or not.
for (const m of code.matchAll(/\.\s*(execute|query|unsafe)\s*\(/g)) {
// flag them all on any receiver, any argument, spaced, optional-call
// (`?.(`) or not.
for (const m of code.matchAll(new RegExp(`\\.\\s*(execute|query|unsafe)${CALL_OPEN}`, 'g'))) {
violations.push({
file: rel,
prong: 'iii-raw-execution',
@@ -1155,7 +1200,7 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
// the relational builder, and unrelated .query() methods are common),
// so factory capability flags execute/unsafe on any receiver and query
// only on the db/client-shaped receiver backstop below.
for (const m of code.matchAll(/\.\s*(execute|unsafe)\s*\(/g)) {
for (const m of code.matchAll(new RegExp(`\\.\\s*(execute|unsafe)${CALL_OPEN}`, 'g'))) {
violations.push({
file: rel,
prong: 'iii-raw-execution',
@@ -1171,7 +1216,7 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
// access tolerates ?. and ! markers (this.db?.query(…)).
for (const m of code.matchAll(
new RegExp(
`(?:\\b(?:db|database|client|conn|connection|pool|pg|pglite|tx|trx)|\\[\\s*['"](?:db|database|client|conn|connection|pool|pg|pglite|tx|trx)['"]\\s*\\])${DOT}(execute|query|unsafe)\\s*\\(`,
`(?:\\b(?:db|database|client|conn|connection|pool|pg|pglite|tx|trx)|\\[\\s*['"](?:db|database|client|conn|connection|pool|pg|pglite|tx|trx)['"]\\s*\\])${DOT}(execute|query|unsafe)${CALL_OPEN}`,
'g',
),
)) {
@@ -1199,7 +1244,7 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] {
if (nsName) sqlAliases.add(`${nsName}\\s*\\.\\s*sql`);
}
if (sqlAliases.size > 0) {
const rawRe = new RegExp(`\\b(${[...sqlAliases].join('|')})\\s*\\.\\s*raw\\s*\\(`);
const rawRe = new RegExp(`\\b(${[...sqlAliases].join('|')})\\s*\\.\\s*raw${CALL_OPEN}`);
if (rawRe.test(code)) {
violations.push({ file: rel, prong: 'iii-raw-execution', detail: 'sql.raw()' });
}
@@ -1749,6 +1794,112 @@ describe('hierarchy writer coverage (contract 1 §6.3b)', () => {
name: 'E68 optional-chained bracketed DI receiver',
src: `export class R { async f(t: string) { await this['db']?.query('TRUNCATE ' + t); } }`,
},
{
name: 'E69 non-null-asserted default export of schema symbol',
src: `import c from './evasion-mid26.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(c).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid26.ts',
src: `import { companies } from '@mosaicstack/db';\nexport default companies!;`,
},
],
},
{
name: 'E70 satisfies-dressed default export of schema symbol',
src: `import c from './evasion-mid27.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(c).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid27.ts',
src: `import { companies } from '@mosaicstack/db';\nexport default companies satisfies object;`,
},
],
},
{
name: 'E71 paren-plus-assertion-plus-non-null default export',
src: `import c from './evasion-mid28.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(c).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid28.ts',
src: `import { companies } from '@mosaicstack/db';\nexport default (companies as unknown)!;`,
},
],
},
{
name: 'E72 non-null-asserted ns-member default export (dynamic ns)',
src: `import c from './evasion-mid29.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(c).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid29.ts',
src: `const mod = await import('@mosaicstack/db');\nexport default mod.companies!;`,
},
],
},
{
name: 'E73 parenthesized ns-member export-const of schema symbol',
src: `import { co } from './evasion-mid30.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(co).values({}); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid30.ts',
src: `import * as ns from '@mosaicstack/db';\nexport const co = (ns.companies);`,
},
],
},
{
name: 'E74 non-null-asserted default export of factory local',
src: `import mk from './evasion-mid31.js';\nexport async function f(t: string) { await mk('u').execute('DELETE FROM ' + t); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid31.ts',
src: `import { createDb } from '@mosaicstack/db';\nexport default createDb!;`,
},
],
},
{
name: 'E75 non-null-asserted ns-member factory default export',
src: `import mk from './evasion-mid32.js';\nexport async function f(t: string) { await mk('u').execute('DELETE FROM ' + t); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid32.ts',
src: `import * as ns from '@mosaicstack/db';\nexport default ns.createDb!;`,
},
],
},
{
name: 'E76 satisfies-dressed default export of factory local',
src: `import mk from './evasion-mid33.js';\nexport async function f(t: string) { await mk('u').execute('DELETE FROM ' + t); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid33.ts',
src: `import { createDb } from '@mosaicstack/db';\nexport default createDb satisfies typeof createDb;`,
},
],
},
{
name: 'E77 parenthesized ns-member factory derivation re-exported',
src: `import mk from './evasion-mid34.js';\nexport async function f(t: string) { await mk('u').execute('DELETE FROM ' + t); }`,
extras: [
{
rel: 'packages/db/src/evasion-mid34.ts',
src: `import * as ns from '@mosaicstack/db';\nconst mk = (ns.createDb);\nexport default mk;`,
},
],
},
{
name: 'E78 optional-call write verb',
src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await db.insert?.(companies).values({}); }`,
},
{
name: 'E79 optional-call DI receiver chain',
src: `export class R { constructor(private pool?: { execute(s: string): Promise<unknown> }) {}\n async f(t: string) { await this.pool?.execute?.('TRUNCATE ' + t); } }`,
},
{
name: 'E80 optional-call execution in factory-capable file',
src: `import { createDb } from '@mosaicstack/db';\nexport async function f(t: string) { const d = createDb('u'); await d.execute?.('DELETE FROM ' + t); }`,
},
{
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({}); }`,
},
];
const CLEAN_CONTROLS: Array<{ name: string; src: string }> = [
{