/** * Security Scanner — decoy/misdirection URL-target heuristic * @module @skillsmith/core/security/scanner/SecurityScanner.decoy * * SMI-6033 Wave 4 (Gap 6): reuses the typosquat detector's brand data — * `BRAND_ALIASES` (typosquat.ts) and `AUTHORITY_CLAIMING_AFFIXES` * (typosquat.ts, exported for exactly this reuse as part of Wave 1's Gap 6 * prerequisite) — to catch a fetch/exec instruction whose surrounding prose * claims a specific vendor (a brand token, optionally reinforced by an * authority-claiming affix like "official"/"verified") while the fetch * target's actual domain does not belong to that vendor and is not in the * general `DEFAULT_ALLOWED_DOMAINS` allowlist. * * Per the plan's §9 reconciliation table: `decoy_misdirection` is "N/A — * never standalone... Approximate NL heuristic by construction; co-signal * required (medium tier), unchanged." This detector therefore NEVER emits * `high`/`critical` — only `medium`, or nothing (see the doc-context * downgrade below, which uses `low`, not a third severity choice for the * non-doc case). The co-signal mechanism that would let this medium finding * contribute toward escalating a separate weak `code_execution` finding is a * SEPARATE dispatch's job — `SecurityScanner.exec.ts`'s * `CO_SIGNAL_MIN_SEVERITY` replacement — and is NOT implemented in this file. * * Design note, NOT explicit in the plan text and flagged here for review: * the plan says to compare "the claimed vendor's canonical domain" against * the fetch target's domain, but `BRAND_ALIASES`'s values are GitHub OWNER * SLUGS (`anthropics`, `google-gemini`, ...) — they answer "whose GitHub org * publishes this," not "what is this vendor's real website." A small * curated `BRAND_CANONICAL_DOMAINS` map below, scoped to the exact same six * brand tokens `BRAND_ALIASES` already curates, fills that gap; a brand * token with no entry here is not currently possible (the two maps share the * same key set by construction) but the lookup fails safe (empty domain * list -> never matches -> never suppresses) if that ever drifts. * * Second design note: the plan text reads "a brand token OR an * authority-claiming phrase implying a specific vendor." An authority- * claiming affix ALONE ("the official installer") never names a specific * vendor and so cannot resolve a canonical domain to compare against — this * detector therefore requires a brand-token match to identify "the claimed * vendor" at all (necessary for the domain comparison to mean anything), and * treats a co-occurring authority-claiming affix as a confidence booster * (medium -> high), not an alternate, independently-sufficient trigger. This * mirrors typosquat.ts's own rule 3 (`hasBrandToken && hasAuthorityAffix`), * which also requires both signals together. * * Two fixes from adversarial review (2026-08-16 — see * docs/internal/code_review/2026-08-15-smi6033-wave4-escalation-model.md * for the full account): * * 1. Fetch-target correlation. The original version treated ANY URL on a * line that ALSO matched the generic FETCH_COMMAND_PATTERN as the fetch * target — including a URL that was merely mentioned in prose alongside * an unrelated fetch-verb usage on the same line (e.g. * "curl --version; see mirror documentation at ", where curl is * checking its own version, not fetching that URL). Replaced with * `isActualFetchTarget`, which requires the fetch verb to be * IMMEDIATELY followed (only flag-like tokens and whitespace, no * command separator, no prose) by the URL — i.e. the URL must actually * be the verb's argument. * * 2. Authority-affix proximity. `hasAuthorityAffix` used to scan the ENTIRE * ±5-line window independently of where the brand token itself was * found, so an unrelated authority phrase elsewhere in the window (e.g. * "for official documentation on Python packaging, see PEP 517") could * wrongly boost confidence to 'high' for a brand claim it has nothing to * do with. Now scoped to a tight window around the brand token's OWN * line (`DECOY_AUTHORITY_AFFIX_PROXIMITY_LINES`). */ import type { SecurityFinding } from './types.js'; import type { LineContext } from './SecurityScanner.helpers.js'; export declare function scanDecoyMisdirection(content: string, lineContexts?: LineContext[]): SecurityFinding[]; //# sourceMappingURL=SecurityScanner.decoy.d.ts.map