Skip to content

Security advisories

Paul Asadoorian edited this page Aug 10, 2026 · 1 revision

Security advisories / CVE tracking — advisory-check (opt-in)

Two different things are checked, and the report says which is which. Installed system packages are matched against your distro's security tracker. Separately, the Python and Node environments the distro does not manage — project venvs, uv/pipx apps, pip install --user — are matched against OSV. That second half means fettle walks your filesystem: [advisories] venv_roots (default ["~/src"]) and venv_depth (default 5) bound the search, and every run prints what it looked at:

What was checked:
  arch   installed system packages, matched against the Arch Linux security tracker
  osv    49 Python environment(s) found on disk — a walk of ~/src (depth 5) plus
         uv/pipx apps and pip --user; environments the distro does NOT manage

In the HTML report, a finding that spans several environments expands to show them, oldest version first — because within one finding the installed versions differ a lot (pip sat at 11 distinct versions across 44 venvs), and which ones are furthest behind is what turns a count into a work queue. Plain lines, so a drag-select copies clean paths:

[-] 44 environments
    23.2.1       /home/paulda/src/ALEAPP/venv
    23.2.1       /home/paulda/src/CVE-Half-Day-Watcher/venv
    24.0         /home/paulda/src/CVE-2022-31814/venv
    …
    26.1.1       /home/paulda/src/bifrost/.venv

The terminal report lists them by a short label and resolves those to full paths at the end (and in the JSON), because "jetkvm is vulnerable" is not actionable until you know which jetkvm:

Environments (46) — the short names above, in full:
  ALEAPP     /home/paulda/src/ALEAPP/venv
  jetkvm     /home/paulda/src/jetkvm/venv

Exit status: 1 when something is Critical and a fix is already released — the one case that should stop an automated run. Everything else outstanding is a warning and exits 0, as is a run whose feed data could not be refreshed (reported as such, never as a clean bill of health).

fettle advisory-check (Arch/Manjaro, Debian, and Ubuntu for OS packages, plus Python/Node packages via OSV on any distro) tells you, per installed package: CVEs with a fix you haven't applied yet, and — the distinctive part — CVEs you're currently vulnerable to with no fix released yet (a heads-up before an advisory/patch exists). It bulk-fetches the distro's security tracker into a rebuildable SQLite cache (~/.cache/fettle/advisories.db; sqlite3 is stdlib, so the core stays dependency-free), refreshed on-run when stale or via fettle advisory-update.

fettle advisory-update       # refresh the local advisory cache
fettle advisory-check        # report pending + fix-available CVEs (read-only)

The report leads with a Pending fixes callout (vulnerable, no fix yet), then a severity-banded Fix available table, then the packages the tracker doesn't cover (AUR/manual/foreign) so a clean result never over-reassures. Version comparison is delegated to vercmp (Arch) / dpkg --compare-versions (Debian). It's opt-in (never in the default -a set). On fettle -u/-a it prints a best-effort security note before the upgrade — how many packages are known-vulnerable, how many Critical, and which Criticals have no fix released (the only ones an upgrade cannot address). It never blocks the update: an unpatched CVE is a pre-existing condition that refusing to upgrade does not fix, and for anything with a fix released the upgrade is the remedy. It reads only the cached database, so a network problem can never delay an upgrade either.

[advisories] config: cache_ttl, severity_threshold, exclude_packages (globs), exclude_classes (hide distro class tags, e.g. Debian ["nodsa","unimportant", "end-of-life"]), ubuntu_pending / ubuntu_pending_severity (opt-in Ubuntu "no fix yet", below), and venv_roots / venv_depth (where to hunt for virtualenvs, below). On Manjaro, "fix available" is phrased as possible sync lag, not alarm.

Language coverage is deliberately limited to what your distro does not manage — virtualenvs, uv tools, pipx apps, per-user (pip install --user) installs, bun/nvm Node trees, and cargo installed Rust crates (read from cargo's own install index, since a crate's binaries need not share its name). A crate built from a path/git checkout rather than the registry is labelled cargo(path) / cargo(git), because its version need not be the published release of that name. Distro-packaged modules (python-requests and friends) belong to your distro's tracker, which knows about backported fixes; judging them by PyPI version numbers instead produces both false alarms and duplicate findings. Each result is labelled by environment (SploitScan:requests), because the same vulnerable package in three virtualenvs is three things to fix. Virtualenv discovery is bounded by venv_roots (default ["~/src"]) and venv_depth (default 5) — an unbounded $HOME walk is far too slow to run on every check.

Debian's tracker dump is large (~80 MB); a refresh downloads and parses it once per cache_ttl. Coverage is by source package; third-party/local .debs aren't separately flagged yet. On Ubuntu, fix-available findings come from the per-release OVAL feed (with Canonical's priority); "vulnerable, no fix yet" (pending) is opt-in via [advisories] ubuntu_pending = true + ubuntu_pending_severity (high by default), sourced from OSV — a real box carries ~1300 pending Ubuntu CVEs, so the severity floor keeps it to a few actionable items. Python/Node packages are checked against OSV on any distro (system-wide pip/npm ls -g).

Clone this wiki locally