Introducing Vulnerability Monitoring in osctrl

A mint-glass scanning ring surrounds connected computers with amber vulnerability alerts and a central module bearing the osctrl logo.

osctrl v0.6.0 ships a new capability: vulnerability monitoring. The software inventory collected from every enrolled node is matched continuously against OSV advisories, the CISA Known Exploited Vulnerabilities (KEV) catalog and, optionally, NVD CVEs. The findings show up in the frontend, in alert notifications and in the node posture score.

Why vulnerability management is a cornerstone for compliance

Most security and compliance frameworks require the same things: know what is installed on every device you operate, know which devices are exposed to known vulnerabilities, verify it continuously and be able to prove it.

  • ISO/IEC 27001:2022 covers it in A.8.8, Management of technical vulnerabilities,

  • PCI DSS devotes its Requirement 11.3 to identifying and managing vulnerabilities,

  • the CIS Controls name Control 7 Continuous Vulnerability Management,

  • and SOC 2 auditors ask for the same evidence: inventory, correlation and timely response.

Vulnerability management is a cornerstone for compliance because everything downstream depends on it: patch priorities, risk scoring, incident response and the audit itself. A stale inventory or a quarterly scan cannot support any of that, because the fleet changes every day. Devices join and leave, packages get updated and new CVEs get published, so the only workable approach is continuous.

osquery already collects the inventory from every device continuously. The new feature closes the remaining part: the correlation with advisory data, the alerting and the scoring.

How it works

The design follows the same principles as the rest of osctrl: modular components, security in mind and off by default.

Inventory collection

Applying one of the new inventory profiles, vuln-linux, vuln-darwin or vuln-windows, to an environment's schedule makes every node report its installed packages daily, in snapshot mode, through the same enrolled connection it already uses. There is no second agent, no extra credentials and no scan traffic.

Each snapshot fully replaces the previous inventory for the node. A snapshot that would exceed 50,000 rows is rejected rather than truncated, because a truncated list would look like a clean fleet, and a false "not vulnerable" result is worse than no result.

Advisory feeds and matching

A background worker in osctrl-api downloads the OSV ecosystems the fleet actually reports, plus the KEV catalog and, optionally, NVD CVEs. Syncs are incremental after the first, and withdrawn advisories are removed. Version comparison uses the comparators published by OSV (dpkg, rpm, PEP 440 and semver), and severity is scored locally from the CVSS vectors included in each record.

Matching follows the conventions of each ecosystem: Debian and Ubuntu are matched by source package, so libssl3 matches openssl advisories. RPM versions include the epoch, PyPI names are PEP 503-normalized, and with NVD enabled, Windows programs, macOS apps, Homebrew and Chocolatey packages are matched by product name. These NVD-based matches stay as "possible" findings, and they never trigger alerts or affect scores.

Findings lifecycle

A finding that no longer matches gets resolved instead of deleted, keeping its history. If it matches again, it reopens with its original first_seen. Resolved findings are purged after a configurable retention window.

One design rule matters more than the rest: unassessed is never reported as clean. Before the advisory feeds have loaded, for a node that has not reported inventory yet, for an ecosystem whose feed has not synced, or for a distribution that is not covered, the results are counted and shown as not assessed, in the environment summary, the node views and the posture score. The API responses carry loaded and stale flags for exactly this reason. A false "no vulnerabilities" result is worse than a warning, so the UI never shows one.

Keeping the fleet up to date

Vulnerability monitoring pays off when it drives patching. The loop between inventory, findings and patch state is where keeping devices up to date becomes measurable:

  • Daily inventory refresh: the moment a device checks in with new packages, after a patch, an upgrade or a rollback, its findings are recomputed. A missed patch shows up at the next check-in, not at the next quarterly scan.

  • Fixed versions are explicit: every finding shows the installed version against the fixed version, or "No fix yet", which is its own actionable signal when there is no patch to apply.

  • Patch, and the finding closes: the next inventory snapshot resolves the finding. Over time this produces the evidence auditors ask for. Vulnerabilities identified, correlated with the actual fleet, remediated and verified as remediated, with timestamps.

  • Known exploited comes first: KEV findings are flagged everywhere and are the priority targets for anything with a patch available. When a finding later lands in KEV or rises in severity, osctrl records the escalation and alerts again, so CISA adding a CVE to the catalog brings the affected devices back to the top of the queue.

