Skip to content

Ecosystems

The SDK defines all 19 OSV ecosystems as typed constants — no stringly-typed mistakes.

Full list

ConstantValueNotes
EcosystemGoGoGo module path
EcosystemNpmnpmNPM package name
EcosystemPyPIPyPINormalized PyPI name
EcosystemRubyGemsRubyGemsGem name
EcosystemCratesIocrates.ioRust crate
EcosystemPackagistPackagistPHP
EcosystemMavenMavenJava — name is groupId:artifactId
EcosystemNuGetNuGet.NET
EcosystemHexHexErlang/Elixir
EcosystemPubPubDart
EcosystemLinuxLinuxKernel only
EcosystemDebianDebianMay have :<RELEASE> suffix
EcosystemAlpineAlpineRequires :v<RELEASE> suffix
EcosystemRockyRockyMay have :<RELEASE> suffix
EcosystemAlmaLinuxAlmaLinuxMay have :<RELEASE> suffix
EcosystemAndroidAndroidComponent name
EcosystemOSSFuzzOSS-FuzzFuzz target
EcosystemConanCenterConanCenterC/C++
EcosystemGitHubActionsGitHub Actions{owner}/{repo}

Grouped by category

Ecosystem → version scheme

An ecosystem doesn't just name a registry — it also implies how versions sort, which is exactly what a range.type needs (see RangeType). This mapping is why you can't compare versions with a plain string <.

Naming conventions & suffixes

The package.name string is not free-form — each ecosystem has its own shape, and some carry a mandatory or optional :<release> suffix. Getting this wrong is the most common reason a HasEcosystem match silently fails.

Distro suffixes are part of the ecosystem, not the name

For Alpine the release suffix (Alpine:v3.18) is required; for Debian/Rocky/AlmaLinux it is optional. The suffix lives on the ecosystem string, so an exact-match HasEcosystem(EcosystemAlpine) will not match Alpine:v3.18. Compare with the base constant only when you have normalized the suffix away.

Usage

go
// Check a single ecosystem
if v.Affected.HasEcosystem(osv.EcosystemPyPI) {
    // ...
}

// Filter affected entries
pypiAffected := v.Affected.FilterByEcosystem(osv.EcosystemPyPI)

// Maven decomposition
for _, a := range v.Affected {
    if a.Package != nil && a.Package.IsMaven() {
        fmt.Println(a.Package.GetGroupID())    // groupId
        fmt.Println(a.Package.GetArtifactID()) // artifactId
    }
}

Two entry points consume an Ecosystem constant — one asks a yes/no question, the other returns a narrowed slice. Both walk the same AffectedSlice:

Ecosystem is a typed string, not a free string

Pass a constant like osv.EcosystemPyPI, not the literal "PyPI". The constant carries the exact casing the OSV spec requires, so the comparison is case-sensitive and typo-proof.

FilterByEcosystem guards the slice, not the element's Package

HasEcosystem checks item.Package != nil before reading .Ecosystem (line 49), so a null/missing package on an entry is skipped safely. FilterByEcosystem only guards against the slice itself being nil (if x == nil { return nil }, line 69) — its predicate then reads affected.Package.Ecosystem directly, with no per-element nil check. So an affected entry whose package is null will panic. In practice every well-formed OSV affected entry carries a package, but if you parse untrusted data, validate first with [[osv-validate]] or guard the slice yourself.

Maven name decomposition

GetGroupID / GetArtifactID use strings.SplitN(name, ":", 2), so a name like org.apache.commons:collections4 splits into org.apache.commons (group) and collections4 (artifact) — only the first : is a separator. Both are nil-safe: a nil Package or a name without a : returns "" rather than panicking. They work on any Package but only carry meaning when IsMaven() is true.

Source: package.go

Last updated:

Released under the MIT License.