import type { EvalStrategy } from "../config/schema.js"; import type { SecurityFinding } from "./security-audit-types.js"; /** * Deterministic scanner pre-filter (arch-20260712 ADR-4). * * Runs each `config.security.scanners` command under the shared audit * AbortSignal, parses known scanner output (slither, semgrep) into * SecurityFinding[] priors, and degrades unknown scanners to a bounded * raw-text excerpt finding. Every scanner is isolated: a missing binary, * nonzero exit, or thrown error yields `[]` for that scanner only — the * pre-filter itself NEVER rejects (runSecurityAudit's Promise.race time-box * must be able to trust that this always settles). */ /** The minimal result shape runScannerPreFilter needs from a child process. */ export interface ScannerRunResult { exitCode: number | undefined; stdout: string; failed: boolean; } /** * Runs one scanner command and resolves with its outcome. NEVER expected to * reject in production (the default implementation uses execa's `reject: * false`); the caller wraps every invocation in try/catch anyway as a * defensive backstop (mirrors src/fleet/runner.ts's "reject:false plus * try/catch" belt-and-suspenders pattern). */ export type ScannerRunner = (cmd: string, args: string[], opts: { cwd: string; signal: AbortSignal; }) => Promise; /** * Parse slither `--json` output into SecurityFinding[]. * * Real shape: `{ success, error, results: { detectors: [...] } }`. Each * detector: `{ check, impact, confidence, description, elements: [{ type, * name, source_mapping: { filename_relative, filename_absolute, lines[], * starting_column } }] }`. * * SecurityFinding has no top-level severity field (ReviewFinding locks * description + evidence[]) — the impact bucket is encoded as a `[High]` * style prefix on `description`, and `source: "slither"` marks provenance. * * Defensive narrowing at every level (Pattern A / medline-source.ts style): * any structural mismatch returns `[]` rather than throwing. Accepts a raw * (non-object) value too — e.g. a truncated JSON string passed directly — * so callers never need to guard before calling this. */ export declare function parseSlitherOutput(json: unknown): SecurityFinding[]; /** * Parse semgrep `--json` output into SecurityFinding[]. * * Real shape: `{ results: [...], errors: [], paths: {...} }`. Each result: * `{ check_id, path, start: { line, col }, end: {...}, extra: { severity, * message, lines } }`. * * Same severity-in-description convention as the slither parser * (`[ERROR]`/`[WARNING]`/`[INFO]` prefix + `source: "semgrep"`); same * defensive narrowing (malformed input → `[]`, never throws). */ export declare function parseSemgrepOutput(json: unknown): SecurityFinding[]; /** * Parse `npm audit --json` output into SecurityFinding[]. * * v7+ shape: `{ vulnerabilities: { : { name, severity, via: [...], * range, nodes: [...], fixAvailable } }, metadata: {...} }`. `via` entries * mix plain advisory-source strings and objects `{ title, url, severity }` * in the same array — real npm output does this. * * v6 fallback shape: `{ advisories: { : { module_name, severity, title, * url } } }`. Both shapes are supported (whichever key is a well-formed * object wins); neither present, or malformed -> []. * * All findings map to `vulnClass: "supply-chain"` (sc-7-2). Defensive * narrowing at every level (Pattern A) — never throws. */ export declare function parseNpmAuditOutput(json: unknown): SecurityFinding[]; /** * Parse `osv-scanner --format json` output into SecurityFinding[]. * * Real shape: `{ results: [ { source: { path, type }, packages: [ { * package: { name, ecosystem, version }, vulnerabilities: [ { id, summary, * severity } ] } ] } ] }`. Every finding maps to `vulnClass: "supply-chain"` * (sc-7-2). Defensive narrowing at every level (Pattern A) — never throws. */ export declare function parseOsvOutput(json: unknown): SecurityFinding[]; /** * Parse `gitleaks --report-format json` output into SecurityFinding[]. Its * report is a TOP-LEVEL ARRAY (unlike npm-audit/osv-scanner's object root) * of `{ Description, File, StartLine, EndLine, RuleID, Secret, Match, * Commit }`. * * The raw `Secret` field is a live credential — it is NEVER echoed into a * finding; `Match` (or a redacted placeholder) is used for the evidence * snippet instead. Every finding maps to `vulnClass: "secret-handling"` * (sc-7-2). Defensive narrowing at every level (Pattern A) — never throws. */ export declare function parseGitleaksOutput(json: unknown): SecurityFinding[]; /** * Whether a configured scanner requires network access to run (hits a * remote registry or vulnerability database). Used by the supply-chain axis * (sprint 7, security-auditor-agent.ts) to gate network-capable scanner * kinds behind `config.security.egress.onlineResearch` — `gitleaks` is a * purely local secret scan and is NOT gated. */ export declare function isNetworkScanner(scanner: EvalStrategy): boolean; export interface ScannerPreFilterInput { scanners: EvalStrategy[]; projectRoot: string; signal: AbortSignal; /** Injected runner — default wraps execa. Tests inject a fake for CI-offline coverage. */ runner?: ScannerRunner; } /** * Run every configured scanner and return the combined SecurityFinding * priors. `scanners: []` is a pure no-op — no runner is invoked and zero * child processes are spawned (ADR-4, sc-5-4). * * Per-scanner isolation: each scanner is wrapped in its own try/catch and * contributes `[]` on any failure (missing binary, nonzero exit, thrown * error) without affecting the others (sc-5-2). The whole function NEVER * rejects — even when the shared AbortSignal fires mid-scan, the killed * scanner simply contributes `[]` while already-finished scanners' findings * are preserved (sc-5-3). */ export declare function runScannerPreFilter(input: ScannerPreFilterInput): Promise; //# sourceMappingURL=security-scanners.d.ts.map