How matching works
How Basilisk joins package identities to advisories and evaluates affected versions.
There are two matchers. One connects PURLs to OSV affected entries. The other connects CPE identities to NVD configurations. Both compare the advisory data with versions published on conda-forge.
The matchers do not try to decide whether a vulnerability is exploitable in your environment. They answer a smaller question: is a known conda-forge version inside the range reported by the advisory source?
A match is not a verdict
An evaluated result means the version is inside a reported range. Runtime configuration, patches applied by a conda-forge build, and whether vulnerable code is reachable are not evaluated yet.
The version universe
For each package, Basilisk takes the version strings from conda-forge repodata and combines versions from all subdirs into one list.
For now, matching stops at the version. It does not distinguish platforms, builds, or build numbers. This means a patched conda-forge rebuild can still match an upstream advisory for the same version.
Basilisk also assumes that a conda-forge version maps to the same upstream version. The affected-version set keeps the original repodata string.
OSV and PURLs
The osv-purl matcher considers primary and alternative PURLs from purl-associator.
First, the identity and OSV entry need to belong to the same ecosystem. An npm advisory cannot match a PyPI identity just because both packages have the same name.
Names are normalized before the join:
- PyPI names use PEP 503 normalization: lowercase, with runs of
-,_, and.treated as the same separator. - Other ecosystem names are lowercased.
- GitHub repository URLs are lowercased, normalized to HTTPS, and stripped of a trailing
.git.
After the identity joins an affected entry, the matcher checks its ranges and explicit version list.
Version comparators
Version rules differ between ecosystems, so Basilisk picks the comparator from the PURL type:
| Identity | Comparator |
|---|---|
| PyPI | PEP 440 |
| crates.io, npm, RubyGems | Semantic Versioning |
| GitHub, CRAN, and other fallback cases | conda version ordering |
| NVD CPE ranges | conda version ordering |
The result records which comparator was used. If that comparator cannot parse a bound, Basilisk does not quietly treat it as open-ended.
OSV range events
OSV stores ranges as events. Basilisk sorts them with the selected comparator and checks each known conda-forge version.
introducedopens an affected interval.fixedcloses it and is not itself affected. The interval is[introduced, fixed).last_affectedcloses the interval after that version, solast_affecteditself is included.introduced: "0"means there is no lower bound.- Explicit versions are matched against the version strings in the conda-forge universe.
Worked example: an OSV fixed event
This example uses a PyPI identity, so the matcher selects PEP 440.
The package identity and known conda-forge versions are:
{
"identity": "pkg:pypi/pillow",
"versions": ["9.1.0", "10.1.0", "10.2.0"],
"latest": "10.2.0"
}The OSV affected entry has an unbounded start and a fix at 10.2.0:
{
"package": { "ecosystem": "PyPI", "name": "Pillow" },
"ranges": [{
"type": "ECOSYSTEM",
"events": [
{ "introduced": "0" },
{ "fixed": "10.2.0" }
]
}]
}The interval is [0, 10.2.0). The fixed version closes the interval and is not affected.
| Version | Result | Reason |
|---|---|---|
9.1.0 | affected | after the unbounded start and before the fix |
10.1.0 | affected | before the fix |
10.2.0 | not affected | the fixed endpoint is excluded |
The matcher writes the versions that actually exist in conda-forge repodata:
{
"matcher": "osv-purl",
"comparator": "pep440",
"applicability": "evaluated",
"affected_versions": ["9.1.0", "10.1.0"],
"active_on_latest": false
}active_on_latest is false because 10.2.0 is not in the affected-version set.
An advisory can contain several ranges or affected entries. Basilisk combines the versions found through all entries for the same package, advisory, and identity.
Git commit ranges cannot usually be compared to release versions. If an entry only has a GIT range and no explicit versions, the identity match is kept as unevaluated rather than dropped.
NVD and CPEs
The nvd-cpe matcher joins package CPEs to NVD criteria using the lowercase (vendor, product) pair. It only considers application CPEs (part = a) marked vulnerable: true.
For each matching criterion:
- Inclusive and exclusive bounds are checked against the conda-forge versions.
- A concrete version inside the CPE is treated as an exact version under the comparator.
- A wildcard version without bounds cannot be narrowed to specific versions.
The last case becomes version_unbounded and includes every known version of the package.
Worked example: an NVD upper bound
{
"criteria": "cpe:2.3:a:zlib:zlib:*:*:*:*:*:*:*:*",
"versionEndIncluding": "1.2.12",
"vulnerable": true
}With conda-forge versions 1.2.11, 1.2.12, and 1.2.13, the result is:
| Version | Result |
|---|---|
1.2.11 | affected |
1.2.12 | affected |
1.2.13 | not affected |
The criterion is bounded, so the match is evaluated.
If the CPE used * for the version and had no bounds, the result would be version_unbounded with all three versions.
Deciding applicability
Each match gets one of three applicability values:
| Value | When it is used |
|---|---|
evaluated | At least one known conda-forge version matched an OSV range, explicit version, or bounded NVD criterion. |
unevaluated | The identity joined, but the available OSV version information could not be evaluated. Common examples are GIT-only ranges and invalid range bounds. |
version_unbounded | An NVD CPE matched the vendor and product but provided no usable version bounds. |
If a range is valid and none of the known versions fall inside it, no match is written.
Applicability and identity confidence are separate. A range can be fully evaluated even when the package identity is still automatic or unverified.
What the API returns
The OSV-compatible query endpoints return evaluated matches by default. Add ?confidence=low to include unevaluated and version_unbounded matches.
Each result can include:
- the affected conda-forge versions,
- whether the latest known version is in that set,
- the comparator,
- the matched PURL or CPE,
- identity status,
- matcher name and version.
active_on_latest uses exact membership in the affected-version set. It does not compare or normalize the latest version a second time.
