The dependency you did not choose
A cache library nobody installs on purpose reached two billion monthly installs with a credential stealer attached. The lesson is about transitive trust, not about npm.
On 4 August 2026, someone took over the GitHub account of the maintainer behind keyv, pushed straight to main, and cut a release. Aikido's write-up counts eleven packages compromised directly — keyv at around 604 million monthly downloads, flat-cache at 580 million, file-entry-cache at 571 million, cacheable-request at 137 million — and at least 444 packages across 1,381 versions infected as it spread, somewhere north of two billion combined monthly installs.
Ask a room of developers how many of them installed flat-cache on purpose. You will not get many hands.
That is the whole problem in one question.
What it actually did
The payload was mundane in shape and thorough in execution. A preinstall script dropped two files, which downloaded the Bun runtime and used it to run a 728 KB obfuscated stealer. It went after npm tokens, GitHub credentials, AWS keys, Kubernetes secrets, HashiCorp Vault tokens, and Stripe and Slack credentials.
Then it used what it found to keep going. Stolen npm tokens let it republish other packages the victim maintained. Stolen GitHub App tokens let it write hooks into repository configuration, so the code ran again when another developer simply opened the project in their editor.
That last detail is the one worth sitting with. The infection did not need anyone to run npm install a second time. Opening the repo was enough.
This was not an unlucky month
It is tempting to file this under bad luck and move on. The year does not support that reading.
On 30 March, axios was compromised through a maintainer account takeover that bypassed the project's GitHub Actions pipeline. On 11 May, 84 malicious versions across 42 @tanstack packages went out in a six-minute window as part of the campaign tracked as Mini Shai-Hulud — and they went out through TanStack's own release pipeline, using its legitimate OIDC identity, after attacker code hijacked the CI runner mid-workflow. The blast radius reached far enough that OpenAI published a response about two affected employee devices. On 1 June, at least 32 packages under @redhat-cloud-services were compromised via a GitHub account taken over by a malicious VS Code extension. Zscaler's researchers count May 2026 as the busiest month on record, with 14 campaigns and 346 indexed packages in 31 days.
The common factor is not a flaw in npm. In four of these, the entry point was a person's account, and in one it was the editor extension that person had installed. The registry worked exactly as designed. It published what an authenticated maintainer told it to publish.
The uncomfortable arithmetic
A modern web project has somewhere between eight hundred and two thousand packages in its dependency tree. You chose perhaps thirty of them. The rest arrived as a consequence.
Every one of those maintainers is a person with an email address, a password manager they may or may not use well, and an editor full of extensions. Your production credentials are, in a real sense, protected by the weakest account recovery flow among two thousand strangers.
Nobody audits two thousand packages. That is not a moral failing, it is arithmetic. So the useful question is not "how do we vet everything" but "what happens when one of them turns".
What is actually worth doing
Most advice after an incident like this is a list of things nobody will do. These four are cheap enough that a small team will actually keep them.
Turn off install scripts by default. npm config set ignore-scripts true, then allow the handful that genuinely need it. Both the keyv and axios payloads ran through install hooks. This one setting breaks the most common delivery mechanism, and it costs you about twenty minutes of finding out which packages complain.
Stop letting CI hold long-lived tokens. The stealer was hunting credentials because credentials are what make the next hop possible. Short-lived, narrowly scoped tokens issued per run turn a total compromise into an inconvenience. If your CI has an npm token that can publish anything you own, assume that is the actual crown jewel.
Pin, and delay. A lockfile is not enough on its own if you refresh it eagerly. Most of these attacks were caught within hours to days. A policy of not adopting a release until it is a week old costs you nothing on the vast majority of updates and takes you out of the window where the damage happens.
Know what you would do at 2am. Which credentials would you rotate, in what order, and who can do it? If the answer is "we would work it out", you will work it out slowly, while a worm is republishing packages under your name.
The part that does not have a fix
None of the above addresses the underlying condition, which is that we have built an industry on a trust model with no revocation story. When a maintainer's account falls, there is no mechanism that tells the two billion installs downstream to stop.
The registries are improving, and provenance attestation, trusted publishing and mandatory 2FA on high-traffic packages all raise the cost of the account-takeover route. But TanStack is the case that should temper any comfort taken from that: those packages were signed by the real pipeline, with the real identity, because the attacker was inside the workflow when it ran. Trusted publishing proves where a build came from. It does not prove that what went in was what the maintainer intended.
So the shape of the thing is unchanged. Somewhere in your dependency tree is a package you have never heard of, maintained by someone you will never meet, and it runs with your permissions.
The right response is not paranoia. It is to stop treating that as an unusual state of affairs, and to build as though a package will turn, because roughly once a month now, one does.
Building something like this?
We are a product studio in Kathmandu. Tell us what you are building and an engineer will reply.