A credential stealing worm reached roughly 2 billion monthly installs with a valid 'signed by GitHub Actions' stamp, because the attacker controlled the signer's account.
On Tuesday afternoon, a developer somewhere in the JavaScript ecosystem ran npm install, watched the familiar "signed by GitHub Actions" provenance line scroll past, and kept working. By the time anything looked wrong, the worm living inside the package had already sent their GitHub tokens to a public repository whose description read "Shai-Hulud: Here We Go Again", and was using those tokens to look for the next maintainer to compromise.
By 13:37 CEST on August 4, 2026, at least 434 packages across 1381 versions had been signed by the attacker, with a combined footprint of more than 2 billion monthly installs, according to Aikido's running tracker. Socket, Wiz, SafeDep, and The Hacker News are reporting the same incident with overlapping package lists, which is the corroboration that turns a vendor blog into a confirmed event.
The entry point was the GitHub account of Jared Wray, the maintainer behind keyv, a small key-value caching library that, by itself, is downloaded about 127 million times a week and that sits at the root of a family of cache utilities used across Node.js code. Once the attacker had the maintainer's account, they pushed two files to the main branch: a dropper called setup.mjs and a 728-kilobyte obfuscated credential stealer called Math_Symbol.js. The dropper fetched a fresh copy of the Bun runtime from GitHub, then used execFileSync to launch the stealer. A preinstall script in the new package versions told npm to run that dropper automatically during install. Then the attacker cut a release through GitHub Actions, which generated a real, valid provenance signature attesting that the build had been produced by the workflow attached to the keyv repository.
The signature was valid, and the workflow that produced it was running under an identity the attacker controlled. The JavaScript ecosystem has spent the last few years promoting "sign your builds" as the defense against supply-chain compromise: if the artifact on npm carries a valid signature from a trusted identity, a downstream consumer can be confident the bytes match what the maintainer intended to ship. The attacker passed both checks at once.
A Hacker News thread on the incident put it this way: a worm with root in a maintainer's GitHub account has root in that project's build pipeline, and the signature is just a description of what the build pipeline did.
The confirmed package list spans keyv itself (version 6.0.0), flat-cache 6.1.24, file-entry-cache 11.1.6, cacheable-request 13.0.20, cache-manager 7.2.10, and a cluster of @cacheable/* utilities. A second wave of community-spread infections has already shown up in @deliveroo/reevent 1.0.1, @or-sdk/invitations 1.4.9, @picsart/ai-sdk 3.32.2, @qlik/embed-runtime 1.6.4, and picasso.js 2.11.6, each a package that depended on something in the original keyv family, where a maintainer who ran install handed the worm a fresh batch of credentials.
The mitigations that follow are not new. What is new is the size of the demonstration. Pin dependencies by hash rather than by version range, and the worm's package.json change has nowhere to go. Disable preinstall, install, and postinstall scripts by default in your package manager, and the dropper never runs; this is the same approach npm and yarn have supported for years via --ignore-scripts, and it is one of the rare cases where the unfriendly default is the safer one. Treat the signer as a possibly compromised identity rather than a verified one, and the signature becomes evidence about what happened in the build, not a stamp of approval. None of this replaces the need to recover the maintainer account, but the trust model that the JavaScript ecosystem actually shipped is the one that has to change: the one where a valid signature is enough.
The list of affected packages is still growing as of publication, with Aikido's tracker updating every few hours. The larger question is structural: the JavaScript ecosystem promoted signed builds as the defense against exactly this scenario, and on Tuesday, the signature was the worm's, not the maintainer's.