Documentation
¶
Overview ¶
Package evidence is StageFreight's security-ENRICHMENT layer. The scanners (OSV, Trivy, Grype) DISCOVER vulnerabilities; this package ENRICHES each discovered vulnerability with evidence so policy can judge the full story — "Critical + reachable + KEV-listed + fixable" — instead of a bare severity.
The pipeline is:
Discovery (scanners) → Normalized Vulnerabilities → Enrichment (contributors) → Findings (vuln + evidence) → Policy → Renderer
Reachability (govulncheck for Go) is deliberately just the FIRST evidence contributor, not the framework itself. KEV, EPSS, fix-availability, SBOM-ownership, exploit-maturity and runtime-loaded evidence all slot in behind the same EvidenceContributor seam without touching the pipeline. Evidence is DATA — a contributor never decides policy.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Confidence ¶
type Confidence int
Confidence qualifies an analyzer's verdict without gating policy — it is DISCLOSED to the operator (govulncheck is High; an experimental Rust analyzer may be Medium/Experimental) so they know how much to trust a downgrade.
const ( ConfidenceNone Confidence = iota ConfidenceExperimental ConfidenceMedium ConfidenceHigh )
func ParseConfidence ¶
func ParseConfidence(s string) Confidence
ParseConfidence is the inverse of String, for reconstructing from a persisted catalogue. Unrecognized input is ConfidenceNone.
func (Confidence) String ¶
func (c Confidence) String() string
type Evidence ¶
type Evidence interface {
Kind() string // "reachability", "kev", "epss", …
}
Evidence is one enrichment fact about a vulnerability. It is DATA, never a verdict — policy alone interprets it. Each concrete kind (ReachabilityEvidence today; KEVEvidence, EPSSEvidence, FixEvidence… later) implements this.
type EvidenceContributor ¶
type EvidenceContributor interface {
Name() string
Supports(ecosystem string) bool
// Contribute returns evidence to attach, indexed by the vulnerability it enriches. A vuln
// absent from the result simply gets no evidence from this contributor.
Contribute(ctx context.Context, target Target, vulns []Vulnerability) (map[VulnRef]Evidence, error)
}
EvidenceContributor enriches findings for the ecosystems it supports. It is the pluggable seam: a Go reachability contributor (govulncheck), a Rust one (experimental), and ecosystem-agnostic ones (KEV, EPSS) all satisfy it. A contributor produces evidence; it never decides policy, and it must never fabricate evidence for something it did not examine.
type Finding ¶
type Finding struct {
Vuln Vulnerability
Evidence []Evidence
}
Finding is a discovered vulnerability plus the evidence contributors have attached.
func (Finding) Reachability ¶
func (f Finding) Reachability() (ReachabilityEvidence, bool)
Reachability extracts the reachability evidence from a finding, if a contributor attached it.
type GoReachability ¶
type GoReachability struct {
// Run invokes govulncheck and returns its -json output for the module rooted at dir.
// Injectable so the parser is unit-testable without the tool.
Run func(ctx context.Context, dir string) ([]byte, error)
}
GoReachability is the govulncheck-backed reachability contributor for the "go" ecosystem — the first EvidenceContributor. govulncheck performs call-graph analysis over the built program, so it answers "can the vulnerable code be reached?", the precision layer over the module-level scanners. Go stdlib and toolchain advisories are covered too.
func NewGoReachability ¶
func NewGoReachability() *GoReachability
NewGoReachability returns the contributor wired to the real govulncheck binary.
func (*GoReachability) Contribute ¶
func (g *GoReachability) Contribute(ctx context.Context, target Target, vulns []Vulnerability) (map[VulnRef]Evidence, error)
Contribute runs govulncheck once and maps its findings onto the vulnerabilities being enriched. A vulnerability govulncheck did not report a finding for gets NO evidence — it stays Unknown (fail-closed), never fabricated as reachable or unreachable.
func (*GoReachability) Name ¶
func (*GoReachability) Name() string
func (*GoReachability) Supports ¶
func (*GoReachability) Supports(eco string) bool
type ReachabilityEvidence ¶
type ReachabilityEvidence struct {
State ReachabilityState
Analyzer string // e.g. "govulncheck"
Confidence Confidence
Facts []string // human-readable, e.g. "imported golang.org/x/crypto/ssh",
}
ReachabilityEvidence is the FIRST evidence kind: whether a vulnerability's code is actually reachable in this program's call graph. It answers the question a scanner can't — not "is a vulnerable module present?" but "can the vulnerable code be called?". Produced by a reachability contributor (Go → govulncheck; Rust → experimental).
Tri-state on purpose: "Unknown" (no analyzer for this ecosystem) is NOT "Unreachable". Collapsing them would silently hide vulnerabilities we simply could not analyze.
func (ReachabilityEvidence) Kind ¶
func (ReachabilityEvidence) Kind() string
Kind implements Evidence.
type ReachabilityState ¶
type ReachabilityState int
ReachabilityState is the reachability verdict.
const ( ReachUnknown ReachabilityState = iota // no analyzer ran for this ecosystem ReachReachable // an analyzer proved a call path ReachUnreachable // an analyzer proved the code never enters the call graph )
func ParseReachabilityState ¶
func ParseReachabilityState(s string) ReachabilityState
ParseReachabilityState is the inverse of String — it reconstructs a state from its serialized form (e.g. when reading a persisted catalogue). Unrecognized input is ReachUnknown (the fail-closed default).
func (ReachabilityState) String ¶
func (s ReachabilityState) String() string
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds the enabled contributors, run in registration order.
func NewRegistry ¶
func NewRegistry(cs ...EvidenceContributor) *Registry
NewRegistry builds a registry from the given contributors.
func (*Registry) Enrich ¶
Enrich runs every contributor over the vulnerabilities it supports and returns Findings with evidence attached. It is fail-closed and monotonic: a missing contributor for an ecosystem, or a contributor that errors, simply adds NO evidence — nothing is downgraded on absent evidence. Enrichment can only ever ADD signal; absence leaves a finding at its scanner severity (see policy.go).
func (*Registry) Register ¶
func (r *Registry) Register(c EvidenceContributor)
Register appends a contributor.
type Target ¶
type Target struct {
// EcosystemDir maps a normalized ecosystem to the module/source root its contributors
// should examine. A KEV/EPSS contributor ignores it; a reachability contributor needs it.
EcosystemDir map[string]string
}
Target carries what contributors need to do their work — e.g. a reachability contributor needs the source root (call-graph analysis needs source + a build). Extensible without changing the interface.
type Vulnerability ¶
type Vulnerability struct {
ID string // the discovering scanner's identifier, e.g. "GO-2026-5932", "CVE-2026-1234"
Aliases []string // other identifiers for the same advisory, e.g. ["CVE-…", "GHSA-…"]
Ecosystem string // normalized: "go", "rust", "npm", "oci", …
Package string // AFFECTED package (the reachability join key), e.g. "golang.org/x/crypto/openpgp"
Symbol string // optional affected symbol
Severity string // "CRITICAL" | "HIGH" | "MODERATE" | "LOW" | "" (as the scanner reported)
Source string // which scanner discovered it: "osv" | "trivy" | "grype"
}
Vulnerability is a normalized finding from the discovery layer — the join identity every contributor enriches. Kept minimal on purpose: each scanner maps its native output into this.
ID is the identifier the discovering scanner used, but it is NOT the correlation contract: the same underlying vulnerability carries several identifiers (CVE, GHSA, GO advisory), and a contributor may report under a different one than discovery did. Aliases holds the other known identifiers (OSV publishes them), and contributors correlate against Identifiers() — the full set — never a single scanner-specific ID.
func (Vulnerability) Identifiers ¶
func (v Vulnerability) Identifiers() []string
Identifiers returns every identifier this vulnerability is known by — its primary ID plus any aliases. Contributors correlate their findings against this set so a CVE-keyed discovery still joins a GO-advisory-keyed analyzer (and vice versa), without OSV-ID ever being the contract.
func (Vulnerability) Ref ¶
func (v Vulnerability) Ref() VulnRef
Ref is the join key for a vulnerability.