Documentation
¶
Overview ¶
Package advisory resolves vulnerability advisories for the commit0-analyzer scan target.
Advisory Sources ¶
Advisories are fetched from multiple sources and merged:
- Go vulnerability database (https://vuln.go.dev) — the only public source that reliably carries symbol-level data (specific vulnerable functions/methods), which is required for CONFIDENCE_SYMBOL_REACHABLE findings (govulndb.go, fetcher.go).
- OSV.dev per-ecosystem bundles for all supported ecosystems (osv.go).
- GitHub Security Advisories — offline OSV bundle as the breadth floor, plus the live GraphQL API when GITHUB_TOKEN is set (ghsa_source.go).
- GitLab advisory database — the MIT-licensed community mirror by default (gitlab_source.go).
- NVD — opt-in CPE-based breadth matching, always package-level (nvd.go).
Sources plug in through the Source interface (source.go) and are composed by MultiSource (merge.go): advisories are grouped by alias equivalence and folded fail-safe (max severity, union of ranges and sources; Withdrawn only when unanimous). An EnrichmentChain then layers on CVSS score computation, NVD CVSS/CWE authority, CISA KEV listing, EPSS probability, and CWE normalization, and risk.go fuses advisory data with the reachability tier into a deterministic 0–100 risk score.
Version Matching ¶
Each ecosystem registers a tri-state version comparator (comparator_registry.go): a version is Affected, NotAffected, or Undecidable. An unparseable version or unregistered ecosystem is Undecidable — never NotAffected — and the advisory is forwarded with Incomplete set. Only NotAffected may drop an advisory.
Responsibilities ¶
- Parse OSV-format JSON (and GitLab YAML) records into internal Advisory values with correct Advisory.SymbolLevel classification.
- Match a package@version against affected ranges via the per-ecosystem comparators (SEMVER half-open [introduced, fixed) intervals and exact version lists).
- Cache advisory data on disk with atomic writes (temp→fsync→rename) and cross-process file locking to prevent torn reads under concurrent access.
- Pin and verify snapshots by content digest; hard-error on digest mismatch (a mutable snapshot defeats reproducible CI).
- Warn loudly — via StalenessWarningError — when the snapshot is older than a configurable threshold. The warning is surfaced, never silently swallowed, because unknown ≠ safe at the data boundary.
- Convert internal Advisory values to *commit0v1.Advisory for embedding in an AnalyzeRequest; provenance fields (digest, age) go into finding properties, not the wire Advisory.
Data Boundary Invariant ¶
"unknown ≠ safe": a failed or stale advisory lookup must never be treated as "no vulnerabilities found". Callers must propagate errors and warnings and degrade to CONFIDENCE_UNKNOWN rather than suppressing findings.
Advisory Distribution ¶
This package NEVER bundles or redistributes advisory database content. Offline mode consumes the user's own pre-fetched snapshot (sidesteps licensing concerns). The network-fetch path writes to the cache directory using [atomicWrite] to keep the fetch and offline paths consistent.
Index ¶
- Constants
- Variables
- func BestCVSS(adv *Advisory) (vector string, baseScore float64, ok bool)
- func CWEName(id string) (string, bool)
- func ProvenanceString(meta []SourceContribution) string
- func RegisterComparator(ecosystem string, fn ComparatorFunc)
- func SeverityConflictString(meta []SourceContribution) string
- func StaleSourceString(stale []string) string
- func WithGHSAGraphQLURL(url string) ghsaOption
- func WithGHSAHTTPClient(c *http.Client) ghsaOption
- func WithGHSAToken(token string) ghsaOption
- func WithGitLabBaseURL(u string) gitlabOption
- func WithGitLabHTTPClient(c *http.Client) gitlabOption
- type Advisory
- type CVSSMetric
- type CWEEnricher
- type Cache
- type CacheConfig
- type ComparatorFunc
- type EPSSEnricher
- type EPSSScore
- type Enricher
- type EnrichmentChain
- type EnrichmentIncompleteError
- type Fetcher
- type FreshnessSLA
- type GHSASource
- type GitLabSource
- type KEVEnricher
- type KEVEntry
- type MultiSource
- type NVDCPESource
- type NVDEnricher
- type NVDOption
- type NamedSource
- type OSVBundleSource
- type Package
- type RefreshFallbackWarning
- type RiskScore
- type Severity
- type SnapshotManifest
- type Source
- type SourceContribution
- type SourcesIncompleteError
- type StalenessWarningError
- type Symbol
- type VersionRange
- type VersionVerdict
Constants ¶
const ( // ReachabilitySymbol is a concrete call path to the vulnerable symbol. ReachabilitySymbol = "symbol" // ReachabilityPackage is a reachable package without symbol-level proof. ReachabilityPackage = "package" // ReachabilityUnknown is an undecided reachability verdict (unknown ≠ safe). ReachabilityUnknown = "unknown" // ReachabilityNotReachable is a proven NOT_REACHABLE verdict (the only tier // that scores 0 — it is the sole proven-safe state). ReachabilityNotReachable = "not_reachable" )
Reachability tiers used as the second input to Score. They mirror the wire Confidence enum but are decoupled from it so the advisory package does not depend on the proto: the caller translates a finding's confidence to one of these strings.
const DefaultStalenessWarning = 7 * 24 * time.Hour
DefaultStalenessWarning is the default threshold after which a snapshot is considered stale. 7 days matches typical Go vuln DB update cadence.
const EcosystemCratesIO = "crates.io"
EcosystemCratesIO is the canonical ecosystem tag for Rust crates, matching the OSV schema value used by https://osv.dev (the crates.io/all.zip bundle) and the RustSec advisory database.
const EcosystemGo = "Go"
EcosystemGo is the canonical ecosystem tag for Go modules, matching the OSV schema value used by https://vuln.go.dev and https://osv.dev.
const EcosystemHex = "Hex"
EcosystemHex is the canonical ecosystem tag for Elixir/Erlang packages on Hex.pm, matching the OSV schema value used by https://osv.dev (the Hex/all.zip bundle).
const EcosystemMaven = "Maven"
EcosystemMaven is the canonical ecosystem tag for Maven (Java/JVM) packages, matching the OSV schema value used by https://osv.dev (the Maven/all.zip bundle).
const EcosystemNPM = "npm"
EcosystemNPM is the canonical ecosystem tag for npm packages, matching the OSV schema value used by https://osv.dev (the npm/all.zip bundle).
const EcosystemNuGet = "NuGet"
EcosystemNuGet is the canonical ecosystem tag for .NET/NuGet packages, matching the OSV schema value used by https://osv.dev (the NuGet/all.zip bundle).
const EcosystemPackagist = "Packagist"
EcosystemPackagist is the canonical ecosystem tag for PHP/Composer packages, matching the OSV schema value used by https://osv.dev (the Packagist/all.zip bundle).
const EcosystemPub = "Pub"
EcosystemPub is the canonical ecosystem tag for Dart/Flutter packages on pub.dev, matching the OSV schema value used by https://osv.dev (the Pub/all.zip bundle).
const EcosystemPyPI = "PyPI"
EcosystemPyPI is the canonical ecosystem tag for Python packages, matching the OSV schema value used by https://osv.dev (the PyPI/all.zip bundle).
const EcosystemRubyGems = "RubyGems"
EcosystemRubyGems is the canonical ecosystem tag for Ruby gems, matching the OSV schema value used by https://osv.dev (the RubyGems/all.zip bundle).
const EcosystemSwiftURL = "SwiftURL"
EcosystemSwiftURL is the canonical ecosystem tag for Swift packages distributed via the Swift Package Manager, matching the OSV schema value used by https://osv.dev (the SwiftURL/all.zip bundle).
Unlike most ecosystems, SwiftURL package identity is the git repository URL, not a registry name. OSV records store the bare URL without scheme or .git suffix (e.g. "github.com/apple/swift-nio"). The adapter normalizes the Package.resolved location field to match this form before querying.
const ManifestFilename = "commit0-analyzer-snapshot-manifest.json"
ManifestFilename is the well-known name for the snapshot manifest file inside a snapshot directory.
const SourceEPSS = "epss"
SourceEPSS is the --source flag token that opts the EPSS exploit-prediction enricher into the post-merge enrichment chain. EPSS is prioritization metadata (a CVE-keyed exploit-probability join), not a package→advisory source, and is opt-in because the feeds are heavy enough to slow a default scan.
const SourceGHSA = "ghsa"
SourceGHSA is the source attribution tag for the GitHub Security Advisory (GHSA) database. It covers both the offline OSV-format bundle (the github/advisory-database repository) and the live GraphQL delta/enrichment layer.
const SourceGitLab = "gitlab"
SourceGitLab is the source attribution tag for the GitLab Advisory Database (gemnasium-db). By default it mirrors the MIT-licensed community fork gitlab-org/advisories-community, which is time-delayed (~30 days) relative to the primary gemnasium-db but carries no GitLab usage restrictions and is therefore the correct default for this AGPL tool.
const SourceGoVulnDB = "go-vuln-db"
SourceGoVulnDB is the source attribution tag for the Go vulnerability database.
const SourceNVD = "nvd"
SourceNVD is the source-attribution tag for the NVD CVE-keyed enrichment role. It marks CVSS metrics and SourceContributions that NVD supplied by joining an advisory's existing CVE alias. This role adds no new package→advisory edges, so it is structurally FP-safe.
const SourceNVDCPE = "nvd-cpe"
SourceNVDCPE is the source-attribution tag for the opt-in, lower-confidence CPE-breadth role. Advisories carrying this tag were matched heuristically by CPE product, are never symbol-level, and are gated off by default downstream: the wiring layer maps this tag to properties["match"]="cpe-heuristic".
const SourceOSV = "osv.dev"
SourceOSV is the source attribution tag for the OSV.dev vulnerability database.
Variables ¶
var OSVBundleEcosystems = []string{ EcosystemGo, EcosystemNPM, EcosystemCratesIO, EcosystemPyPI, EcosystemMaven, EcosystemNuGet, EcosystemPackagist, EcosystemRubyGems, EcosystemHex, EcosystemPub, EcosystemSwiftURL, }
OSVBundleEcosystems lists every ecosystem whose advisory bundle is available at the OSV GCS bucket (<osvDefaultBaseURL>/<ecosystem>/all.zip).
These are the ecosystem names accepted by OSVBundleSource.Refresh — passing any of these values to Refresh will download and extract the corresponding bundle from osv.dev. The names match the "ecosystem" field in OSV JSON records and are therefore also correct for Package.Ecosystem when calling Query.
Each entry corresponds to an EcosystemXxx constant declared in source.go so callers do not use raw strings. When a new ecosystem is added to osv.dev, add its constant to source.go and append it here.
Functions ¶
func BestCVSS ¶
BestCVSS returns the most authoritative CVSS metric for an advisory: the vector with the highest positive base score. When no metric carries a positive score (e.g. only an unscored v4.0 vector is present) it still surfaces the first vector with score 0 so the vector is never silently dropped. ok is false only when the advisory carries no CVSS metric at all.
func CWEName ¶
CWEName returns the human-readable name for a canonical CWE id (e.g. "CWE-79") from the bundled static table. The second return is false for ids not in the table; callers must keep the id-only form rather than treating absence as an error.
func ProvenanceString ¶
func ProvenanceString(meta []SourceContribution) string
ProvenanceString renders the deterministic per-source provenance summary for a merged advisory's source metadata. It is the exported entry point used by the CLI wiring/render layer to surface the audit trail without recomputing it.
func RegisterComparator ¶
func RegisterComparator(ecosystem string, fn ComparatorFunc)
RegisterComparator registers a version-range comparator for the given ecosystem. It is intended to be called from package-level init() functions so that each language can register its comparator in its own file without editing a shared switch.
Registering the same ecosystem twice panics — this is a programming error that must be caught at startup, not silently overwritten.
func SeverityConflictString ¶
func SeverityConflictString(meta []SourceContribution) string
SeverityConflictString renders the per-source severity spread when sources actually disagree, and "" otherwise. Exported for the CLI wiring/render layer.
func StaleSourceString ¶
StaleSourceString renders the sorted, comma-joined stale-source list, and "" when nothing is stale. Exported for the CLI wiring/render layer.
func WithGHSAGraphQLURL ¶
func WithGHSAGraphQLURL(url string) ghsaOption
WithGHSAGraphQLURL overrides the GraphQL endpoint (used by tests to inject an httptest server).
func WithGHSAHTTPClient ¶
WithGHSAHTTPClient overrides the HTTP client used for GraphQL requests.
func WithGHSAToken ¶
func WithGHSAToken(token string) ghsaOption
WithGHSAToken sets the GitHub token explicitly, bypassing the GITHUB_TOKEN environment lookup.
func WithGitLabBaseURL ¶
func WithGitLabBaseURL(u string) gitlabOption
WithGitLabBaseURL overrides the gitlab.com base URL used to build the archive and project-API requests (used by tests and to retarget the primary gemnasium-db host).
func WithGitLabHTTPClient ¶
WithGitLabHTTPClient overrides the HTTP client used for downloads.
Types ¶
type Advisory ¶
type Advisory struct {
// ID is the canonical advisory identifier (e.g. "GO-2024-0001").
ID string
// Ecosystem is the package ecosystem this advisory belongs to (e.g. "Go").
// Set by the Source implementation that produced this advisory.
Ecosystem string
// Module is the affected Go module path.
Module string
// Aliases are alternative identifiers (CVE, GHSA IDs).
Aliases []string
// VersionRanges are the affected semver ranges.
VersionRanges []VersionRange
// Symbols are the specific vulnerable symbols (empty when SymbolLevel=false).
Symbols []Symbol
// SymbolLevel is true when at least one import entry carries symbol data.
// When false, the analyzer must degrade to package-level confidence.
SymbolLevel bool
// Sources lists the advisory data sources (always includes SourceGoVulnDB for MVP).
Sources []string
// Withdrawn is the RFC3339 timestamp at which this advisory was retracted by
// the Go vuln DB maintainers. A non-empty value means the advisory is no
// longer considered a real vulnerability. Query filters these out before
// returning results; this field is exposed so callers can inspect the
// reason an advisory was excluded when needed (e.g. in debug logging).
Withdrawn string
// FixRefs holds the URLs from the OSV references[] array whose type is "FIX".
// These point at the commits or patches that resolved the vulnerability and
// are used by later pipeline phases to fetch and extract vulnerable symbols.
// Sorted and deduplicated; empty slice when the record has no FIX references.
// Not included in ToProto — consumed Go-side before the proto is built.
FixRefs []string
// Incomplete is set to true when the version comparison for this advisory was
// undecidable (e.g. unparseable version string, unrecognised ecosystem). The
// advisory is still included in query results so the host can emit a synthetic
// UNKNOWN finding; dropping it would be a silent false negative.
// Not part of the wire proto.
Incomplete bool
// Versions is the explicit version list from the OSV affected[].versions field.
// It is populated when an affected entry has no SEMVER/ECOSYSTEM ranges — only
// the versions enumeration. AffectsVersionV uses it for exact-membership matching
// when VersionRanges is empty. When VersionRanges is non-empty, Versions is
// ignored (the range comparison is authoritative).
// Not part of the wire proto.
Versions []string
// UndecidableRanges is true when the OSV affected entry carried a non-version
// (GIT-commit) range commit0-analyzer cannot compare AND no versions[] enumeration to fall
// back on. AffectsVersionV returns VersionUndecidable in that case so the
// advisory is forwarded as an UNKNOWN finding rather than silently dropped
// (unknown != safe). It distinguishes a GIT-range-only entry (undecidable) from
// a truly empty entry with no version constraint at all (not affected).
// Not part of the wire proto.
UndecidableRanges bool
// Severity is the vulnerability risk level parsed from the OSV severity[]
// array (CVSS v3/v4 base score) or database_specific.severity string.
// SeverityUnspecified (zero value) means no severity data was present.
// The host may use this to surface risk tiers in findings without touching
// the wire proto (which is out of scope for this advisory layer).
// Not part of the wire proto.
Severity Severity
// Provenance — not part of the wire proto; stamped into finding properties.
SnapshotDigest string // content digest of the snapshot this advisory came from
SnapshotAge string // human-readable age string (e.g. "72h")
DBSourceVersion string // version string reported by the DB (e.g. "2024-06-01T00:00:00Z")
// CVSS holds every parsed CVSS metric for this advisory (possibly from
// multiple sources / versions). Severity is derived from these via
// severityFromMetrics when present.
CVSS []CVSSMetric
// EPSS is the exploit-prediction signal for this advisory's CVE; nil when none.
EPSS *EPSSScore
// KEV is the CISA known-exploited signal for this advisory's CVE; nil when none.
KEV *KEVEntry
// CWEs are the associated CWE identifiers (e.g. "CWE-79"); empty when none.
CWEs []string
// RiskScore is the fused risk-prioritization signal; nil until computed.
RiskScore *RiskScore
// SourceMeta records per-source contributions for conflict resolution and
// provenance; empty until populated by the merge layer.
SourceMeta []SourceContribution
}
Advisory is the internal representation of a resolved vulnerability advisory.
Provenance fields (SnapshotDigest, SnapshotAge, DBSourceVersion) are carried here so downstream components can stamp them into finding properties without coupling to cache internals. They do NOT appear on the wire Advisory proto.
func (*Advisory) AffectsVersion
deprecated
AffectsVersion reports whether the advisory affects the given version.
Routing is ecosystem-aware:
- npm: uses node-semver semantics via npmVersionInRange (bare versions, no "v" prefix required, correct prerelease and 4-part-version handling).
- All other ecosystems (Go, etc.): uses the existing Go semver path via versionInRange, which requires a canonical "vX.Y.Z" string.
Returns false on any parse error (conservative: unknown → not matched).
Deprecated: prefer AffectsVersionV, which returns a tri-state verdict so that parse errors and unknown ecosystems are never silently treated as not-affected.
func (*Advisory) AffectsVersionV ¶
func (a *Advisory) AffectsVersionV(version string) VersionVerdict
AffectsVersionV is the tri-state version of AffectsVersion.
Routing is ecosystem-aware, delegating to the comparator registered for a.Ecosystem via RegisterComparator. The built-in registrations cover Go, npm, crates.io, and PyPI. New ecosystems register their comparator in their own file's init() without editing this function.
When no comparator is registered for a.Ecosystem, or when a comparator returns VersionUndecidable, the caller must treat the advisory as "still possibly affected" and emit a synthetic UNKNOWN finding + set incomplete=true.
A parse error in any comparator returns VersionUndecidable, never VersionNotAffected. This ensures that an unparseable or unrouted version does not silently drop an advisory host-side (which would be a false negative).
type CVSSMetric ¶
type CVSSMetric struct {
// Version is the CVSS version: "3.0", "3.1", or "4.0".
Version string
// Vector is the full CVSS vector string, captured losslessly.
Vector string
// BaseScore is the computed base score (see type doc for v4.0 caveat).
BaseScore float64
// Source attributes which feed supplied this metric (e.g. "nvd", "ghsa").
// Empty when the producing source did not record an attribution.
Source string
}
CVSSMetric is a single parsed CVSS vector together with its computed base score. It is additive enrichment carried Go-side only — it never appears on the wire Advisory proto.
BaseScore semantics by version:
- "3.0" / "3.1": the exact base score computed from the vector per the official CVSS v3.x specification.
- "4.0": the vector is captured losslessly but BaseScore is 0 because the exact v4.0 base-score math is deferred (see cvss.go). A zero BaseScore on a v4.0 metric therefore means "not yet computed", and severityFromMetrics deliberately does NOT downgrade severity from it — unknown ≠ safe.
func ParseCVSS ¶
func ParseCVSS(vector string) (CVSSMetric, error)
ParseCVSS parses a CVSS v3.0, v3.1, or v4.0 vector string into a CVSSMetric.
For v3.0/3.1 it validates the required base metrics and computes the exact base score per the official CVSS v3.x specification (§7.1), reusing the established weight tables. For v4.0 it validates the required base metrics and captures the vector losslessly, but defers the exact base-score computation: the v4.0 scoring algorithm is the MacroVector lookup method, which is tracked as a follow-up. A v4.0 metric therefore carries BaseScore=0 ("not yet computed"); downstream severity derivation never treats that as a downgrade.
A malformed, unsupported, or incomplete vector returns an error so the caller treats the metric as unknown — never as a silent zero/None that could hide a Critical (unknown ≠ safe).
type CWEEnricher ¶
type CWEEnricher struct{}
CWEEnricher normalizes, validates, de-duplicates, and stably sorts the CWE identifiers already collected on each advisory by the GHSA/NVD sources. It does no network I/O, so it never fails: it is purely a deterministic clean-up pass.
An identifier that is not in the bundled name table is kept (id-only), never dropped — dropping a CWE would silently lose weakness context.
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache is a concurrency-safe, on-disk advisory cache with snapshot pinning, content-digest verification, and staleness warnings.
Concurrency guarantees:
- Per-key singleflight: only one goroutine fetches/reads a given key at a time.
- Atomic file writes: temp-file → fsync → rename to prevent torn reads.
- Cross-process file lock (flock LOCK_EX) on the cache directory lock file to prevent concurrent processes from writing the same key simultaneously.
func NewCache ¶
func NewCache(cfg CacheConfig) *Cache
NewCache constructs a Cache with the given configuration.
func (*Cache) Get ¶
Get returns advisories for pkg@version. It honours the following precedence:
- If SnapshotPin is set, query that snapshot directory (after digest verification).
- Otherwise, if Offline is true, query Dir (must be pre-populated).
- Otherwise, fetch from the network into Dir, then query.
A *StalenessWarningError is returned alongside advisories when the snapshot is older than the staleness threshold. Callers must handle this non-nil error and still use the embedded advisories.
func (*Cache) Query ¶
Query implements Source by delegating to Get. It allows Cache to be used directly as a Source in a MultiSource composition without a wrapper type. The method signature matches Source.Query exactly.
func (*Cache) Refresh ¶
Refresh ensures the writable cache Dir is populated and up-to-date for the given set of module paths. It is called once before the per-dep Get loop so that Get/Query remain network-free.
Refresh is a no-op when:
- SnapshotPin is set (pins are read-only and never fetched).
- Offline is true (network access is explicitly disabled).
- No Fetcher is configured.
Staleness check: Refresh fetches /index/db.json to read the live DB modified timestamp and compares it to the cached manifest's DBSourceVersion. A fetch is performed when:
- The cache Dir is missing or empty (no manifest).
- The cached DBSourceVersion is strictly older than the live modified.
- ForceUpdate is true (always re-fetch regardless of version match).
On a fetch error, Refresh returns a hard error without modifying the manifest (unknown ≠ safe: a failed fetch must never leave the cache appearing clean).
type CacheConfig ¶
type CacheConfig struct {
// Dir is the working cache directory for on-disk caching of fetched advisories.
Dir string
// SnapshotPin, if non-empty, pins queries to a pre-fetched snapshot directory
// rather than fetching from the network. Verified against the manifest digest.
// A pinned snapshot is read-only and never fetched or mutated; Refresh is a
// no-op when SnapshotPin is set.
SnapshotPin string
// Offline, if true, disables all network access. Requires SnapshotPin or a
// pre-populated Dir. Returns a clear error when the snapshot is missing.
// Refresh is a no-op when Offline is true.
Offline bool
// StalenessWarning is the age threshold past which a staleness warning is
// surfaced. Zero uses DefaultStalenessWarning.
StalenessWarning time.Duration
// Fetcher is the network client used to populate the writable Dir.
// Only consulted by Refresh; Get/Query are always network-free.
// Nil means online fetch is unavailable (equivalent to Offline for Refresh).
Fetcher *Fetcher
// ForceUpdate, if true, causes Refresh to re-fetch even when the cached
// DBSourceVersion already matches the live DB modified timestamp.
ForceUpdate bool
}
CacheConfig holds configuration for a Cache instance.
type ComparatorFunc ¶
type ComparatorFunc func(version string, r VersionRange) VersionVerdict
ComparatorFunc is the signature for an ecosystem-specific version range comparator. It receives the query version (canonical form, may carry a "v" prefix depending on the ecosystem) and a single VersionRange, and returns a tri-state verdict.
Contract:
- Parse errors MUST return VersionUndecidable, never VersionNotAffected. An undecidable verdict propagates upward as a synthetic UNKNOWN finding with incomplete=true — it is never silently dropped.
- The function MUST be safe to call concurrently from multiple goroutines.
type EPSSEnricher ¶
type EPSSEnricher struct {
// APIBaseURL is the FIRST.org EPSS API base; empty uses epssAPIBaseURL.
APIBaseURL string
// CSVURL is the daily snapshot URL; empty uses epssCSVURL.
CSVURL string
// HTTP is the client for both feeds; nil uses a client with defaultHTTPTimeout.
HTTP *http.Client
// CacheDir is where the CSV snapshot floor is cached. Empty disables the floor
// (API-only); offline mode then has no data source and fails closed.
CacheDir string
// Offline disables all network access. The CSV floor must already be cached.
Offline bool
// MaxAge bounds CSV cache trust before a refresh; zero uses epssDefaultMaxAge.
MaxAge time.Duration
// Now is the clock used for staleness; nil uses time.Now.
Now func() time.Time
}
EPSSEnricher fills Advisory.EPSS by joining on each advisory's CVE aliases.
It is hybrid: the FIRST.org query API provides the freshest scores, and the daily CSV snapshot is the offline floor (cached with a conditional GET). A CVE the feeds do not score is left untouched — that is a legitimate "no signal", NOT "safe". A genuine fetch failure with no usable cache is reported as an error so the caller marks the scan incomplete (unknown ≠ safe).
type EPSSScore ¶
type EPSSScore struct {
// Probability is the EPSS probability of exploitation in the next 30 days [0,1].
Probability float64
// Percentile is the EPSS percentile rank among all scored CVEs [0,1].
Percentile float64
// Date is the EPSS model date (YYYY-MM-DD) the score was published on.
Date string
}
EPSSScore is the FIRST Exploit Prediction Scoring System signal for a CVE. Filled by the EPSS enricher (later phase); nil when no EPSS data was fetched.
type Enricher ¶
type Enricher interface {
// Name returns a stable identifier used in partial-failure reporting.
Name() string
// Enrich mutates the given advisories in place. The slice elements are
// addressable, so implementations set fields via advs[i].Field = ....
Enrich(ctx context.Context, advs []Advisory) error
}
Enricher augments a batch of advisories with additional intelligence (CVSS detail, EPSS, KEV, CWE, etc.). Implementations mutate the advisories in place.
Failure semantics ("unknown ≠ safe"): an enricher that cannot complete (a network, HTTP, rate-limit, or parse failure) returns a non-nil error. The caller MUST treat that as incomplete enrichment — never as "no enrichment, so clean". A nil return means the enricher ran to completion (which may include legitimately finding no signal for a given advisory).
Implementations must be safe to call with partial data: an advisory missing a CVE alias, for instance, is simply left untouched rather than erroring.
type EnrichmentChain ¶
type EnrichmentChain []Enricher
EnrichmentChain runs a sequence of enrichers over the same advisory batch.
It runs every enricher even if an earlier one fails (a partial failure must not suppress the remaining signals), accumulates failures, and returns an *EnrichmentIncompleteError when any enricher failed. A chain with no failures returns nil. An empty chain is a successful no-op.
func (EnrichmentChain) Enrich ¶
func (c EnrichmentChain) Enrich(ctx context.Context, advs []Advisory) error
Enrich implements Enricher so chains compose. It mirrors MultiSource.Query's partial-failure aggregation.
func (EnrichmentChain) Name ¶
func (c EnrichmentChain) Name() string
Name implements Enricher so an EnrichmentChain can itself be nested in another chain. The name reflects that it is a composite.
type EnrichmentIncompleteError ¶
type EnrichmentIncompleteError struct {
// FailedEnrichers lists the name of every enricher that returned an error,
// in execution order.
FailedEnrichers []string
// Errors holds the per-enricher errors in the same order as FailedEnrichers.
Errors []error
}
EnrichmentIncompleteError is returned by EnrichmentChain.Enrich when one or more enrichers fail. Like SourcesIncompleteError, it is NOT fatal: the caller should warn per failed enricher and mark the scan incomplete (drives exit 3), then proceed with whatever enrichment succeeded.
func (*EnrichmentIncompleteError) Error ¶
func (e *EnrichmentIncompleteError) Error() string
type Fetcher ¶
type Fetcher struct {
// BaseURL is the root URL of the vuln.go.dev API (no trailing slash).
// Defaults to "https://vuln.go.dev"; override in tests via httptest.Server.URL.
BaseURL string
// HTTP is the HTTP client used for all requests. A non-nil client with a
// reasonable timeout is expected; NewFetcher sets a 30-second default.
HTTP *http.Client
}
Fetcher downloads OSV advisories from a vuln.go.dev-compatible endpoint (default: https://vuln.go.dev) into a local cache directory.
It is intentionally stateless: all state lives in the destination directory written by FetchModules, so concurrent Fetcher instances targeting distinct directories are safe without additional coordination.
func NewFetcher ¶
func NewFetcher() *Fetcher
NewFetcher returns a Fetcher targeting the real vuln.go.dev with a 30-second per-request timeout. Inject the returned value into CacheConfig.Fetcher.
func (*Fetcher) FetchModules ¶
func (f *Fetcher) FetchModules(ctx context.Context, modules []string, destDir string) (dbModified string, err error)
FetchModules downloads OSV advisories for the requested module paths from the vuln.go.dev v1 API and writes them atomically into destDir.
Protocol:
- GET /index/modules.json — full module→advisory index (fetched once).
- Collect unique GO-IDs whose module path is in the modules set.
- GET /ID/<id>.json for each unique ID; atomicWrite each into destDir.
- GET /index/db.json — return its "modified" for the caller to record in the manifest as DBSourceVersion.
Failure semantics ("unknown ≠ safe"):
- Any non-200 HTTP response or network error is a hard error.
- On any error, NO advisory files are written (all writes are deferred until all fetches succeed, so the caller never sees partial state).
- The caller (Cache.Refresh) is responsible for writing the manifest; FetchModules never writes the manifest itself.
Context cancellation is respected between individual ID fetches.
type FreshnessSLA ¶
type FreshnessSLA struct {
// Soft is the age past which a source is reported stale (warn-only).
Soft time.Duration
// Hard is the age past which a source contributes but is incomplete-eligible.
Hard time.Duration
// HardIncomplete enables the Hard threshold to mark the result incomplete.
// Default false → warn-only.
HardIncomplete bool
}
FreshnessSLA defines source-freshness thresholds. A source older than Soft is reported stale (warn + stale_source tag). A source older than Hard marks the result incomplete ONLY when HardIncomplete is set — the default is warn-only so stale data never produces a surprise exit-3.
func (FreshnessSLA) Evaluate ¶
func (f FreshnessSLA) Evaluate(meta []SourceContribution, now time.Time) (stale []string, incomplete bool)
Evaluate reports which sources are stale (older than Soft) and whether any source past Hard should mark the result incomplete (only when HardIncomplete). A source whose age cannot be determined is skipped here (its missing freshness is surfaced by the caller, not turned into a false staleness claim).
type GHSASource ¶
type GHSASource struct {
// GraphQLURL is the GitHub GraphQL endpoint. Defaults to the public API.
GraphQLURL string
// HTTP is the client used for GraphQL requests. Defaults to a 30-second client.
HTTP *http.Client
// Token is the GitHub token sent as a Bearer header. Falls back to the
// GITHUB_TOKEN environment variable when empty. When neither is set, the
// GraphQL layer is skipped.
Token string
// contains filtered or unexported fields
}
GHSASource implements Source against the GitHub Security Advisory database using the hybrid model from the advisory-intelligence plan:
- Offline floor (bundle): OSV-format JSON records cached per ecosystem under <cacheDir>/<ecosystem>/*.json, queried fully offline. This is the breadth floor and the only layer used when no GitHub token is available.
- Live delta/enrichment (GraphQL): the securityVulnerabilities query layers fresher entries plus CWE and CVSS-vector enrichment. It is token-gated: with no token the layer is skipped (degrade, not fail); a requested-and- attempted GraphQL call that errors makes the result incomplete.
Failure semantics ("unknown ≠ safe"): a GraphQL error when GHSA was requested propagates as a non-nil error so MultiSource marks the scan incomplete. A missing bundle directory mirrors OSVBundleSource: it returns no advisories (the "not yet refreshed" state) and the wiring layer is responsible for ensuring the bundle is present.
Thread safety: Query is safe to call from multiple goroutines. The bundle index is built once under a mutex and is read-only thereafter.
func NewGHSASource ¶
func NewGHSASource(cacheDir string, opts ...ghsaOption) *GHSASource
NewGHSASource returns a GHSASource whose offline bundle lives under cacheDir. Apply functional options to override the GraphQL endpoint, token, or HTTP client.
func (*GHSASource) Query ¶
Query implements Source. It returns advisories from the offline bundle and, when a token is available, layers in fresher/enriched GraphQL results.
An ecosystem GHSA does not serve returns (nil, nil). A GraphQL failure when the layer was attempted returns the bundle advisories alongside a non-nil error so the caller marks the scan incomplete.
type GitLabSource ¶
type GitLabSource struct {
// BaseURL is the gitlab.com root (no trailing slash). Defaults to
// gitlabDefaultBaseURL.
BaseURL string
// HTTP is the client used for archive/API downloads. Defaults to a 30-second
// client; inject a longer-timeout client via WithGitLabHTTPClient for slow
// links or very large archives.
HTTP *http.Client
// ForceUpdate, when true, forces Refresh to re-download even when the cache is
// still fresh.
ForceUpdate bool
// contains filtered or unexported fields
}
GitLabSource implements Source against the GitLab Advisory Database (gemnasium-db) offline-bundle model. Unlike OSVBundleSource it downloads ONE archive covering every ecosystem, safe-extracts the per-package YAML records preserving the "<package_type>/<package_slug>/<id>.yml" layout under cacheDir, and queries them fully offline. The manifest is written last so a crash mid-extraction leaves no valid manifest, forcing a re-fetch.
Failure semantics ("unknown ≠ safe"): a missing cache directory mirrors the other bundle sources — Query returns no advisories (the "not yet refreshed" state), and the wiring layer marks the scan incomplete when the source was explicitly requested but uncached. An advisory whose affected_range cannot be parsed into a comparable range is NEVER dropped: it is returned with UndecidableRanges=true so AffectsVersionV reports Undecidable and the host emits a synthetic UNKNOWN finding.
Thread safety: Query is safe to call from multiple goroutines (read-only file reads after extraction). Refresh serialises via a cross-process directory lock.
func NewGitLabSource ¶
func NewGitLabSource(cacheDir string, opts ...gitlabOption) *GitLabSource
NewGitLabSource returns a GitLabSource caching the extracted archive under cacheDir. Apply functional options to override the base URL or HTTP client.
func (*GitLabSource) Query ¶
Query returns advisories from the extracted cache for pkg at version. The package's advisory directory is "<cacheDir>/<package_type>/<package_slug>/"; every *.yml / *.yaml file in it is parsed and version-matched via the per-ecosystem comparator (adv.AffectsVersionV). Affected advisories are returned; undecidable ones are returned with Incomplete=true (UNKNOWN, never dropped); only provably not-affected advisories are dropped.
An ecosystem gemnasium does not serve, or a missing cache directory (not yet refreshed / no advisories for the package), returns (nil, nil) — never an error.
func (*GitLabSource) Refresh ¶
func (s *GitLabSource) Refresh(ctx context.Context) error
Refresh ensures the local cache is populated and current. It downloads the whole gemnasium-db archive once, safe-extracts the YAML records, and writes the manifest last. It is idempotent: a Refresh within the freshness window of the last successful one (and ForceUpdate=false) skips the download entirely, so calling it from each ecosystem block still fetches at most once per scan.
Failure semantics: any branch-resolution, download, or extraction failure is a hard error and the manifest is NOT written, so the next Refresh re-fetches.
type KEVEnricher ¶
type KEVEnricher struct {
// URL is the KEV catalog URL; empty uses kevCatalogURL.
URL string
// HTTP is the client; nil uses a client with defaultHTTPTimeout.
HTTP *http.Client
// CacheDir is where the catalog JSON is cached. Empty disables caching
// (online-only); offline mode then has no data source and fails closed.
CacheDir string
// Offline disables network access. The catalog must already be cached.
Offline bool
// MaxAge bounds cache trust before refresh; zero uses kevDefaultMaxAge.
MaxAge time.Duration
// Now is the clock used for staleness; nil uses time.Now.
Now func() time.Time
}
KEVEnricher fills Advisory.KEV by joining each advisory's CVE aliases against the CISA KEV catalog. A CVE that is not in the catalog is left untouched — a missing entry is "not currently catalogued", NEVER "safe". A fetch failure with no usable cache is reported as an error so the scan is marked incomplete.
type KEVEntry ¶
type KEVEntry struct {
// Listed is true when the CVE appears in the KEV catalog.
Listed bool
// DateAdded is the catalog addition date (YYYY-MM-DD).
DateAdded string
// DueDate is the federal remediation due date (YYYY-MM-DD).
DueDate string
// KnownRansomware is true when CISA marks the entry as used in ransomware.
KnownRansomware bool
}
KEVEntry is the CISA Known Exploited Vulnerabilities catalog signal for a CVE. Filled by the KEV enricher (later phase); nil when the CVE is not in the catalog OR the catalog could not be fetched — the distinction is carried by the scan's incomplete flag, never by silently treating a missing entry as "not exploited".
type MultiSource ¶
type MultiSource struct {
// contains filtered or unexported fields
}
MultiSource composes N advisory sources behind a single Source interface. It fans out Query calls to each source sequentially, collects per-source results, and merges them by alias-equivalence.
Failure semantics ("unknown ≠ safe"):
- A single source error does NOT abort the query. The error is recorded and the other sources' results are still merged and returned.
- When at least one source fails, the returned error is a *SourcesIncompleteError listing the failed source names. The caller must use this to warn and mark the scan incomplete.
- If ALL sources fail, an empty slice is returned alongside the error.
Concurrency: Query is safe to call from multiple goroutines; each call executes its fan-out sequentially within the call.
func NewMultiSource ¶
func NewMultiSource(sources ...NamedSource) *MultiSource
NewMultiSource returns a MultiSource that queries the given named sources in the order they are provided.
func (*MultiSource) Query ¶
Query implements Source. It fans out to each registered source, collects all advisories, merges by alias-equivalence, and returns a *SourcesIncompleteError if any source failed. Callers must check for *SourcesIncompleteError via errors.As and treat it as a warning (not a fatal abort).
type NVDCPESource ¶
type NVDCPESource struct {
// contains filtered or unexported fields
}
NVDCPESource implements Source. It is the opt-in, lower-confidence CPE-breadth matcher: it surfaces CVEs whose CPE product matches the queried package, tagged SourceNVDCPE, always package-level, and marked Incomplete unless an exact or decidably-in-range version match is provable. It is wired only when --source includes the distinct "nvd-cpe" token. A feed fetch failure is unknown (error), never an empty clean result.
func NewNVDCPESource ¶
func NewNVDCPESource(cacheDir string, opts ...NVDOption) *NVDCPESource
NewNVDCPESource returns an NVDCPESource backed by the feed cached under cacheDir.
type NVDEnricher ¶
type NVDEnricher struct {
// contains filtered or unexported fields
}
NVDEnricher implements Enricher. It joins each advisory to NVD by its existing CVE alias and attaches the authoritative CVSS vector(s), CWE ids, and a source contribution. It creates no new findings, so it cannot introduce false positives. A requested-but-unavailable feed is incomplete, never silent clean.
func NewNVDEnricher ¶
func NewNVDEnricher(cacheDir string, opts ...NVDOption) *NVDEnricher
NewNVDEnricher returns an NVDEnricher backed by the feed cached under cacheDir. With no WithNVDBaseURL it operates purely offline against the cached floor.
func (*NVDEnricher) Enrich ¶
func (e *NVDEnricher) Enrich(ctx context.Context, advs []Advisory) error
Enrich implements Enricher: join by CVE alias and attach NVD signal.
Failure semantics: if no advisory carries a CVE alias there is nothing to fetch and Enrich returns nil. Otherwise the feed is required; an unavailable feed returns a non-nil error (incomplete). When the live refresh fails but a cached floor exists, the cached enrichment is applied AND an error is returned (degrade + incomplete) — coverage is preserved without hiding the uncertainty.
type NVDOption ¶
type NVDOption func(*nvdFeedClient)
NVDOption configures an NVDEnricher / NVDCPESource feed client.
func WithNVDAPIKey ¶
WithNVDAPIKey sets the NVD API key (raises the rate-limit budget).
func WithNVDBaseURL ¶
WithNVDBaseURL sets the API 2.0 base URL (enables the live hybrid layer).
func WithNVDClock ¶
WithNVDClock injects the clock and sleep functions (for deterministic tests).
func WithNVDHTTPClient ¶
WithNVDHTTPClient overrides the HTTP client.
func WithNVDMaxRetries ¶
WithNVDMaxRetries overrides the backoff retry budget.
type NamedSource ¶
type NamedSource struct {
Name string
S Source
// Trust is the source's trust tier, consulted only as a tie-break in
// chooseRepresentative AFTER the symbol-level and range-width rules. A higher
// value wins. The zero value ("unset") expresses no preference, preserving the
// pre-trust representative choice (the lexicographic-ID tie-break). Trust never
// changes WHICH advisory groups exist — only which member copy represents a
// group — so adding a more-trusted source can never drop coverage.
Trust int
}
NamedSource pairs a Source implementation with a human-readable name used in log/warning messages and SourcesIncompleteError.FailedSources.
type OSVBundleSource ¶
type OSVBundleSource struct {
// BaseURL is the root URL of the OSV GCS bucket (no trailing slash).
// Defaults to "https://osv-vulnerabilities.storage.googleapis.com".
BaseURL string
// HTTP is the client used for bundle downloads. A 60-second timeout is set
// by default to accommodate large initial downloads (Go bundle ~50 MiB
// compressed); callers may inject a shorter-timeout client for tests.
HTTP *http.Client
// ForceUpdate, when true, causes Refresh to re-download the bundle even
// when the server responds 304 Not Modified (mirrors Cache.ForceUpdate).
ForceUpdate bool
// StalenessWarning is unused by OSVBundleSource (bundles are refreshed
// explicitly via Refresh); retained for API symmetry with CacheConfig.
StalenessWarning time.Duration
// contains filtered or unexported fields
}
OSVBundleSource implements Source against the OSV offline bundle model. It downloads <BaseURL>/<ecosystem>/all.zip, safe-extracts the OSV JSON records into <cacheDir>/<ecosystem>/, and queries them fully offline via the shared dirSource. The manifest is written last so a crash mid-extraction leaves no valid manifest, forcing a re-fetch on the next Refresh.
Thread safety: Refresh and Query are safe to call from multiple goroutines — Refresh acquires a directory lock (flock) and Query is read-only after extraction. Concurrent Refresh calls for the same ecosystem will serialise via the lock; concurrent Query calls are always safe.
func NewOSVBundleSource ¶
func NewOSVBundleSource(cacheDir string, opts ...osvOption) *OSVBundleSource
NewOSVBundleSource returns an OSVBundleSource that caches bundles under cacheDir. Apply functional options to override the default BaseURL, HTTP client, or ForceUpdate flag.
func (*OSVBundleSource) Query ¶
func (s *OSVBundleSource) Query(ctx context.Context, pkg Package, version string) ([]Advisory, error)
Query returns advisories from the OSV bundle cache for pkg at version. It delegates to the shared dirSource over <cacheDir>/<pkg.Ecosystem>/.
If the ecosystem cache directory does not exist (i.e. Refresh has not been called for this ecosystem), Query returns (nil, nil) — not an error. The caller is responsible for calling Refresh before the first Query.
Returned advisories have Sources=["osv.dev"] and Ecosystem=pkg.Ecosystem. Withdrawn advisories are excluded (inherited from parseOSVRecord via dirSource).
func (*OSVBundleSource) Refresh ¶
func (s *OSVBundleSource) Refresh(ctx context.Context, ecosystem string) error
Refresh ensures the local cache for ecosystem is populated and up-to-date. It performs a conditional GET (If-None-Match with the stored ETag) when a prior manifest exists, skipping extraction when the server responds 304.
Failure semantics ("unknown ≠ safe"):
- Any HTTP non-200/non-304 status, network error, read error, zip-safety violation, or extraction error is a hard error.
- On any error, the manifest is NOT written, so verifyManifest will fail on the next Query call, forcing a re-fetch.
- The manifest is written LAST after all entries are extracted, so a crash mid-extraction is detected by the missing manifest on the next call.
type Package ¶
type Package struct {
// Ecosystem is the language ecosystem, e.g. EcosystemGo ("Go"), "npm", "PyPI".
Ecosystem string
// Name is the ecosystem-specific package name or module path.
// For Go this is the module path (e.g. "github.com/foo/bar").
Name string
}
Package identifies a package within a specific language ecosystem. It is the unit of identity passed to Source.Query so that a single source implementation can serve multiple ecosystems or decline to handle ecosystems it does not cover.
type RefreshFallbackWarning ¶
type RefreshFallbackWarning struct {
// Warning is a human-readable description of the degraded state.
Warning string
// ProbeErr is the underlying error from the staleness-probe fetch.
ProbeErr error
}
RefreshFallbackWarning is returned by Refresh when the live-DB staleness probe (GET /index/db.json) fails BUT a valid local cache already exists.
"unknown ≠ safe" applies here: the caller MUST surface this warning AND mark the scan incomplete — the cached data may be stale. The scan result must never appear as a clean pass when live freshness could not be confirmed.
Contrast with a hard error: if the cache is also missing or unverifiable, Refresh returns a plain error (not RefreshFallbackWarning) and the scan must abort with exit 3 rather than producing results at all.
func (*RefreshFallbackWarning) Error ¶
func (e *RefreshFallbackWarning) Error() string
func (*RefreshFallbackWarning) Unwrap ¶
func (e *RefreshFallbackWarning) Unwrap() error
type RiskScore ¶
type RiskScore struct {
// Score is the numeric risk on a 0–100 scale.
Score float64
// Tier is the human-readable band (e.g. "critical", "high").
Tier string
// Rationale explains, deterministically, how the score was derived.
Rationale string
}
RiskScore is the fused, explainable risk-prioritization signal. Filled by the risk-fusion phase; nil until then.
func Score ¶
Score fuses reachability with CVSS, KEV, and EPSS into a deterministic, explainable 0–100 risk score. It is a pure function: no clock, no network, no map iteration, fully reproducible for golden/SARIF stability.
Invariants:
- NOT_REACHABLE → 0 (the only proven-safe state).
- KEV dominates: a KEV-listed reachable finding is boosted into the top band.
- Monotonic in each input (CVSS, reachability tier, EPSS, KEV).
- A reachable finding never falls below its reachability-implied floor; missing enrichment never demotes it to "ignore".
type Severity ¶
type Severity int
Severity is the risk level of a vulnerability, derived from the CVSS base score or the textual severity in an OSV record's severity[] array. The integer values are ordered: Unspecified < Low < Medium < High < Critical.
const ( // SeverityUnspecified is the zero value when no severity data is present. SeverityUnspecified Severity = iota // SeverityLow corresponds to CVSS base score 0.1–3.9. SeverityLow // SeverityMedium corresponds to CVSS base score 4.0–6.9. SeverityMedium // SeverityHigh corresponds to CVSS base score 7.0–8.9. SeverityHigh // SeverityCritical corresponds to CVSS base score 9.0–10.0. SeverityCritical )
type SnapshotManifest ¶
type SnapshotManifest struct {
// ContentDigest is a SHA-256 hex digest of the sorted concatenation of all
// OSV JSON file contents in the snapshot directory (excluding this manifest).
ContentDigest string `json:"content_digest"`
// BuildTimestamp is when this snapshot was created / last fetched.
BuildTimestamp time.Time `json:"build_timestamp"`
// DBSourceVersion is an opaque version string from the upstream DB
// (e.g. the modified timestamp of the latest entry fetched).
DBSourceVersion string `json:"db_source_version"`
}
SnapshotManifest is the on-disk provenance record for a pinned snapshot. It is written alongside the OSV JSON files and verified on every load.
type Source ¶
type Source interface {
// Query returns all advisories that affect pkg at the given version.
// version must be a canonical semver string (e.g. "v1.2.3").
// Returns (nil, nil) when pkg.Ecosystem is not served by this source.
Query(ctx context.Context, pkg Package, version string) ([]Advisory, error)
}
Source is the seam through which advisory backends plug into the resolution pipeline. The MVP provides only one implementation: [goVulnDBClient] (the Go vulnerability database). Future sources — OSV.dev, GHSA — will implement this interface and be composed via a merge layer (roadmap, not MVP).
Contract:
- Query must be safe to call concurrently from multiple goroutines.
- Query returns only advisories whose affected version ranges include version.
- An empty result is NOT an error; it means the package@version is clean per this source, or that the source does not cover pkg.Ecosystem. "No advisory found" is distinct from "query failed" — callers must treat a non-nil error as unknown, not safe.
- Every returned Advisory must carry source attribution in Advisory.Sources.
- An implementation may serve one or more ecosystems and MUST return (nil, nil) for ecosystems it does not cover.
type SourceContribution ¶
type SourceContribution struct {
// Name is the source name (e.g. "ghsa", "nvd", "go-vuln-db").
Name string
// Severity is the severity this source reported for the advisory.
Severity Severity
// Vector is the CVSS vector this source reported, if any.
Vector string
// FetchedAt is the RFC3339 time this source's data was fetched.
FetchedAt string
// SnapshotAge is a human-readable freshness string (e.g. "72h").
SnapshotAge string
}
SourceContribution records what a single source contributed to a merged advisory so cross-source conflict resolution and provenance reporting can decide without re-querying the source.
type SourcesIncompleteError ¶
type SourcesIncompleteError struct {
// FailedSources lists the name of every source that returned a non-nil error.
FailedSources []string
// Errors holds the per-source errors in the same order as FailedSources.
Errors []error
}
SourcesIncompleteError is returned by MultiSource.Query when one or more sources fail. It is NOT a fatal error: the caller (CLI scan loop) should:
- Print a warning for each failed source to stderr.
- Mark the scan incomplete (drives exit 3 via policy.EvalFlags.Incomplete).
- Continue gating on whatever advisories the succeeding sources returned.
"unknown ≠ safe": a partial result is never silently treated as a clean pass.
func (*SourcesIncompleteError) Error ¶
func (e *SourcesIncompleteError) Error() string
type StalenessWarningError ¶
type StalenessWarningError struct {
Warning string
Age time.Duration
Threshold time.Duration
Advisories []Advisory
}
StalenessWarningError is returned (wrapping the fetched advisories) when a snapshot is older than the configured staleness threshold. It is a WARNING, not a fatal error: callers should surface the warning AND use the advisories.
The "unknown ≠ safe" invariant requires warnings to be surfaced, never silently swallowed. Callers must use errors.As to extract the advisories.
func (*StalenessWarningError) Error ¶
func (e *StalenessWarningError) Error() string
type Symbol ¶
type Symbol struct {
// Package is the fully-qualified Go import path (e.g. "crypto/tls").
Package string
// Name is the symbol name (e.g. "Conn.Read" or "ParseCert").
Name string
}
Symbol is a vulnerable function or method within a package.
type VersionRange ¶
type VersionRange struct {
Introduced string // inclusive lower bound; empty = v0.0.0
Fixed string // exclusive upper bound; empty = no fix yet
LastAffected string // inclusive upper bound (OSV last_affected); empty = not used
}
VersionRange represents a single semver range event pair. Both Introduced and Fixed are canonical semver strings (e.g. "v1.2.3"). An empty Introduced means "since the beginning" (v0.0.0). An empty Fixed and empty LastAffected means "unfixed" (still vulnerable). At most one of Fixed and LastAffected is set per range.
type VersionVerdict ¶
type VersionVerdict int
VersionVerdict is the tri-state result of a version range comparison.
A parse error or unrecognised ecosystem always returns VersionUndecidable, never VersionNotAffected. This ensures that an unparseable or unrouted version does not silently drop an advisory host-side (which would be a false negative).
const ( // VersionNotAffected means the version is provably outside every affected range. // This is the ONLY safe reason to drop an advisory. VersionNotAffected VersionVerdict = iota // VersionAffected means the version falls within at least one affected range. VersionAffected // VersionUndecidable means the comparison could not be completed — typically // due to an unparseable version string or an unrecognised ecosystem. The // advisory MUST still be forwarded and the dep MUST be marked incomplete=true. VersionUndecidable )
Source Files
¶
- cache.go
- cargo_semver.go
- comparator_registry.go
- composer_version.go
- conflict.go
- cvss.go
- cwe.go
- cwe_names.go
- doc.go
- enrichment.go
- epss.go
- fetcher.go
- ghsa_source.go
- gitlab_source.go
- govulndb.go
- hex_version.go
- kev.go
- maven_version.go
- merge.go
- model.go
- npm_semver.go
- nuget_version.go
- nvd.go
- osv.go
- pep440.go
- pub_version.go
- risk.go
- rubygems_version.go
- semver.go
- source.go
- swift_version.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ghfetch fetches the unified diff and changed-file contents for a commit URL, producing the inputs that the symbol extractor consumes.
|
Package ghfetch fetches the unified diff and changed-file contents for a commit URL, producing the inputs that the symbol extractor consumes. |
|
Package parity is the advisory-intelligence quality harness: it measures commit0-analyzer's advisory coverage against external scanners (osv-scanner, grype, trivy, govulncheck) on a fixed corpus of real repositories and records the false-positive / false-negative deltas with a reason for each.
|
Package parity is the advisory-intelligence quality harness: it measures commit0-analyzer's advisory coverage against external scanners (osv-scanner, grype, trivy, govulncheck) on a fixed corpus of real repositories and records the false-positive / false-negative deltas with a reason for each. |
|
Package symbolextract invokes a plugin's --extract-symbols subcommand to identify which exported symbols were touched by a security-fix patch.
|
Package symbolextract invokes a plugin's --extract-symbols subcommand to identify which exported symbols were touched by a security-fix patch. |
|
Package symbolindex provides a persistent cache of advisory → resolved symbol mappings so the symbol-fetch network roundtrip is paid once per advisory and subsequent scans are served from disk.
|
Package symbolindex provides a persistent cache of advisory → resolved symbol mappings so the symbol-fetch network roundtrip is paid once per advisory and subsequent scans are served from disk. |