Alerting

The new vuln_finding alert rule source plugs into the existing alerting system, with the usual environment and node scoping, cooldowns and notification channels (webhook and email):

Threshold (vuln_min_severity)Matches
kevKnown-exploited findings only
criticalCritical findings, plus all known-exploited
highHigh and above, plus all known-exploited
mediumMedium and above, plus all known-exploited
lowLow and above, plus all known-exploited
anyEverything, including unknown severity

Notification volume stays bounded. Findings are grouped per advisory, so one CVE affecting two hundred nodes sends one notification, and each rule is capped at ten advisories per environment per hour, worst first, plus an hourly digest for the rest. Escalations, a known-exploited jump or a severity raise, have their own budget, so they cannot crowd out new findings, and they never repeat an alert after a feed glitch restores an old state.

The posture score

With the security posture capabilities enabled, nodes now get a "Known vulnerabilities" control, mapped to ISO 27001 A.8.8 and weighted into the fleet risk score:

Open confirmed findingsResult
Any KEV or criticalFail, at critical weight
Otherwise any highFail, at high weight
Otherwise medium, low or unknownWarn
None, with some packages not assessedWarn
None, everything assessedPass

For compliance reporting this is the practical part: the same risk score your organization reports on for SOC 2 or ISO 27001 now includes known-exploited exposure from every device in the fleet, continuously and per node. The health page also gets an "Advisory feeds" item, so a degraded or stale feed is visible infrastructure trouble and never looks like a clean fleet.

The frontend

Everything is reachable from the UI:

  • An Environment → Vulnerabilities page with summary cards for open findings, known exploited, affected nodes and not assessed, plus severity filters, a KEV-only filter, an open/resolved state filter, a paged findings table, and an advisory dialog with the affected nodes in the environment.

  • A Vulnerabilities tab on every node page, showing that node's open findings and how many of its packages could not be assessed.

  • A feeds card for super admins with per-source health and a Sync now button, and a picker in the schedule view to merge the inventory profiles into any environment in a couple of clicks.

Untrusted feed content is handled the way security tooling should handle it. Advisory text renders as plain text, only https: references are clickable, and the frontend test suite covers it.

Getting started

The feature is off by default, like the console and the file explorer. Enable it with --vuln-enabled on both osctrl-tls and osctrl-api (environment variable SERVICE_VULN_ENABLED), then merge the inventory profiles into an environment's schedule from the UI. The optional flags worth knowing:

FlagPurposeDefault
--vuln-sync-hoursFeed sync interval6
--vuln-retention-daysHow long resolved findings are kept90
--vuln-max-download-mbCap per feed download2048
--vuln-ecosystemsExplicit OSV ecosystems listOnly what the fleet reports
--vuln-nvd-enabledNVD CPE matching for appsOff
--vuln-osv-url, --vuln-kev-urlFeed sources, mirrorablePublic OSV / CISA

Two practical notes. The inventory never leaves your deployment: the only outbound traffic is the feed downloads, to URLs you control, so air-gapped sites can point those URLs at an internal mirror and run the whole capability on premises. And if you turn on NVD matching, the first full sync takes about an hour, while later syncs are small.

The complete reference, including the data flow, the coverage matrix and the metrics, is in pkg/vulns/README.md.

Conclusion

Vulnerability monitoring answers, against your own deployment, the questions that used to require an external scanner or a stale inventory: which hosts are exposed to CVE-X, which devices still run packages with no fix available, and what changed in the fleet. It is continuous, it keeps the data in-house and it feeds the same score that compliance reporting uses.

If you have any questions about vulnerability monitoring or osctrl in general, feel free to create an issue in GitHub, check the documentation, or reach out in the #osctrl channel in the official osquery Slack community (request an auto-invite!).