vulnix: a CVE scanner that reads the Nix store instead of a package list
Vulnerability (CVE) scanner for Nix/NixOS [maintainer=@henrirosten]
At a glance
- What is it?
- vulnix walks the derivations reachable from your live paths, matches them against the NVD, and gives you a report plus a way to say, in a TOML file, that a particular finding is already being handled. Its own documentation admits the name matching is crude, which is both its main weakness and the thing to check first.
- Who is it for?
- Adopt vulnix if you run NixOS or build with Nix and need to know which of your store paths are exposed to known advisories, especially if you can add it to CI or a Sensu monitoring integration, because the JSON output and the store-level scan are what a platform team actually needs.
- Can I use it commercially?
- Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 3 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
It scans live paths, not a package list
vulnix is described as a utility that validates a Nix store for any packages that are reachable from live paths and likely to be affected by vulnerabilities listed in the NVD. It implements a CLI utility to inspect the current status, and a monitoring integration for Sensu.
That framing is the interesting part. A conventional vulnerability scan for a Linux system looks at installed packages and compares versions against advisories. vulnix instead looks at the store, and the store is where Nix keeps every derivation, including the ones that were built and then replaced. Asking what is reachable from live paths is asking which derivations are actually in use, which is a more accurate question than asking what was ever built.
The output format in the README looks like a report grouped by derivation, with the store path of each affected derivation, a table of CVE identifiers and their CVSSv3 scores, and links to the NVD detail pages. The example shows two derivations with active advisories, one of them with three CVEs at scores from 7.8 down to 6.5.
The tool pulls all published CVEs from NIST and caches them locally, then matches the name and version of every derivation referenced from the command line against the known entries. A whitelist filters out unwanted results, and the README is explicit about why that matters, since output can contain false positives, packages that cannot be fixed, or issues that are already addressed.
The project is BSD-3-Clause licensed, its releases are 1.12.3, 1.12.4 and 1.12.5 through 2026, and the last push was on 2026-10-20.
The name matching is coarse, and the README admits it
The section on theory of operation explains how a Nix package is lined up with a product in the NVD database, and it is where the tool's main weakness lives.
Matching Nix package names to NVD products is done via a coarse heuristic. A direct match is tried first. If no product is found, variations with lower case and underscore instead of hyphen are tried. Then the README states the conclusion plainly: it is clear that this mapping is too simplistic and needs to be improved in future versions.
That sentence deserves more attention than it usually gets, because it determines how you should read a report. A package that does not appear in the output is not necessarily a package with no known vulnerabilities. It may be a package whose name does not line up with the NVD's product naming, and the fallback only tries one family of variations, lower case and underscore in place of hyphen.
The two failure modes are worth separating. A false positive is a report against a package that is not really affected, and the whitelist exists to absorb that. A false negative is a vulnerable package that never appears, and the whitelist cannot absorb that at all, because there is nothing to exclude. The heuristic only widens the search, it never narrows the risk, so the false negative is the failure that matters for a security tool.
The practical response is to treat the first run as a mapping exercise rather than a verdict. Look at what it reports, and then look at the packages in your store that it did not report, and check the naming yourself. The other option is to use the JSON output and post-process it against a different advisory source, which is one of the reasons the machine-readable mode exists.
Four commands, and a set of system requirements that are not optional
The usage examples in the README cover the four things people actually want to do. Scanning the current system:
vulnix --systemChecking the output of a nix-build together with its transitive closure:
vulnix result/Checking derivations without determining requisites:
vulnix -R /nix/store/*.drvAnd producing JSON for machine post-processing:
vulnix --json /nix/store/my-derivation.drvThe system requirements are the part that catches people out, and they are all consequences of reading the store rather than a database of installed packages.
It depends on the common Nix tools such as nix-store being in the path. It depends on being able to interact with the Nix store database at /nix/var/nix/db, which means it must either run as the same user that owns the store database or have nix-daemon active. That second clause is the one to plan for, because on a multi-user or NixOS system the daemon is the normal case and the scanner inherits whatever access the daemon grants.
It parses the .drv files directly, which is what makes the transitive closure checks possible, and it is tested with Nix 1.10 and later and with the 2.x series.
And it refuses to work without some locale environment settings. The README's advice is to export LANG=C.UTF-8 if you see encoding errors, which is the kind of detail that is invisible until a CI job fails on a machine with a different default locale.
Whitelists are TOML, expire on a date, and can cite a ticket
The whitelist is the feature that makes the tool usable day to day, and it is designed around the way a real security team works.
Whitelists are loaded from local files or from HTTP servers, and the option can be given more than once:
vulnix -w /path/to/whitelist.toml \
-w https://example.org/published-whitelist.tomlThe file format is TOML, with the package to be filtered as a section header followed by per-package options. The selection headers are the package at a specific version, the package regardless of version, and a wildcard that excludes all packages.
The options are where the design earns its keep. The cve option takes a list of CVE identifiers, and the rule stays valid as long as the detected CVEs are a subset of the ones listed, so a new CVE on the same package re-opens the finding automatically. The until option takes a date in the form YYYY-MM-DD and confines the rule's lifetime, so the exclusion stops applying on that date. The issue_url option points at an issue tracker, and comment carries free text, and both are informational only.
The example in the README is the workflow in miniature. You create a ticket on your issue tracker, you estimate the time to get the vulnerable package fixed, and you write the whitelist entry:
["ffmpeg-3.4.2"]
cve = ["CVE-2018-6912", "CVE-2018-7557"]
until = "2018-05-01"
issue_url = "https://issues.example.com/29952"
comment = "need to backport patch"That particular version of ffmpeg is then left out of the reports until either another CVE is published or the specified date is reached. The date is the safety net: an exclusion with a deadline cannot quietly hide a finding forever.
Patch auto-detection removes the findings you already fixed
The other half of the noise reduction is built into the scanner itself, and it is the cleverest thing in the repository.
vulnix inspects derivations for patches that supposedly fix specific CVEs. When a patch filename contains one or more CVE identifiers, those CVEs are not reported any more. In Nix terms, a derivation with a patch called CVE-2018-6951.patch is treated as having addressed that advisory.
Patches that fix multiple CVEs should name them all with a non-numeric separator, and the README gives the example of a file named CVE-2017-14159+CVE-2017-17740.patch, so the scanner can tell two identifiers apart.
The detection also works when patches are pulled in through fetchpatch and similar mechanisms, as long as the name contains a CVE identifier. That is what makes the feature usable in practice, because most real derivations do not carry a patch file in the repository but fetch one by URL with a hash.
There is an obvious assumption here, and the README states it: the filename is treated as evidence that the vulnerability is fixed. A patch named for a CVE that does not actually fix it will silence the finding, and a patch that fixes a CVE without saying so in its name will not. The convention is cheap to follow and it is the kind of thing a platform team can enforce in a shared derivation template, which is probably why it works as well as it does.
A pip package, a Nix flake and a Sensu integration
The packaging story is the last piece, and it explains how the tool is meant to be deployed.
The setup file describes it as scanning a Nix store for derivations that are affected by vulnerabilities, and it registers a console script named vulnix. The Python requirement is 3.11 or newer, and the runtime dependencies are click for the command line, requests for the CVE feed, toml for the whitelist files, pyyaml, and ZODB with zodbpickle, which is the object database used for the local cache of published CVEs. The test extra adds pytest, pytest-cov, freezegun for time-dependent tests and setuptools_scm, and the version number is read from a VERSION file at the root rather than hard coded.
The classifiers place it firmly in the operations category. The development status is production and stable, the environment is console, the intended audience is system administrators, and the topic is systems administration, with Python 3.11 through 3.13 supported.
There is a Nix side as well. The repository has a default.nix, a shell.nix, a flake.nix and a flake.lock, so the scanner can be built and run inside Nix itself, which is the only sensible way to run it against a Nix store in some setups.
And there is the monitoring integration for Sensu, which is the piece that turns a command line tool into something a platform team deploys. The JSON output mode exists for exactly that kind of machine post-processing, and a scanner wired into a monitoring system is a scanner people will actually run on a schedule.
The author is listed as Flying Circus Internet Operations, a German hosting provider, which tells you something about the environment the tool was written for, and the repository listing records the licence as BSD-3-Clause.
Editorial conclusion
Adopt vulnix if you run NixOS or build with Nix and need to know which of your store paths are exposed to known advisories, especially if you can add it to CI or a Sensu monitoring integration, because the JSON output and the store-level scan are what a platform team actually needs. Do not adopt it as your only source of truth, because the package name to NVD product matching is a coarse heuristic that the README itself calls too simplistic, so a clean report can mean an unmatched name rather than a safe system. Before you rely on it, check the store permissions and locale requirements on the machine that will run it, run it against a store where you know the answer, and review the whitelist rules in TOML, because a rule with a cve list stops applying as soon as an extra CVE appears.
Frequently asked questions
What does vulnix check?
It validates a Nix store for packages that are reachable from live paths and are likely to be affected by vulnerabilities listed in the NVD. It pulls published CVEs from NIST, caches them locally, and matches the name and version of the derivations referenced from the command line against those entries.
How do I run vulnix against my system?
The README gives vulnix --system for the current system, vulnix result/ to check a nix-build output with its transitive closure, vulnix -R /nix/store/*.drv to check derivations without determining requisites, and vulnix --json for machine-readable output.
What are the system requirements for vulnix?
It needs the common Nix tools such as nix-store on the path, and it needs to interact with the Nix store database at /nix/var/nix/db, so it must run as the user that owns the store database or have nix-daemon active. It parses .drv files directly, is tested with Nix 1.10 and later and 2.x, and needs locale settings, with the README suggesting LANG=C.UTF-8 if you hit encoding errors.
How do whitelists work in vulnix?
They are TOML files, loaded from local paths or HTTP servers with the -w option, with the package as a section header. The cve option lists identifiers and the rule holds only while the detected CVEs are a subset of them, the until option gives a date on which the rule stops applying, and issue_url and comment are informational.
Does vulnix detect patches that already fix a CVE?
Yes. It inspects derivations for patches that supposedly fix specific CVEs, and when a patch filename contains one or more CVE identifiers those are not reported. Patches fixing several CVEs should name them all with a non-numeric separator, and the detection also works for patches pulled in through fetchpatch.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nix-community-vulnix)Was this useful?