Skip to content

IsAffected panics on an advisory with an unrecognized ecosystem, crashing the whole scan #2867

Description

@Lougarou

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:304VulnerabilitiesAffectingPackageIsAffected, 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.

Relationship to #2831 / #2837

#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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions