patch

Render an Ansible role that bumps exactly the packages a cve-scan found still vulnerable to their vendor-fixed version, staying on the same release — no cross-distro translation. It reuses the scan/narrow path directly, then groups findings by package: when one package has several CVEs with different fixed versions, it is bumped to the highest one so a single task clears them all. With --apply it also executes the role over SSH against the same host and re-scans to confirm.

Needs credentials — the advisory lookup and any --sign go to the API. Like cve-scan, the underlying scan is license-gated and never metered; patch itself adds no billable charge. rpm/dpkg only — Portage has no reliable exact-version pin via ansible.builtin.package, so patch refuses rather than emit a task that might not pin the version it claims.

vrtmv patch --root /mnt/snap/web01 --all-fixable --sign -o patch/
vrtmv patch --root /mnt/snap/web01 --cve CVE-2026-1234 --cve CVE-2026-5678 -o patch/
vrtmv patch --ssh deploy@web01.corp --all-fixable --apply -o patch/
FlagPurpose
--root / --image / --sshExactly one source (see Sources). --apply requires --ssh.
--ssh-port, --ssh-key, --jump, --sudoSSH collection options.
--cve <id>Patch only this CVE (repeatable). Mutually exclusive with --all-fixable.
--all-fixablePatch every CVE the scan finds still unfixed.
--arch <arch>Scope arch-specific advisories (e.g. x86_64).
--signAsk the API to Ed25519-sign the patch plan. Needs credentials.
--applyExecute the role over SSH against the same host, then re-scan to confirm. Requires --ssh.
-o, --out <path>Output directory for the role + report (default vrtmv-patch).
--jsonEmit the plan as JSON instead of a text summary.

A requested --cve that matches no current finding — not applicable to the release, not installed, or already fixed — is reported in unmatched_cves, never silently dropped.

--apply

Runs the patch immediately over SSH against the exact host it was scanned from (the same ansible-playbook invocation run uses), then re-scans fresh and diffs against what was targeted. It never claims a fix worked because ansible-playbook exited 0 — it re-checks, and its exit code reflects the confirmed outcome: it fails if the playbook reported failures or if the re-scan still finds any targeted CVE.

Three things --apply does that render-only does not:

  • Requires --ssh. A mounted --root/--image is a static filesystem, not a running system to execute against, and refuses --apply.
  • Excludes epoch-ambiguous packages from what it auto-applies (a non-zero-epoch rpm's pinned spec is a documented best-effort gap). These are named in excluded_epoch_ambiguous and left for manual patching.
  • Always renders a rollback role alongside (roles/vrtmv_patch_rollback/), targeting the pre-patch version. It is not run automatically — it is there so a bad patch has an immediate undo. Rolling back reintroduces the CVEs the forward patch fixed; it is not itself a security action.

The executed role also appends a non-blocking, reported restart-recommendation check (needs-restarting -r on rpm, /var/run/reboot-required on Debian/Ubuntu): a package can be upgraded on disk while the running process keeps the old code loaded, and the package database cannot see that.