Malicious packages are not vulnerabilities: why your CVE scanner can pass a package that is malware by design
A vulnerability scanner can pass a malicious package because malware and vulnerabilities are tracked in different datasets. A vulnerability is a bug with a CVE and a fixed version. A malicious package is code written to cause harm, and it usually gets a malware advisory with no CVE and no safe version. Whether your scanner sees it depends on which advisory feed it reads. In a test run on 15 October 2026, npm audit flagged three of four known-malicious npm versions. The fourth is listed in the OSV database but not in GitHub's advisory database, so npm audit passed it.
The question you are actually asking
You ran a dependency scan. It came back clean, or with a few old CVEs. Your agent installs packages on its own all day. You want to know: does "clean" mean "not malware"?
In plain English, no. "Clean" means "no advisory in the dataset this tool reads matched a package in your lockfile." Whether that dataset contains malware entries, and how quickly, is a separate question with a different answer for each tool.
Two kinds of bad package
Think of a vulnerability as a lock that turns out to be pickable. The lock maker ships a better lock, you replace yours, and the problem is gone. That is why a CVE advisory comes with a fixed version.
A malicious package is a lock built with a hidden key for someone else. There is no better version of it to upgrade to. GitHub's documentation puts it this way: "Malware alerts do not contain a fix version, because the defining characteristic of a malware alert is that there are no safe versions" (GitHub Docs).
The two kinds are recorded in different places:
- Vulnerabilities get CVE IDs and appear in the National Vulnerability Database, in GitHub's reviewed advisories, and in OSV.
- Malicious packages get malware advisories. The OpenSSF Malicious Packages project keeps an open database of reports in OSV format, covering typosquatting, dependency confusion and account takeover, and lists "vulnerability reports" as out of scope (OpenSSF malicious-packages). On osv.dev these entries carry
MAL-identifiers. GitHub publishes its own malware advisories, sourced from the npm security team and the OpenSSF repository.
The axios malware advisory, GHSA-fw8c-xr5c-95f9, lists no CVE ID. A tool that matches only CVE identifiers has nothing to match.
The scale
Sonatype counted 17,954 new open source malware packages in the first quarter of 2025 alone, bringing the total in its index to 828,925 (Sonatype, 2 April 2025). That is a vendor index with its own detection methods, so treat it as an order of magnitude. The point is that this is a large, separate stream of data, not a corner case inside the CVE feed.
A worked example you can rerun
On 15 October 2026 we ran npm audit --package-lock-only (npm 10.9.4) against a lockfile that listed five packages. The --package-lock-only flag makes npm read the lockfile without installing anything, so no malicious code was downloaded or run. Four entries were versions with public malware reports. One was a control with ordinary CVEs.
Package@version Public malware record npm audit result
------------------------------- ------------------------------------ ---------------------------------------------
[email protected] GHSA-fw8c-xr5c-95f9 (malware) Flagged critical: "Malware in axios"
[email protected] GHSA-2x9r-6wxq-hrr7 (malware) Flagged critical: "Malware in plain-crypto-js"
[email protected] MAL-2026-5182 (OSV) Flagged critical via GHSA-56pv-xqqh-57cf
[email protected] MAL-2025-47604 (OSV only) Not flagged
[email protected] (control) none; CVE advisories only Flagged high: command injection and others
The miss is the interesting row. [email protected] is the version that added a hidden BCC to every email an agent sent through it (The Hacker News, 29 September 2025). OSV lists it as MAL-2025-47604, imported from the OpenSSF repository on 26 September 2025. A search of GitHub's advisory database for it, including with the type:malware filter, returned zero advisories on 15 October 2026. npm audit reads GitHub's data, so it passed the package.
Two lessons come out of five rows. First, npm audit does read malware advisories today, which many developers do not expect. Second, its coverage is exactly as complete as GitHub's malware feed, and that feed and OSV do not contain the same entries.
The mechanism, layer by layer
Every dependency scanner does the same three things:
- Read your lockfile to get a list of package names and versions.
- Look each one up in one or more advisory datasets.
- Report matches, often filtered by type or severity.
The malware question lives in steps 2 and 3. A tool can miss malware because its dataset has no malware entries, because the entry exists in a different dataset, or because malware alerts are switched off. That third case is common. GitHub's Dependabot malware alerts for npm launched on 17 March 2026 as opt-in (GitHub changelog), and coverage expanded to more ecosystems on 28 July 2026, drawing on the OpenSSF repository; you turn them on under the repository or organisation's Dependabot settings (GitHub changelog).
Where this simple picture stops being true
Timing. Advisories arrive after discovery. The axios malware advisory was published at 03:15:49 UTC on 31 March 2026, according to OSV's record, almost three hours after the first malicious version appeared. A scanner that reads the right feed still cannot flag what has not been reported yet.
False positives. GitHub notes that its malware advisories are "mostly about substitution attacks," and a private package that shares a name with a malicious public one can trigger an alert.
Things that are not packages. Agent skills and remote MCP endpoints do not appear in your lockfile at all. A lockfile scanner, however good its feeds, never sees them.
What to change in CI and in agent hooks
The fix is not a new scanner. It is making sure the checks you already run read malware data, and run at the moment an agent installs something.
In repositories, turn on Dependabot malware alerts if you use GitHub; they are separate from CVE-based alerts and off until enabled. If you use another SCA tool, ask its vendor in writing which malware feeds it reads and how fast new entries arrive. "We detect malicious packages" is not an answer; "we ingest OpenSSF MAL entries within N hours" is.
In CI, keep npm audit or its equivalent, but add a lookup against OSV, which includes the OpenSSF MAL- entries that the postmark-mcp row shows GitHub's feed can lack. Fail the build on any malware match regardless of severity settings, since a malware advisory has no "low" version.
At the agent, the check has to run before the install, not after the pull request. An agent that runs npm install at 2 a.m. has executed any install script long before CI sees the lockfile. A PreToolUse hook or package-manager wrapper that checks the requested name and version against a local known-bad list closes that gap.
For skills and MCP servers, keep an inventory, because no lockfile scanner will build one for you.
Your scanner capability check
Question How to check
----------------------------------------------------------- -----------------------------------------------
1. Does the tool read malware advisories, or only CVEs? Vendor docs; then test with a known MAL version
2. Which malware feeds: GHSA malware, OSV MAL, vendor's? Vendor docs; compare with the table above
3. Are malware alerts switched on? Dependabot settings: "Malware alerts"
4. Does it check at install time or only in CI? Agent installs happen before CI runs
5. Does it see skills and MCP server configs? Point it at a skill folder or .mcp.json
To test question 1 safely, build a lockfile by hand that lists known-malicious versions and run the scanner in lockfile-only mode. Never run npm install on it. The four malicious rows in the worked example are a usable starting set.
If you want a list that covers skills and MCP servers as well as packages, the threat.cafe board tracks all three, and its list is being built as open OSV-format data under CC-BY.
Sources
- GitHub Advisory database — GitHub Docs, accessed 15 October 2026
- malicious-packages — OpenSSF, via pkg.go.dev, accessed 15 October 2026
- Malware in axios (GHSA-fw8c-xr5c-95f9) — GitHub Advisory Database, 31 March 2026
- GHSA-fw8c-xr5c-95f9 — OSV, accessed 15 October 2026
- MAL-2025-47604: Malicious code in postmark-mcp (npm) — OSV, 26 September 2025
- MAL-2026-5182: Malicious code in brave-search-mcp-server (npm) — OSV, 3 June 2026
- First Malicious MCP Server Found Stealing Emails in Rogue Postmark-MCP Package — The Hacker News, 29 September 2025
- Nearly 18,000 New Malicious Packages Discovered in Q1 According to Sonatype Open Source Malware Index — Sonatype, 2 April 2025
- Dependabot now detects malware in npm dependencies — GitHub changelog, 17 March 2026
- Dependabot alerts on malicious packages across more ecosystems — GitHub changelog, 28 July 2026