diff --git a/packages/db/src/hierarchy-writer-coverage.test.ts b/packages/db/src/hierarchy-writer-coverage.test.ts index 0c6e0bcd..dc3bf0b2 100644 --- a/packages/db/src/hierarchy-writer-coverage.test.ts +++ b/packages/db/src/hierarchy-writer-coverage.test.ts @@ -85,12 +85,18 @@ * flagged, and the statically-resolvable DISGUISES of a literal key fail * closed — access or call, in any position: a text-only template-literal * key (obj[`insert`](), ns[`companies`] — template text never reaches - * the lexer's code output), a quoted key carrying expression dressing - * (`ns['companies' as const]`, `ns['companies'!]`), and a quoted key + * the lexer's code output), a quoted OR template key carrying expression + * dressing (`ns['companies' as const]`, `ns['companies'!]`, + * `ns[`companies` as const]` — the dressed rule's key class includes the + * backtick, so the lexed empty backtick pair matches), and a quoted key * built from string escapes (`\u`/`\x`/octal). (A template key WITH * interpolation is a non-literal computed member, above.) Invoking a - * write/exec verb via `.apply`/`.call`/`.bind`, or through any - * `Reflect.*` call naming a verb, fails closed the same way. These + * write/exec verb via `.apply`/`.call`/`.bind` fails closed the same + * way, and ANY appearance of the `Reflect` identifier fails closed + * outright — verb detection through Reflect is not boundable (argument + * windows stop at newlines and nested parens, the method can be + * bracket-spelled, the object aliased), and the tree has zero + * occurrences outside test files. These * code-shape rules match ordinary syntax over ordinary method names, so * they carry their own enumerated disposition (CODE_SHAPE_REGISTER, * empty today): a reviewed legitimate hit is registered, never resolved @@ -1035,18 +1041,25 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] { detail: 'template-literal computed member', }); } - // A quoted computed key carrying expression dressing + // A quoted OR template computed key carrying expression dressing // (`ns['companies' as const]`, `ns['companies' satisfies 'companies']`, - // `ns['companies'!]`) keeps its static value while breaking every - // `['name']` matcher — the bracket alternatives require the closing - // quote to touch the `]`. Tolerance inside the matchers cannot span the - // class (the dressing's type text may itself contain a `]`, e.g. - // `as Foo['x']`), so the SHAPE fails closed: a quote-close followed by - // `!`/`as`/`satisfies` inside a bracket. The `!(?!=)` guard keeps - // ordinary comparisons (`o['k'] !== x` — dressing AFTER the bracket) - // clean. + // `ns['companies'!]`, and the template forms `ns[\`companies\` as + // const]`, `ns[\`companies\`!]`) keeps its static value while breaking + // every `['name']` matcher — the bracket alternatives require the + // closing quote to touch the `]`, and a dressed TEMPLATE key also breaks + // the template rule above (dressing intervenes before the `]`). + // Tolerance inside the matchers cannot span the class (the dressing's + // type text may itself contain a `]`, e.g. `as Foo['x']`), so the SHAPE + // fails closed: a quote-close followed by `!`/`as`/`satisfies` inside a + // bracket. The key class includes the backtick: a dressed template key + // survives in lexed code as the empty adjacent-backtick pair, which the + // empty-key case of this regex matches (an interpolated key keeps its + // `\${…}` between the backticks and stays clean unless dressed — a + // dressed interpolated key fires too, an over-match dispositioned like + // any other shape hit). The `!(?!=)` guard keeps ordinary comparisons + // (`o['k'] !== x` — dressing AFTER the bracket) clean. if ( - new RegExp(`\\[\\s*(['"])(?:(?!\\1)[^\\n])*\\1\\s*(?:!(?!=)|as\\s|satisfies\\s)`).test(code) + new RegExp(`\\[\\s*(['"\`])(?:(?!\\1)[^\\n])*\\1\\s*(?:!(?!=)|as\\s|satisfies\\s)`).test(code) ) { violations.push({ file: rel, @@ -1087,18 +1100,22 @@ function analyzeFile(f: FileFacts, ctx: AnalysisCtx): Violation[] { } // `Reflect` reaches the same members without member syntax: // `Reflect.apply(db.insert, db, [companies])`, `Reflect.get(db, - // 'insert')`, `Reflect.getOwnPropertyDescriptor(db, 'execute')`. Any - // Reflect call whose argument text (up to the first `)`) names a verb - // fails closed — the tree has zero Reflect call sites (measured). - if ( - new RegExp( - `\\bReflect${DOT}[\\w$]+${CALL_OPEN}[^)\\n]*\\b(?:insert|update|delete|execute|query|unsafe|raw)\\b`, - ).test(code) - ) { + // 'insert')`, `Reflect.getOwnPropertyDescriptor(db, 'execute')`. Verb + // detection through Reflect is not boundable: any argument window stops + // at a newline (prettier breaks a >100-col call one-arg-per-line, moving + // the verb past it) or at the first `)` (a nested-paren first argument + // closes it early), the method can be bracket-spelled + // (`Reflect['apply']`), and the object can be aliased + // (`const R = Reflect`). So the IDENTIFIER is the shape: any `Reflect` + // token in lexed code fails closed — member access, aliasing, or + // argument passing alike. The tree has zero occurrences outside test + // files (measured); a reviewed legitimate use is register-dispositioned. + // (`\b` keeps compound identifiers like `ReflectHelper` clean.) + if (new RegExp(`\\bReflect\\b`).test(code)) { violations.push({ file: rel, prong: 'code-construction', - detail: 'Reflect verb indirection', + detail: 'Reflect indirection', }); } } @@ -2162,6 +2179,52 @@ describe('hierarchy writer coverage (contract 1 §6.3b)', () => { name: 'E102 Reflect.get extraction of an execution verb', src: `import { db } from './x.js';\nexport async function f(t: string) { const fn = Reflect.get(db, 'execute') as (s: string) => Promise; await fn.call(db, 'DELETE FROM ' + t); }`, }, + { + name: 'E103 as-dressed template key as write target', + src: `import * as ns from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await db.insert(ns[\`companies\` as const]).values({}); }`, + }, + { + name: 'E104 as-dressed template key as verb call', + src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await db[\`insert\` as const](companies).values({}); }`, + }, + { + name: 'E105 non-null-dressed template key 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: 'E106 dressed template receiver verb in capability-free file', + src: `export class R { constructor(private pool: { query(s: string): Promise }) {}\n async f(t: string) { await this.pool[\`query\` as const]('TRUNCATE ' + t); } }`, + }, + { + name: 'E107 dressed template key in schema conduit export', + src: `import { co } from './evasion-mid43.js';\nimport { db } from './x.js';\nexport async function f() { await db.insert(co).values({}); }`, + extras: [ + { + rel: 'packages/db/src/evasion-mid43.ts', + src: `import * as ns from '@mosaicstack/db';\nexport const co = ns[\`companies\` as const];`, + }, + ], + }, + { + name: 'E108 dressed template key in single-file factory extraction', + src: `import * as ns from '@mosaicstack/db';\nexport async function f(t: string) { const mk = ns[\`createDb\` as const]; const d = mk('u'); await d.execute('DELETE FROM ' + t); }`, + }, + { + name: 'E109 multiline Reflect.apply in prettier-broken shape', + src: `export class R { constructor(private pool: { execute(s: string): Promise }) {}\n async f(t: string) {\n await Reflect.apply(\n this.pool.execute,\n this.pool,\n ['TRUNCATE ' + t],\n );\n } }`, + }, + { + name: 'E110 bracket-spelled Reflect method', + src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await Reflect['apply'](db.insert, db, [companies]); }`, + }, + { + name: 'E111 Reflect.apply with nested-paren first argument', + src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { await Reflect.apply((fn: () => unknown) => fn(), db.insert, [db, [companies]]); }`, + }, + { + name: 'E112 aliased Reflect', + src: `import { companies } from '@mosaicstack/db';\nimport { db } from './x.js';\nexport async function f() { const R = Reflect; await R.apply(db.insert, db, [companies]); }`, + }, ]; const CLEAN_CONTROLS: Array<{ name: string; src: string }> = [ { @@ -2180,6 +2243,14 @@ describe('hierarchy writer coverage (contract 1 §6.3b)', () => { name: 'clean: strict-inequality after a bracket member is not key dressing', src: `export function h(o: Record) { return o['kind'] !== 'x'; }`, }, + { + name: 'clean: undressed interpolated template key stays a non-literal member under the backtick key class', + src: `export function j(o: Record, k: string) { return o[\`\${k}\`]; }`, + }, + { + name: 'clean: compound identifier containing Reflect is not the Reflect object', + src: `export class ReflectHelper { reflectStyle = 1; }\n// Reflect in a comment is prose, not code`, + }, ]; it('analyzer flags every known evasion form', () => {