AIROM’s core scan is offline and deterministic: it reports what an artifact is. The CVE overlay adds what is known about it today — it matches the AI package dependencies AIROM already inventoried (by their purl) against the live OSV.dev advisory database and attaches the resulting CVEs, with real CVSS v3 base scores computed from each vector, to those components.
On by default — disable with --no-cve or --offline. The overlay touches the network, and it is not deterministic across time: the same scan surfaces more CVEs next month as OSV grows. That is right for a vulnerability check and wrong for a reproducible bill of materials, so turn it off (--no-cve) when you need a byte-stable BOM, or --offline to also assert no network for the whole run.
Scope: AI dependencies, not a general-purpose SCA. The overlay queries only the components AIROM inventories — the AI/ML frameworks, SDKs, and serving libraries it already identifies (pypi, npm, golang, cargo, maven, nuget purls). It answers “do the AI parts of my stack have known CVEs?”, not “audit my entire dependency tree.” Use a dedicated SCA for the latter.

Usage

The overlay composes with everything else — a --compliance control that maps to “known vulnerabilities” sees the CVEs (it runs before compliance), and every output format projects them.

How CVEs appear in output

Each CVE becomes a top-level vulnerabilities[] entry with the CVE id, source.name: osv.dev, a ratings[] entry with method: CVSSv31 carrying the real score, severity, and vector, aliases as references[], and affects[].ref pointing at the component’s bom-ref. The first fixed version rides in an airom:cve.fixedVersion property.
CycloneDX with CVEs in vulnerabilities[]

Severity and the --fail-on threshold

Each CVE’s severity comes from its CVSS v3.x vector — AIROM computes the base score per the FIRST formula and buckets it into the standard bands: cve:<severity> in --fail-on is a threshold, not an exact match:

Honesty and degradation

Absence of CVEs is not a safety claim. It means “OSV had no advisory for these exact package versions at scan time,” not “this dependency is safe.” Advisories are published continuously — re-run to re-check.
  • A declared range is not checked at all. An advisory range is evaluated against a release, so there is no answer to “is ^4.20.0 vulnerable” that is not really a claim about some specific version. Components carrying only a declared range are skipped and counted in a warning rather than reported as clean — matching the range’s lower bound would invent advisories already patched while hiding ones introduced later in the range. Commit a lockfile, or scan a deployed environment, to resolve them.
  • A network failure is never fatal — with one deliberate exception. If OSV is unreachable, the affected component simply carries no CVEs and a warning lands in the scan’s Stats.Warnings (visible under --stats); the AIBOM is never held hostage to a third-party API’s uptime. But when a CVE gate is active (--fail-on cve…) and a component could not be checked, the scan fails closed (exit 2) instead of reporting a false “clean” — a gate that silently passes during an outage is worse than no gate.
  • Deduplication. OSV often returns several records (a GHSA and a PYSEC, say) aliasing one CVE. AIROM collapses them to a single entry per id — keeping the most severe rating, preferring a real fixed version over a commit hash, and unioning the aliases — so you see one row per CVE, not one per advisory database.