fix(db): close round-9 review evasions in writer-coverage assertion
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
This commit is contained in:
@@ -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<unknown>; 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<unknown> }) {}\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<unknown> }) {}\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<string, string>) { 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<string, string>, 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', () => {
|
||||
|
||||
Reference in New Issue
Block a user