cve-scan

Match a source's installed packages against known vendor advisories for its detected release, narrow to the CVEs that are genuinely still unpatched (installed_version < fixed_version, using the same family-aware comparator the translation engine uses), and report them. With --sign the verdict is Ed25519-signed via the same /v1/attest/sign path verify uses, so the result is an audit-grade proof rather than a printout.

Never billable — a cve_scan records no metered charge. It is license-gated (VRTMV-437): a scan runs against a VM the account has licensed. On a VM that is not yet licensed, cve-scan now auto-claims a seat when the account has capacity (VRTMV-620), so the scan just works and the host becomes licensed; the seat is a lightweight license, never a metered charge. A 402 is returned only when the account is genuinely out of capacity (free quota, credits and the per-account cap all exhausted) — not as a licensing step. When a scan claims a seat the command says so on stderr.

A claimed seat stays claimed: there is no command that releases it, so scanning a host you never intend to migrate uses capacity you might have wanted for one you do. Seats go to hosts in the order they are scanned, so on a 10-seat account a scan of a larger fleet seats the first ten unlicensed hosts and refuses the rest. To choose which hosts hold seats, license them first. The capacity check happens before the scan and the seat is claimed after it, so hosts scanned at the same time by separate cve-scan processes can all pass the check; the ones past capacity are still scanned, are not seated, and get a usage not recorded note carrying the quota refusal.

Needs credentials — the advisory lookup and any --sign go to the API.

vrtmv cve-scan --root /mnt/snap/web01 --sign -o report/
vrtmv cve-scan --image host.qcow2 --arch x86_64 --json
vrtmv cve-scan --ssh deploy@web01.corp --sudo -o report/
FlagPurpose
--root / --image / --sshExactly one source (see Sources).
--ssh-port, --ssh-key, --jump, --sudoSSH collection options.
--arch <arch>Scope arch-specific advisories (e.g. x86_64). Advisories with no arch recorded always apply.
--signAsk the API to Ed25519-sign the report. Needs credentials.
-o, --out <path>Write the scan report JSON here.
--jsonEmit the report as JSON instead of a text summary.

A fleet: hosts that report one identity

cve-scan-fleet scans the sources of a manifest, or of repeated --root/--image/--ssh, and writes one report for the fleet. Its usage post is what seats an unlicensed host, so it waits until every host is scanned. Hosts that report one identity (see identity) are clones of one image, or one host listed under several names. Each of them is refused: no usage post, so no seat, and its findings are left out of the report. Its row has the state error and a reason that names the hosts and the file the identity came from, and the JSON report lists them under refused_shared_identity. The other hosts are reported and seated as before, and the run exits non-zero. Run vrtmv identity on each clone, or remove the second name from the manifest, and scan again.

A quiet scan is not always a clean one

Zero findings has three meanings, and only one of them is "clean":

  • No advisory data for the release. The lookup joins the advisory tables for the detected release; with no rows there it matches nothing before any version comparison. The report carries advisories_for_release (how many advisory rows the Index holds) and prints advisory data NONE for this release — this scan proves nothing. EOL releases are the likeliest to have no feed and the likeliest to need scanning.
  • End of life. Even with a feed, a release the vendor has stopped patching has a frozen feed. The report carries support_status (supported, eol-no-extension, eol-extension-active, eol-extension-lapsed, or absent) and prints an END OF LIFE banner where a quiet result is unmeasured rather than safe.
  • Genuinely patched. A supported, covered release with an empty findings array.

If you build on --json, treat empty findings as clean only when advisories_for_release > 0 and support_status == "supported". Fail closed — otherwise the least-covered hosts in an estate read as its safest.

To act on the findings, see patch, which bumps exactly the still-vulnerable packages to their vendor-fixed version.