npm download counts are not a package trust signal

Millions of weekly downloads still read as legitimacy on npm. That habit is now an active deception surface.
On 21 September 2026, JFrog Security Research published Equation of Compromise, a campaign teardown that starts with a mathjs lookalike called mathsbase and ends with a durable registry smell: packages that rack up multi-million download totals while having zero reverse dependents. The same week, press coverage of indexed-btree still led with volume. SecurityWeek's 22 September 2026 headline framed the story as a malicious B-tree package that accumulated millions of downloads. Checkmarx's earlier write-up on the btree cluster made the same popularity point while documenting a different trigger path (runtime prototype hooks, not install scripts).
The useful question is not "how popular was the malware?" It is who controlled the popularity metric.
What happened#
JFrog's investigation ties a cluster of npm packages to a longer-running operation: encrypted loaders keyed on developer-specific math workloads, dual command channels (Slack and Ethereum Sepolia contracts), and disposable publisher accounts. That implant story matters. For install-time defence, the download factory is the part that breaks a common heuristic.
JFrog recovered public GitHub Actions farms (worker1 through worker10, plus parallel job_worker* and job-worker* trees) that decrypt a target list, resolve each package tarball URL, and request that tarball thousands to tens of millions of times. The bytes are discarded. npm's download counter still increments. The repositories ship an identical WASM decoder and encrypted commands.json schedules. JFrog reports scheduled download totals for campaign packages in the tens of millions for names such as events-sync, indexed-btree, and btree-core.
Separately, Checkmarx and SecurityWeek describe indexed-btree as a sorted-btree lookalike that hides its first stage inside BTree.prototype.set, so install-script blocking does not see a hook. Those reports treat the multi-million download figure as evidence of reach. JFrog's farm evidence reframes that figure: reach on the counter is not the same as reach through dependency graphs.
What the registry and Attestd actually show#
Two independent observations sit on top of the same packages.
Registry telemetry (verified 23 September 2026). npm's public download API still reported about 2.13 million downloads for indexed-btree and about 2.59 million for mathsbase in the week of 15 to 21 September 2026. Those are count events, not proof that any application depends on the package.
Compromise condition (live Attestd GET /v1/check, same day). Attestd does not treat download volume as a safety input. For the versions named in public research, the independent condition is supply_chain.compromised:
product: mathsbase
version: 1.0.1
risk_state: critical
cve_ids: []
risk_factors: ["supply_chain_compromised"]
supply_chain.compromised: true
supply_chain.sources: ["osv"]
supply_chain.compromised_at: 2026-09-21T20:06:13Z
product: indexed-btree
version: 2.1.2
risk_state: critical
cve_ids: []
risk_factors: ["supply_chain_compromised"]
supply_chain.compromised: true
supply_chain.sources: ["osv"]
supply_chain.compromised_at: 2026-09-03T10:14:01Z
Empty cve_ids with supply_chain.compromised: true is the same shape already covered in supply chain compromise vs CVE: vulnerability state and malicious-publish state are different questions. A popularity gate would have said "this looks established." The compromise field says "do not install this version."
Request shape (authorization required):
curl "https://api.attestd.io/v1/check?product=mathsbase&version=1.0.1&ecosystem=npm" \
-H "Authorization: Bearer $ATTESTD_API_KEY"
Treat the field values above as live-verified on 23 September 2026. They are not a re-statement of npm download charts.
JFrog's defender note matches the graph-side smell: over a million downloads with zero dependent packages and zero dependent repositories held for every vehicle they listed. npm's dependencies: search qualifier is not a reliable reverse-dependency check (JFrog notes it silently degrades). Use an actual dependents index if you need that signal.
Why the CVE and popularity model fails here#
Most automation still collapses "should we install this?" into one of two shortcuts:
- CVE / severity gates. No advisory ID means proceed. Equation of Compromise packages are malware publishes, not CVEs against an honest library. Attestd's empty
cve_idson[email protected]and[email protected]is expected under that model, and still unsafe. - Popularity thresholds. High weekly downloads mean "real project," so scanners deprioritise the long tail. Download farms invert that assumption. The adversary can manufacture the threshold that your watchlist uses to decide what is worth inspecting.
Those shortcuts fail for different reasons than the other integrity myths already on this blog.
Provenance and trusted publishing can be valid on a poisoned build that a legitimate workflow really ran. That is build-identity honesty without package safety.
Lifecycle-script blocking can leave packages that look "clean" at install time while payload execution waits for import or a prototype method, which is the btree trigger class Checkmarx documented. That is install-time honesty without runtime safety.
Download inflation is a third deception layer: telemetry honesty without graph honesty. The counter moves because HTTP GET succeeded. It does not mean anyone declared a dependency, reviewed the tarball, or ran the code in production. Press that leads with "millions of downloads" can accidentally amplify the farm's intended signal.
This is also why comparing Attestd to vulnerability-only sources such as NVD or advisory-centric indexes such as OSV is not about replacing those feeds. It is about not asking them for a popularity-shaped trust verdict they were never designed to give. For the install path itself, see the supply chain docs and CI/CD use case.
What to do instead of trusting the chart#
Before a package with impressive download numbers enters a lockfile, ask for signals the adversary cannot buy as cheaply as Actions minutes.
Check reverse dependents ahead of download charts. A package with multi-million download counts and no dependents is a registry-side anomaly. Prefer ecosyste.ms, libraries.io, or your own SBOM graph over npm's download sparkline.
Gate on compromise independently of popularity and CVE state. In CI, fail on supply_chain.compromised even when risk_state would have been ignored under a severity-only policy, and even when the package looks "popular." The live mathsbase and indexed-btree responses above are the concrete branch condition.
Treat sudden download cliffs on brand-new names as hostile until proven otherwise. JFrog observed packages crossing millions of downloads within days of first publish, then weaponised after a clean look. Volume acceleration without dependents is not a growth story.
Keep name-integrity checks in the same path. Lookalike names (mathsbase vs mathjs, indexed-btree vs sorted-btree) are a separate class from inflated counters, but campaigns often combine both. See how npm supply chain attacks work for the wider pattern set.
Do not let install-script absence close the case. The btree cluster's public analyses show runtime triggers inside ordinary library methods. Blocking postinstall is still worth doing. It is not a substitute for compromise telemetry or code review on high-risk imports.
Download counts are advertising, not attestation#
Organic-looking charts can be adversary-controlled telemetry. Equation of Compromise made the factory public: GitHub Actions workers requesting tarballs and throwing the bytes away so npm would keep counting. Millions of downloads with zero reverse dependents is a smell that survives individual package takedowns because it is a property of the registry metric, not of one tarball.
Attestd's contribution on these versions is not a popularity score. It is the independent supply_chain.compromised condition with version-specific timestamps and sources, which still fires when a download-threshold watchlist would have treated the package as too popular to be interesting.
If your install policy still equates "lots of downloads" with "safe enough to skip," the farm already has your threshold. Change the question to dependents, compromise state, and name integrity before the next chart looks convincing.
Related
EditorialAn LLM SDK package name is not a safety signal
AI and LLM SDK naming is trust bait, not safety. A 2026-09-28 OSV cluster used preinstall Windows PE droppers behind SDK-branded npm packages.
Robert4 min read
EditorialMCP servers that AI agents install are a different risk class
Agent-installed MCP servers are high privilege and often low download. Popularity thresholds miss this class until OSV catches up. What to check before install.
Robert4 min read
EditorialThe real cost of a supply chain compromise
Public 2026 npm incidents show supply chain cost as blocked builds, uncertain credential exposure, and rebuilds. CVE dashboards miss that split.
Robert4 min read