Summary
vulns.IsAffected panics when an OSV advisory's affected[].package.ecosystem is a value that osvecosystem.MustParse does not recognize. Because IsAffected is called for every advisory on the match path, a single such advisory aborts the entire scan instead of being skipped.
Affected code
internal/utility/vulns/vulnerability.go:160 (and :113 in AffectsEcosystem):
if osvecosystem.MustParse(affected.GetPackage().GetEcosystem()).Equal(imodels.Ecosystem(pkg)) && ...
osvecosystem.MustParse panics (Failed MustParse: base ecosystem does not exist in osvschema: "…") on any unrecognized ecosystem string.
- Reached from the normal local-DB match path:
internal/clients/clientimpl/localmatcher/zip.go:304 → VulnerabilitiesAffectingPackage → IsAffected, which loops over v.GetAffected() and calls MustParse on each entry.
Reproduction
A recognized-ecosystem package matched against a multi-ecosystem advisory where one affected[] entry uses an ecosystem the build's vendored osv-schema doesn't know. The package isn't in-range for the known entry, so IsAffected falls through to the unknown entry and panics:
vuln := &osvschema.Vulnerability{
Id: "EXAMPLE-multi",
Affected: []*osvschema.Affected{
{ // npm entry the package is NOT in-range for (5.0.0 <= v < 6.0.0)
Package: &osvschema.Package{Ecosystem: "npm", Name: "my-package"},
Ranges: []*osvschema.Range{{Type: osvschema.Range_ECOSYSTEM,
Events: []*osvschema.Event{{Introduced: "5.0.0"}, {Fixed: "6.0.0"}}}},
},
{ // ecosystem the vendored osv-schema does not know
Package: &osvschema.Package{Ecosystem: "SomeFutureEcosystem", Name: "other"},
Ranges: []*osvschema.Range{{Type: osvschema.Range_ECOSYSTEM,
Events: []*osvschema.Event{{Introduced: "0"}, {Fixed: "2.0.0"}}}},
},
},
}
pkg := &extractor.Package{Name: "my-package", Version: "1.0.0", PURLType: purl.TypeNPM}
vulns.IsAffected(vuln, pkg) // panics: base ecosystem does not exist in osvschema: "SomeFutureEcosystem"
Why it's reachable (no attacker required)
OSV advisories routinely list multiple ecosystems in affected[], and a per-ecosystem DB zip stores the full advisory record (all affected[] entries, not just the matching one). The vendored osv-schema ecosystem registry is pinned per osv-scanner release, while osv.dev's ecosystem set evolves over time. So once osv.dev introduces a new ecosystem that appears in a multi-ecosystem advisory, older osv-scanner builds still pinned in CI will panic on it — a forward-compatibility availability problem with a broad blast radius for a tool that runs in many CI pipelines. It is also deterministically triggerable today with a custom/local advisory database.
#2837 fixed the version-parser panic — semantic.Parse is now skipped gracefully for ecosystems that have no version parser (e.g. GitHub Actions). This is a different, still-present site: the ecosystem-name osvecosystem.MustParse at vulnerability.go:113 and :160 was not changed and still panics on an unrecognized ecosystem string.
Suggested fix
Use the non-panicking osvecosystem.Parse and skip the affected[] entry on error — the same "skip, don't panic on unsupported ecosystems" approach taken in #2837. The default (recognized) path is unaffected.
I have a fix and a reproducing test ready, and would be happy to open a PR once this is assigned.
This was found via automated state-space analysis and confirmed with a reproducing Go test against the current main.
Summary
vulns.IsAffectedpanics when an OSV advisory'saffected[].package.ecosystemis a value thatosvecosystem.MustParsedoes not recognize. BecauseIsAffectedis called for every advisory on the match path, a single such advisory aborts the entire scan instead of being skipped.Affected code
internal/utility/vulns/vulnerability.go:160(and:113inAffectsEcosystem):osvecosystem.MustParsepanics (Failed MustParse: base ecosystem does not exist in osvschema: "…") on any unrecognized ecosystem string.internal/clients/clientimpl/localmatcher/zip.go:304→VulnerabilitiesAffectingPackage→IsAffected, which loops overv.GetAffected()and callsMustParseon each entry.Reproduction
A recognized-ecosystem package matched against a multi-ecosystem advisory where one
affected[]entry uses an ecosystem the build's vendoredosv-schemadoesn't know. The package isn't in-range for the known entry, soIsAffectedfalls through to the unknown entry and panics:Why it's reachable (no attacker required)
OSV advisories routinely list multiple ecosystems in
affected[], and a per-ecosystem DB zip stores the full advisory record (allaffected[]entries, not just the matching one). The vendoredosv-schemaecosystem registry is pinned per osv-scanner release, while osv.dev's ecosystem set evolves over time. So once osv.dev introduces a new ecosystem that appears in a multi-ecosystem advisory, older osv-scanner builds still pinned in CI will panic on it — a forward-compatibility availability problem with a broad blast radius for a tool that runs in many CI pipelines. It is also deterministically triggerable today with a custom/local advisory database.Relationship to #2831 / #2837
#2837 fixed the version-parser panic —
semantic.Parseis now skipped gracefully for ecosystems that have no version parser (e.g. GitHub Actions). This is a different, still-present site: the ecosystem-nameosvecosystem.MustParseatvulnerability.go:113and:160was not changed and still panics on an unrecognized ecosystem string.Suggested fix
Use the non-panicking
osvecosystem.Parseand skip theaffected[]entry on error — the same "skip, don't panic on unsupported ecosystems" approach taken in #2837. The default (recognized) path is unaffected.I have a fix and a reproducing test ready, and would be happy to open a PR once this is assigned.
This was found via automated state-space analysis and confirmed with a reproducing Go test against the current
main.