An npm worm poisoned 444 packages in under four hours

On August 4, 2026, attackers compromised the GitHub account of Jared Wray (jaredwray), maintainer of keyv (127 million weekly downloads, per incident reporting) and the cacheable package family. In under four hours they injected credential-stealing malware across 444 packages — 2,234 poisoned versions total (SafeDep, August 2026, https://safedep.io/keyv-npm-supply-chain-compromise/). Keyv, flat-cache, file-entry-cache, and cacheable-request receive a combined 1,877 million monthly downloads (SafeDep).

Every affected package received setup.mjs and Math_Symbol.js with a preinstall hook executing before npm install completes. The malware harvests npm, GitHub, AWS, HashiCorp Vault secrets, cryptocurrency wallets, and CI/CD environment variables — everything an automated build process touches.

This is the largest self-propagating npm worm documented. It is not a zero-day in the npm registry itself; it is a maintainer account compromise via credential stuffing, phishing, or SIM swap. The vector is familiar. The scale is not.

What a preinstall hook actually does

The preinstall hook executes before package installation completes. That means the malware runs before a developer or a CI pipeline has finished typing the command. It does not wait for import statements. It does not wait for runtime. It executes the moment npm begins processing the package, and by the time the install completes, the secrets are already gone.

Developers reviewing package.json for suspicious dependencies will not see the hook unless they read the scripts block. Automated scanners checking for known malicious patterns will miss novel payload delivery. And CI/CD systems running npm install as part of a build pipeline will execute the hook with whatever credentials that pipeline has access to — which in most cases is everything.

The attack window is the time between compromise and detection. In this case, that window was under four hours for initial injection (Wiz, August 2026, https://www.wiz.io/blog/keyv-and-cacheable-npm-supply-chain-attack), but the propagation continued as downstream projects pulled the poisoned packages. Every npm install that touched keyv or any of the other affected packages between compromise and public disclosure ran the malware.

2 billion monthly installs is an ecosystem-scale number

When you compromise a maintainer account at that tier, you are not attacking one project — you are attacking every project that depends on it, and every project that depends on those, and every CI/CD pipeline that rebuilds when dependencies update.

The malware targeted secrets: npm tokens, GitHub personal access tokens, AWS credentials, HashiCorp Vault keys, cryptocurrency wallet seeds, and environment variables carrying API keys and service account credentials. Those are the credentials that let you publish to registries, push to repositories, deploy to production, and access vaults. Once exfiltrated, they enable persistent access long after the malicious package is removed.

This is not theoretical supply chain risk. These packages carried live credential stealers, distributed through the same mechanism developers use every day to pull in dependencies. The trust model broke: maintainer accounts are the supply chain, and when one is compromised, the blast radius is everything downstream.

The thing that did not fail

The npm registry itself did not have a zero-day exploited. The packages were published through legitimate maintainer credentials. The malware was inserted via a compromised GitHub account, not a registry vulnerability. The preinstall hook is a documented npm feature, not an exploit. Everything worked as designed.

That is the problem. The supply chain attack succeeded because the system trusted the maintainer account, and the maintainer account was compromised. No zero-day required. No novel technique. Just credential theft at a high-value target, followed by automated propagation through dependency graphs that assume published packages are safe.

The npm ecosystem has no cryptographic signing requirement for packages. Maintainer accounts are protected by whatever 2FA the individual maintainer chooses to enable, if any. There is no out-of-band validation for high-impact packages before they propagate to millions of installs. The system assumes that if a package is published under a known maintainer's account, it is legitimate.

That assumption held until August 4. It does not hold anymore.

What this says about managed file transfer and vendor trust

Attackers go after the infrastructure that moves data and code between trusted parties, because once inside that infrastructure, they inherit the trust relationship.

An npm maintainer account is a managed file transfer system. It moves code from a developer's machine to millions of downstream consumers. When that account is compromised, the attacker does not need to exploit each consumer individually — the supply chain does the distribution for them.

The same pattern appears in software update mechanisms, cloud deployment pipelines, and package registries across every language ecosystem. The common element is trust: the downstream consumer assumes that anything coming through the official channel is safe. When the channel is compromised, that assumption becomes the attack vector.

What happens next

The malicious packages were identified and removed. The maintainer account was secured. The compromised credentials exfiltrated during the attack window are still out there, and every system that pulled one of those packages between compromise and removal is potentially exposed.

The response for affected projects is credential rotation: assume everything the build pipeline had access to during the window is compromised, rotate it, and audit for unauthorized use. For npm as an ecosystem, the question is whether this forces a signing and verification model, or whether the trust-the-maintainer assumption holds until the next compromise.

The pattern is always the same: we assume the thing that just broke will not break again, we add a rule to catch that specific case, and the next attack uses a slightly different vector that the new rule does not cover. The fix is not better detection after compromise. The fix is cryptographic proof before propagation.

Until npm requires signed packages and verifies those signatures before allowing installation, the supply chain will remain exactly as vulnerable as the weakest maintainer account in the dependency tree. And given how fast this attack propagated, we now know how fast that vulnerability converts to impact.

If you run npm install in a pipeline with access to production credentials, that pipeline just became a credential exfiltration risk. The worm is contained. The model is not.