/** * VEX (Vulnerability Exploitability eXchange) ingestion + suppression (#626). * A scanner (./vuln-scan.ts) flags every known CVE in an SBOM, but most aren't * actually exploitable in context (unreachable code path, already mitigated, * false positive). VEX is where you (or upstream) assert, per CVE, whether it * *actually* affects this artifact. Without it a gate is CVE fatigue — 500 * findings, 5 that matter. This module parses VEX and filters findings, and it * **reports** every suppression with its justification — a VEX-suppressed * finding is never silently dropped. * * Two formats: **OpenVEX** (the standalone Sigstore/OpenSSF format) and * **CycloneDX**-embedded VEX (`vulnerabilities[].analysis`). Both reduce to the * same `VexStatement` shape here. */ import type { VulnFinding } from "./vuln-scan.js"; /** VEX status for a CVE against an artifact. `not_affected`/`fixed` suppress a finding; `affected`/`under_investigation` keep it (conservative — an unresolved status must not hide a real vuln). */ export type VexStatus = "not_affected" | "affected" | "fixed" | "under_investigation"; /** True for statuses that suppress a matching finding from gating. */ export declare function suppresses(status: VexStatus): boolean; /** One VEX assertion: this CVE has this status for this artifact, with an optional human justification. */ export interface VexStatement { cveId: string; status: VexStatus; /** Why — e.g. `"vulnerable_code_not_in_execute_path"`, `"component_not_present"`. Surfaced on the suppression report so a reviewer sees the reasoning, never a bare "suppressed." */ justification?: string; } /** Parse an OpenVEX JSON document into statements. Unknown statuses / statements missing a vulnerability id are skipped. */ export declare function parseOpenVex(bytes: string): VexStatement[]; /** Parse CycloneDX-embedded VEX (`vulnerabilities[].analysis`) into statements. */ export declare function parseCycloneDxVex(bytes: string): VexStatement[]; /** * Parse a VEX document of either format — dispatched on shape (`statements` -> * OpenVEX, `vulnerabilities` -> CycloneDX). A malformed/unparseable document * yields NO statements (returns `[]`) rather than throwing: it therefore * suppresses nothing, keeping the gate strict (fail-closed) instead of * crashing on attacker- or typo-supplied input. */ export declare function parseVexDocument(bytes: string): VexStatement[]; /** A finding removed from gating by a VEX statement, kept for reporting (never silently dropped). */ export interface SuppressedFinding { finding: VulnFinding; status: VexStatus; justification?: string; } /** The result of filtering findings through VEX: what still gates, and what was suppressed (with reasons). */ export interface VexResult { /** Findings that survived VEX — the gate evaluates these. */ gating: VulnFinding[]; /** Findings suppressed by a `not_affected`/`fixed` statement, with the statement's justification. */ suppressed: SuppressedFinding[]; } /** * Filter `findings` through `statements`. A finding is suppressed only when * some statement for its CVE suppresses it (`not_affected`/`fixed`) AND **no** * statement for that CVE asserts it is `affected`/`under_investigation`. * * This is the conservative merge, deliberately NOT "last statement wins": * when VEX comes from several sources (repo-local files + referrer-attached), * a later `not_affected` must not be able to override an earlier `affected` to * unblock a real vulnerability. Merging VEX can only ever make the gate * *stricter*, never quietly weaker — so an attacker who can append a statement * cannot suppress a finding another source flagged as affected. */ export declare function applyVex(findings: VulnFinding[], statements: VexStatement[]): VexResult; //# sourceMappingURL=vex.d.ts.map