/** * SMI-5881: Pattern scope model — replaces the old per-source-text * multiline-detection heuristic function (formerly in * SecurityScanner.helpers.ts, deleted entirely, no compatibility shim). * @module @skillsmith/core/security/scanner/patterns.scope * * The deleted heuristic sniffed a regex's SOURCE TEXT for the literal * two-character sequences `\r`/`\n` (or a `(?:^|\n)` prefix) to decide * whether a pattern should be tested against the full document (`content`) * or per-line. This heuristic was wrong in BOTH directions: * * - False negative: a pattern that spans lines via a bounded `[\s\S]{0,N}` * character class (which matches a literal newline character at runtime) * but has no literal `\r`/`\n` ESCAPE SEQUENCE in its regex source was * scanned per-line only, so a genuinely cross-line attack could never fire * (AD_HTML_COMMENT_VERB/NOUN, AD_NESTED_INSTRUCTION_BLOCK, AD_ZERO_WIDTH). * - False positive: a pattern containing a NEGATED newline-excluding class * (`[^\n]{0,80}?`, deliberately forbidding a newline crossing) still * contains the literal substring `\n` in its `.source` text, so the naive * heuristic misclassified it as content-scope anyway (JB_JS3A/JS3B, * AD_AN3A) — harmless today (these patterns are correctly kept out of the * per-line pass since they were never meant to be tested there either) but * still a real classification bug, not deliberate design. * * PATTERN_SCOPE replaces the heuristic with an explicit, fail-closed, * per-pattern declaration: `'line'` (per-line pass only), `'content'` * (full-document pass only), or `'both'` (tested in both passes — needed to * fix the false-negative class above without losing existing single-line * coverage). There is no default: an unmapped pattern throws at scan time * (`resolvePatternScope`) and the whole module fails to load if any pattern * in `SCOPED_PATTERN_SETS` is missing an entry (`assertScopeCoverage`, called * at the bottom of this file). Unlike the evidence-tier map (which safely * defaults an unmapped pattern to its STRONGEST tier), scope has no * analogous safe default — a pattern silently scanned in the WRONG pass (or * neither) is a silent coverage gap, not a conservative failure mode. * * Every scope assignment below is BASELINE-PRESERVING relative to the old * heuristic's actual current behavior, with exactly 4 explicit promotions * (`'line'` → `'both'`, section 1.4 of the SMI-5881 design): AD_HTML_COMMENT_ * VERB, AD_HTML_COMMENT_NOUN, AD_NESTED_INSTRUCTION_BLOCK, AD_ZERO_WIDTH. * Every other pattern — including the two known false positives above * (JB_JS3A/JS3B, AD_AN3A) — keeps whatever scope the old heuristic already * computed for it; re-scoping any of those changes finding cardinality and * needs its own corpus review, out of scope for this P0 fix. */ export type PatternScope = 'line' | 'content' | 'both'; export declare const PATTERN_SCOPE: ReadonlyMap; /** Every pattern array consumed by a scope-resolving scan function. */ export declare const SCOPED_PATTERN_SETS: ReadonlyArray<{ name: string; patterns: readonly RegExp[]; }>; /** * Resolve a pattern's scope by object identity. Throws (does NOT default) for * any pattern reaching a scope-resolving scanner without a PATTERN_SCOPE * entry — there is no safe default direction for scope the way there is for * evidence tier (classifyEvidence's fail-closed-to-strongest-tier doesn't * apply here: a pattern scanned in the wrong pass, or neither, is a silent * coverage gap regardless of which "direction" you'd guess). */ export declare function resolvePatternScope(pattern: RegExp): PatternScope; //# sourceMappingURL=patterns.scope.d.ts.map