Most vulnerability advice is useless in practice: subscribe to feeds, read announcements, patch quickly. Nobody has time to map thousands of CVEs against their own package list by hand. The useful question is narrower. Not "what vulnerabilities exist" but "which vulnerabilities affect the exact package versions installed on my servers, right now."
Start with what you actually have
# Debian/Ubuntu
dpkg-query -W -f='${Package} ${Version}\n'
# RHEL family
rpm -qa --qf '%{NAME} %{VERSION}-%{RELEASE}\n'
# Alpine
apk list --installed
That inventory is the foundation. Without it you are guessing.
Query against a vulnerability database
OSV.dev aggregates vulnerability data across ecosystems, including distro-specific advisories, and exposes a free API. You can query a single package and version:
curl -s -X POST https://api.osv.dev/v1/query \
-d '{"package":{"name":"jq","ecosystem":"Ubuntu:24.04"},"version":"1.7.1-3ubuntu0.24.04.2"}'
The ecosystem string matters. Ubuntu:24.04 and Debian:12 return different results for the same upstream package, because distros backport fixes on their own schedule.
The nuance people miss
A CVE with no vendor fix is not actionable yet. Plenty of medium-severity issues sit unpatched because the distro has not shipped a fixed version. Tracking them is useful. Panicking about them is not.
Backports break naive version comparison. Ubuntu's 1.7.1-3ubuntu0.24.04.2 may already contain the fix that upstream shipped in a later version number. Compare against distro advisories, not upstream releases.
Severity labels vary by source. A vendor's "low" and a CVSS score can disagree. Use whichever your compliance framework expects, and be consistent.
Making it continuous
A one-off scan is a snapshot. Similar to file integrity monitoring, a single check only tells you what's true right now - the value is in tracking it over time. Packages change with every update, and new CVEs are published daily against versions you already run. The scan needs to repeat and the results need to be diffable - what is new since last week, what has been fixed, what is still waiting on a vendor. It's the same constraint that decides whether alert triage scales or drowns a team: state has to persist between runs, not get rebuilt from a fresh list every time. SecAI runs this loop automatically: it inventories installed packages per server, queries OSV on a schedule, and tracks each finding through to resolution rather than re-reporting the same list forever. See it alongside the rest of the detection stack.