/** * Security Scanner — MF-4 assignment-value gate (SMI-5207) * @module @skillsmith/core/security/scanner/SecurityScanner.value-gate * * The `sensitive_path` MF-4 gate: given a line that matched one of the three * VALUE_GATED_ASSIGNMENT_PATTERNS, decide whether it assigns a REAL credential * (→ HIGH) or only prose (→ MEDIUM). HIGH is the default; a downgrade requires * positive prose evidence, and since HIGH is the pre-SMI-5207 unconditional * behaviour that default could not regress detection. * * SMI-6505 QUALIFIES THAT LAST CLAUSE — it is no longer unconditionally true of * this module. It held while every rule here only recognized prose. The * embedded-key boolean-flag rule below deliberately turns a previously-HIGH * input MEDIUM (`allow_credentials=True`, a FastAPI CORS flag that was blocking * installation of any skill documenting CORS setup). The MF-4b veto's own * invariant is unaffected: that one is a claim about the `weakPasswordVeto` * option, and it still holds — see its block below. * * Split out of SecurityScanner.scanners.ts for the 500-line pre-commit gate * once round 8's segmentation logic landed (that file is at 4xx with the MF-3 * gate and six per-category scanners already in it). Cohesive unit — every * symbol here serves the single question "is this assignment's value real?". */ /** * MF-4 value classification, SEGMENTED PER ASSIGNMENT (SMI-5207 adversarial * review, round 8). Each assignment key on the line owns exactly the text * between its own `:`/`=` and the NEXT key (or EOL); the finding stays HIGH if * ANY of those values is a real credential, and downgrades only when EVERY one * of them positively reads as prose. * * The plan's single-whole-line-span shape (`SPAN.exec(line)[1]`) leaks across * assignments in THREE directions on a line carrying two keys. All three are * false-negative REGRESSIONS (unconditionally HIGH pre-SMI-5207) and none is * residual R-3, which is a stopword inside the assignment's OWN value — here * the prose bleeds in from a DIFFERENT assignment entirely: * * 1. LEFT — `SPAN` is unanchored, so it captures from the LEFTMOST key, not * necessarily the one that produced the finding (scanSensitivePaths emits * for the first entry matching in ARRAY order, and CREDENTIALS(2)/ * SECRETS(3) precede PASSWORD(8)). `password: this credentials: * Tr0ub4dor&3` → captured `this credentials: Tr0ub4dor&3` → `this` → * MEDIUM. This is the reviewer's reported counter-example. * 2. RIGHT — `(\S.*)$` runs to EOL and swallows the next assignment's prose: * `credentials: Tr0ub4dor&3 password: this` → trailing `this` → MEDIUM. * 3. Anchoring at the finding's own match (the narrow fix) closes 1, not 2, * and opens a mirror of 1: only ONE finding is emitted per line (the * scanner loop `break`s), so on `password: Tr0ub4dor&3 credentials: this` * the emitted finding sits RIGHT of the real credential, which a * match-anchored scan never sees → MEDIUM. * * Segmenting closes all three. It is also the correct granularity: severity * attaches to the LINE (the finding's `location` is the whole trimmed line), so * a line holding any real credential is HIGH. Monotonicity holds — this returns * true at least as often as the per-span reading, and true is the status quo. * * SMI-6441 note: "true is the status quo" is still literally correct, but no * longer conveys the blast radius of a single keeplist miss. Under the MF-4b * veto ONE lexicon hit in ANY segment escalates the WHOLE line — so a line * whose first segment is the acceptance criterion's own must-stay-MEDIUM case * goes HIGH anyway if a later segment trips the veto. The next-line path does * the same across a line boundary, letting an indented continuation drive the * severity of an unindented key line. Both follow from the design and neither * loses detection; recorded so a future reader sizes one missing keeplist * entry correctly. */ export declare function assignmentHasRealValue(lines: string[], index: number, options?: { readonly weakPasswordVeto?: boolean; }): boolean; //# sourceMappingURL=SecurityScanner.value-gate.d.ts.map