Ecosystems
The SDK defines all 19 OSV ecosystems as typed constants — no stringly-typed mistakes.
Full list
| Constant | Value | Notes |
|---|---|---|
EcosystemGo | Go | Go module path |
EcosystemNpm | npm | NPM package name |
EcosystemPyPI | PyPI | Normalized PyPI name |
EcosystemRubyGems | RubyGems | Gem name |
EcosystemCratesIo | crates.io | Rust crate |
EcosystemPackagist | Packagist | PHP |
EcosystemMaven | Maven | Java — name is groupId:artifactId |
EcosystemNuGet | NuGet | .NET |
EcosystemHex | Hex | Erlang/Elixir |
EcosystemPub | Pub | Dart |
EcosystemLinux | Linux | Kernel only |
EcosystemDebian | Debian | May have :<RELEASE> suffix |
EcosystemAlpine | Alpine | Requires :v<RELEASE> suffix |
EcosystemRocky | Rocky | May have :<RELEASE> suffix |
EcosystemAlmaLinux | AlmaLinux | May have :<RELEASE> suffix |
EcosystemAndroid | Android | Component name |
EcosystemOSSFuzz | OSS-Fuzz | Fuzz target |
EcosystemConanCenter | ConanCenter | C/C++ |
EcosystemGitHubActions | GitHub 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
// 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