verdict (Engine A) — design
Class:
design· Function axis:f0the scan fold ·f1the checks ·f2the policy ·f3the verdict type. Strict: only the shape we run. The why is inverdict-reasoning.md; the system view indesign.md.
f0 — the fold
Engine A is the lean, always-on gate (default = ["scan"]). It turns an Artifact (scan.rs:58) into a core::Verdict by folding three independent checks and reducing their findings to the strictest Decision. OsvScanner::scan (impl ScanEngine) runs, in order:
- known-vuln match — for each advisory
AdvisoryDb::queryreturns for the artifact (scan.rs:213),vuln_findingproduces zero/oneFinding; - license —
license_finding→ zero/oneFinding; - provenance —
provenance_finding→ zero/oneFinding;
then decide(&findings) (scan.rs:366) picks the decision. Because Decision derives Ord in declaration order Pass < Warn < Block (core.rs:15), the fold is a max() over severity → decision (decision_for, scan.rs:356) — a clean artifact is Pass (the identity). The result carries its cache key components (policy_version, advisory_db_rev) so a consumer invalidates when either moves.
Reuse: verdict.uses = [feeds, provenance, core]. It matches against feeds' range-native Advisorys and folds provenance's findings; it owns no I/O.
f1 — the three checks
vuln_finding (scan.rs, exploit-intel gating)
One matched Advisory → zero/one Finding, applying, in order:
- VEX suppression — if
policy.vex_not_affectedcontains the advisory id, returnNone(the consumer asserted the vulnerable path is unreachable). - KEV escalation —
adv.kev && policy.kev_blocks⇒severity.max(Critical), tag[KEV]. - EPSS escalation —
adv.epss ≥ policy.epss_block_threshold⇒severity.max(High); the EPSS score is tagged into the summary.
AdvisoryDb::query (scan.rs:213) filters the db to advisories whose matches accepts the artifact ref — range-native via feeds/versions (see feeds-design.md).
license_finding (scan.rs)
Against LicensePolicy (scan.rs:279): a denied license → High (MODGUNN-LICENSE-DENIED); a license absent from a non-empty allow-list → Medium (MODGUNN-LICENSE-UNREVIEWED); no declared license → severity from on_unknown (MODGUNN-LICENSE-UNKNOWN); otherwise no finding.
provenance_finding (scan.rs)
A signed artifact yields nothing. An unsigned one maps ProvenancePolicy (scan.rs:307) Ignore→Pass / Warn→Warn / Require→Block through severity_for into a MODGUNN-PROVENANCE-MISSING finding. The Provenance value itself is produced by the provenance function's evaluate.
f2 — the policy
Policy (scan.rs:320) is the whole tunable surface: version (a cache-key component), license: LicensePolicy, provenance: ProvenancePolicy, kev_blocks: bool, epss_block_threshold: Option<f64>, vex_not_affected: Vec<String>. Policy::default (scan.rs:339) is the shipped baseline. LicensePolicy::allowlist (scan.rs:292) builds an allow-list policy.
f3 — the verdict type (core.rs)
Verdict (core.rs:59) = { artifact, decision, findings, policy_version, advisory_db_rev }. cache_key (core.rs:70) is (blob_sha256, policy_version, advisory_db_rev) — content-addressed so a clean result is computed once and reused. max_severity is the strongest finding severity. ArtifactRef carries (ecosystem, name, version, blob_sha256); Finding is { severity, id, summary }. This is the exact type Engine B also emits, so consumers read one shape.
Canonical labels (single source). Decision::as_str / Display (core.rs) is the one home of the Pass/Warn/Block strings, and Severity::as_str / Display the one home of the severity labels. Consumers — holger's gate & UI, nornir-viz's Security pane — render THESE rather than hand-rolling a match or leaning on {:?} Debug output, so the label set never drifts across the constellation. modgunn's own CLI dogfoods them (main.rs finding/scorecard rows).
Surface
| entry | signature | file:symbol |
| construct | OsvScanner::new(policy, advisories) |
scan.rs:394 |
| scan | ScanEngine::scan(&self, &Artifact) -> Verdict |
scan.rs:386 |
| fold | decide(&[Finding]) -> Decision |
scan.rs:366 |
| cache key | Verdict::cache_key() |
core.rs:70 |
Unit tests cover the fold, gating, and cache key (scan.rs::tests), plus the canonical Decision/Severity label ↔ Display agreement (core.rs::tests).