npm worm poisons 2,234 versions across 444 packages, including keyv and cacheable, stealing cloud and CI credentials
On 4 August 2026, an npm worm published 2,234 poisoned versions across 444 package names, using credentials from twelve organisations. Affected scopes include @ornikar, @deliveroo, @onereach, @or-sdk, @servicetitan, @qlik, @picsart, and the keyv and cacheable families. The keyv, flat-cache, file-entry-cache and cacheable-request packages together receive about 1,877 million monthly downloads. ESLint pulls in flat-cache and file-entry-cache, so many projects receive them transitively. The payload steals cloud credentials, registry and GitHub tokens, database strings and private keys, and reads GitHub Actions runner memory. A watcher runs an attacker-supplied command when a stolen token is revoked. SafeDep links the activity to the Shai-Hulud marker also seen in the May 2026 TanStack compromise, but states that this does not establish attribution.
Detalhes técnicos
- First known poisoned publish was at 09:35 UTC and the last at 13:18 UTC. Eight organisations published between 10:12 and 10:46 UTC at roughly one package per second. The identical payload and the timing support an automated propagation hypothesis, which remains an inference because the token-theft routine is still obfuscated.
- Poisoned packages add a
preinstallhook (node setup.mjs) and addsetup.mjsandMath_Symbol.jsto the publish allowlist. The library code underdist/is unchanged. - keyv has an extra trigger. Committed
.claude/settings.json(SessionStart hook) and.vscode/tasks.json(runOn folderOpen) files cross-reference each other'ssetup.mjs, so opening a checkout in VS Code or Claude Code runs the payload withoutnpm install. The commit was marked verified and attributed to github-actions[bot], consistent with an API-created commit carrying a spoofed author. - The keyv release was published through GitHub Actions OIDC and carries valid SLSA provenance, because the normal pipeline built malicious source. @hubsync and @ornikar were published from direct npm accounts.
setup.mjsis an obfuscated loader. It downloads Bun 1.3.13 from the official GitHub release, runs the 727,680-byteMath_Symbol.jsbundle (math_init.jsin some packages), then deletes the downloaded files. The install stage contacts only GitHub.- The bundle holds ten payloads encrypted with PBKDF2 (salt
svksjrhjkcejg, 200,000 iterations) and AES-256-GCM. They include a GitHub token watcher, a Runner.Worker/proc/<pid>/memscraper, a workflow that dumpstoJSON(secrets)to an artifact (named "Run Copilot"), and two RSA-4096 public keys for exfiltration. - The payload targets AWS, GCP, Azure, GitHub, npm, Stripe, Vault, Kubernetes, database and private-key credentials, and cloud metadata endpoints.
- The watcher polls api.github.com/user every 60 seconds and runs the saved handler on a 40x response. It persists through a macOS LaunchAgent or a Linux systemd user service with lingering enabled, and stops after 24 hours if the handler has not run. The handler is unknown, so treat it as arbitrary local execution. In the TanStack incident the handler ran
rm -rf ~/. - Exfiltration uses public GitHub dead-drop repositories and Actions artifacts. More than 1,000 public repositories carried the description "Shai-Hulud: Here We Go Again".
- The payload also contains npm publishing, OIDC token exchange, and Fulcio and Rekor client code, so future poisoned packages could carry valid provenance.
Remediação
- Search lockfiles, CI caches and installed packages for the affected names and payload files. Match on the install hook and file hash rather than the filename.
- Remove the watcher's LaunchAgent or systemd unit before revoking credentials, and run
loginctl disable-lingerfor affected Linux users. - Rotate GitHub PATs and App tokens, npm tokens, cloud credentials, Vault tokens, Kubernetes service-account tokens, database credentials and private keys.
- Disable lifecycle scripts in CI (
ignore-scripts=truefor npm,enable-pre-post-scripts=falsefor pnpm). - Search every repository reachable by exposed credentials for
Run Copilot,toJSON(secrets),.claude/settings.json,.vscode/tasks.json,.claude/setup.mjs,.vscode/setup.mjsandmath_init.js. - Treat any checkout opened in an IDE or coding agent as a possible execution event, and remove the hooks before reopening it.
- Review source changes, install hooks and release behaviour in addition to checking provenance and commit signatures.
Linha do tempo
-
npm worm poisons 2,234 versions across 444 packages, including keyv and cacheable, stealing cloud and CI credentials
ler a análise desta fonte ocultar
On 4 August 2026, an npm worm published 2,234 poisoned versions across 444 package names, using credentials from twelve organisations. Affected scopes include @ornikar, @deliveroo, @onereach, @or-sdk, @servicetitan, @qlik, @picsart, and the keyv and cacheable families. The keyv, flat-cache, file-entry-cache and cacheable-request packages together receive about 1,877 million monthly downloads. ESLint pulls in flat-cache and file-entry-cache, so many projects receive them transitively. The payload steals cloud credentials, registry and GitHub tokens, database strings and private keys, and reads GitHub Actions runner memory. A watcher runs an attacker-supplied command when a stolen token is revoked. SafeDep links the activity to the Shai-Hulud marker also seen in the May 2026 TanStack compromise, but states that this does not establish attribution.
Technical Details
- First known poisoned publish was at 09:35 UTC and the last at 13:18 UTC. Eight organisations published between 10:12 and 10:46 UTC at roughly one package per second. The identical payload and the timing support an automated propagation hypothesis, which remains an inference because the token-theft routine is still obfuscated.
- Poisoned packages add a
preinstallhook (node setup.mjs) and addsetup.mjsandMath_Symbol.jsto the publish allowlist. The library code underdist/is unchanged. - keyv has an extra trigger. Committed
.claude/settings.json(SessionStart hook) and.vscode/tasks.json(runOn folderOpen) files cross-reference each other'ssetup.mjs, so opening a checkout in VS Code or Claude Code runs the payload withoutnpm install. The commit was marked verified and attributed to github-actions[bot], consistent with an API-created commit carrying a spoofed author. - The keyv release was published through GitHub Actions OIDC and carries valid SLSA provenance, because the normal pipeline built malicious source. @hubsync and @ornikar were published from direct npm accounts.
setup.mjsis an obfuscated loader. It downloads Bun 1.3.13 from the official GitHub release, runs the 727,680-byteMath_Symbol.jsbundle (math_init.jsin some packages), then deletes the downloaded files. The install stage contacts only GitHub.- The bundle holds ten payloads encrypted with PBKDF2 (salt
svksjrhjkcejg, 200,000 iterations) and AES-256-GCM. They include a GitHub token watcher, a Runner.Worker/proc/<pid>/memscraper, a workflow that dumpstoJSON(secrets)to an artifact (named "Run Copilot"), and two RSA-4096 public keys for exfiltration. - The payload targets AWS, GCP, Azure, GitHub, npm, Stripe, Vault, Kubernetes, database and private-key credentials, and cloud metadata endpoints.
- The watcher polls api.github.com/user every 60 seconds and runs the saved handler on a 40x response. It persists through a macOS LaunchAgent or a Linux systemd user service with lingering enabled, and stops after 24 hours if the handler has not run. The handler is unknown, so treat it as arbitrary local execution. In the TanStack incident the handler ran
rm -rf ~/. - Exfiltration uses public GitHub dead-drop repositories and Actions artifacts. More than 1,000 public repositories carried the description "Shai-Hulud: Here We Go Again".
- The payload also contains npm publishing, OIDC token exchange, and Fulcio and Rekor client code, so future poisoned packages could carry valid provenance.
Remediation
- Search lockfiles, CI caches and installed packages for the affected names and payload files. Match on the install hook and file hash rather than the filename.
- Remove the watcher's LaunchAgent or systemd unit before revoking credentials, and run
loginctl disable-lingerfor affected Linux users. - Rotate GitHub PATs and App tokens, npm tokens, cloud credentials, Vault tokens, Kubernetes service-account tokens, database credentials and private keys.
- Disable lifecycle scripts in CI (
ignore-scripts=truefor npm,enable-pre-post-scripts=falsefor pnpm). - Search every repository reachable by exposed credentials for
Run Copilot,toJSON(secrets),.claude/settings.json,.vscode/tasks.json,.claude/setup.mjs,.vscode/setup.mjsandmath_init.js. - Treat any checkout opened in an IDE or coding agent as a possible execution event, and remove the hooks before reopening it.
- Review source changes, install hooks and release behaviour in addition to checking provenance and commit signatures.