What shipped between 09:35 and 11:44 UTC

The first poisoned release was keyv version 6.0.0, published at 09:35 UTC on 4 August. Semgrep, which named the campaign ChainDrop, counted 1,557 malicious versions across 435 packages in the burst that followed, between 09:40 and 11:44 UTC. Wiz, tracking the same incident, put the figure at more than 400 distinct packages and traced it to a maintainer account whose credentials had been taken. These were not typosquats and they were not new packages. They were the real libraries, republished under the real maintainer's name, with an extra file attached.

The spread crossed organisational boundaries within half an hour. Affected scopes included @deliveroo, @ornikar, @qlik, @servicetitan, @onereach, @or-sdk, @arv-bedrock and @adminide-stack, alongside the keyv and cacheable families themselves. Those organisations share no code and no ownership. What they shared was a stolen publishing token: each infection harvested npm credentials from the machine that installed the package, then used them to republish whatever else that token could reach. That is what makes this a worm rather than a single poisoned release.

The carriers are packages nobody chose

We pulled download counts from the npm registry for the 30 days ending 3 August, the day before the compromise. keyv drew 619.7 million downloads. flat-cache drew 579.8 million. file-entry-cache drew 571.2 million. cacheable-request drew 137.0 million, cacheable 30.1 million and cache-manager 16.3 million. Those six libraries together account for roughly 1.95 billion downloads in the month before they were poisoned.

Almost nobody installs flat-cache or file-entry-cache deliberately. They are caching helpers that arrive underneath linting and build tooling, several levels down a dependency tree, in projects whose owners have never typed either name. That is the uncomfortable part of the arithmetic. The packages with the largest blast radius are precisely the ones absent from the dependency list you reviewed and approved, so auditing your own choices tells you very little about this incident. The number that predicts your exposure is how many times your build ran on 4 August.

The payload went looking for the AI assistant

The mechanism was a preinstall hook, which matters because it runs during dependency resolution and does not wait for your code to import anything. The loader files, setup.mjs and math_init.js, downloaded a standalone Bun runtime and executed an obfuscated second stage. Wiz identified the traffic by its user agent, Bun/1.3.13. The harvester went after npm tokens, cloud credentials, SSH keys, CI secrets, cryptocurrency wallets and AI configuration files.

It also wrote persistence into the .claude and .vscode directories, which is a deliberate choice about where developers now keep trust. An editor and an AI assistant hold repository access, shell permission and, increasingly, the authority to run commands. Command and control was hidden in an Ethereum smart contract, read with an eth_call and returning the domain npm-cache[.]com, so the operators can repoint their infrastructure without shipping new malware. Exfiltration went into GitHub repositories created under the compromised identities and labelled Shai-Hulud: Here We Go Again, naming the malware family this payload descends from.

Two hours is long enough to be a NIS2 finding

For an operator in the EU or the UK the compliance question is not only whether you were breached but whether you can describe what happened. Two of the affected scopes belong to Deliveroo and Ornikar, a UK delivery platform and a French driving school company. Neither is an obscure shop and neither chose to be part of this incident. NIS2 places supply chain security with management rather than with the security team, and DORA asks financial entities for the same evidence about third party ICT risk.

So the record matters as much as the remediation. Establish whether any build ran between 09:35 and roughly 15:00 UTC on 4 August, the point at which the Ethereum command and control channel was documented and analysis was still continuing. Rotate every credential a runner could reach, not only the npm token: cloud keys, GitHub tokens, SSH keys, Kubernetes and Terraform credentials. Rebuild the runners rather than cleaning them. Then write down what you rotated and when, because that document is what a regulator, an auditor or an insurer will ask for.