Threat · curated 4 Aug 2026

Keyv-Linked npm Worm Poisons Hundreds of Packages, Plants Claude Code and VS Code Hooks

Dossier

Coverage timeline

4 Aug 2026thehackernews.com

Single-source incident — first reported, latest, and curated coincide.

Why it matters

The Keyv npm worm weaponizes Claude Code and VS Code workspace hooks as an execution path, showing how AI coding-agent trust prompts and IDE automation become a live supply-chain infection vector for developer credentials.

A self-propagating credential-stealing npm worm first seen in keyv@6.0.0 spread into hundreds of packages across multiple organizations on August 4, 2026, using a preinstall script to harvest repository, registry, cloud, and private-key material from developer and CI environments before republishing to more packages. SafeDep verified 353 poisoned versions across 79 package names, with Aikido reporting a wider footprint. The compromised Keyv repository also retained Claude Code and VS Code hooks that can execute the payload once a developer trusts the workspace.

campaign

Summary

A self-propagating, credential-stealing npm worm that first appeared in keyv@6.0.0 spread beyond the Keyv and Cacheable namespaces into hundreds of packages across multiple organizations on August 4, 2026. SafeDep verified 353 poisoned versions across 79 package names, while broader totals from SafeDep monitoring (442 versions/353 names) and Aikido (868 packages/1,381 versions) could not be independently reproduced at the reporting cutoff.[0]

The malicious release used a preinstall script to run a credential-stealing compiled bundle inside developer and CI environments, harvesting repository, registry, cloud and private-key material and then using available npm publishing access to poison more packages. Any workstation or runner that executed an affected version should be treated as credential-exposed.[0]

The activity is attributed to the Shai-Hulud malware family based on shared Claude Code and VS Code hooks, the setup.mjs filename and a Bun 1.3.13 download previously seen in an April lightning PyPI compromise, but neither the initial access path nor a named operator is established.[0]

Attack chain

  1. Initial compromise / package poisoning: A malicious keyv@6.0.0 release was published, adding 'node setup.mjs' as a preinstall command and bundling setup.mjs and Math_Symbol.js while leaving compiled library code unchanged. The release carried valid OIDC and SLSA provenance because it passed through the project's legitimate GitHub Actions release workflow, which verified the build but not the safety of the source.[0]
  2. Execution via lifecycle script: On install under npm clients that permit lifecycle scripts, the preinstall script runs stage one, which checks for Bun, downloads Bun 1.3.13 from the runtime's official GitHub releases if needed, and hands off to a 727,680-byte compiled bundle.[0]
  3. Alternate execution via editor/agent hooks: The repository carries a second execution path: a .claude/settings.json SessionStart hook calling .vscode/setup.mjs and a .vscode/tasks.json Environment Setup task with runOn folderOpen calling .claude/setup.mjs, which can execute the payload once a user trusts the workspace or permits the project configuration.[0]
  4. Credential harvesting: The compiled bundle harvests GitHub, npm, cloud, Vault, Kubernetes, database and private-key material, reads GitHub Actions runner memory, and installs a token-revocation watcher.[0]
  5. Propagation: Using stolen npm publishing access, the payload carries machinery to modify, version and republish packages, poisoning additional packages in whole-namespace publishing bursts; automated propagation best fits the timing though the initiating function was not recovered.[0]

Disclosure timeline

DateEvent
2026-04Semgrep documented the same Claude Code and VS Code hooks, setup.mjs filename and Bun 1.3.13 download in a compromise of the lightning PyPI package.[0]
2026-08-04The worm spread beyond the Keyv and Cacheable namespaces into hundreds of packages; SafeDep, Socket and Aikido reported on the campaign.[0]
2026-08-04 17:40 ISTnpm package pages showed earlier releases restored as 'latest' for at least nine packages (including keyv@5.6.0, flat-cache@6.1.23, cache-manager@7.2.9), while both hook files were still present on the repository's main branch and core/keyv/package.json still declared version 6.0.0 with the preinstall command.[0]

How it works

The worm abuses npm dependency lifecycle scripts: the poisoned keyv@6.0.0 declared 'node setup.mjs' as a preinstall command, so simply installing the package under an npm client that permits lifecycle scripts executes attacker code. Stage one checks for the Bun runtime, downloads Bun 1.3.13 from official GitHub releases if absent, and executes a 727,680-byte compiled bundle that steals credentials and republishes packages using stolen npm identities.[0]

A second, independent execution path lives in repository configuration: a .claude/settings.json SessionStart hook and a .vscode/tasks.json Environment Setup task (runOn folderOpen) invoke setup.mjs. These trigger on session start or folder open, though VS Code blocks automatic tasks in untrusted workspaces and Claude Code applies workspace trust, so they require the workspace to be trusted.[0]

The malicious release passed through the project's legitimate GitHub Actions release workflow, so it carried valid OIDC and SLSA provenance that correctly identified the build process but could not establish that the source entering it was safe. The payload also installs a token-revocation watcher that triggers on revocation, so rotating credentials first can run an attacker-supplied local handler.[0]

Affected versions and patch status

ProductAffectedPatch status
keyv (npm)6.0.0 (malicious preinstall payload)At 17:40 IST on Aug 4, keyv@5.6.0 was restored as 'latest'; the 5.x line and published @keyv/* adapters were reported clean, but core/keyv/package.json still declared 6.0.0 with the preinstall command on main.[0]
Broader npm namespaces (Keyv, Cacheable and beyond)Hundreds of packages; SafeDep verified 353 poisoned versions across 79 names, with larger unverified totals from SafeDep monitoring and AikidoSome earlier releases restored as 'latest' for at least nine packages including flat-cache@6.1.23 and cache-manager@7.2.9; full campaign not independently mapped, so exposure checks require exact package names, resolved versions and lockfiles.[0]

Indicators of Compromise

TypeIndicatorContext
otherkeyv@6.0.0First confirmed malicious npm release; added a preinstall command and shipped the payload files.[0]
file-pathsetup.mjsStage-one loader file shipped in the poisoned package and invoked via preinstall and the editor/agent hooks; same filename seen in the April lightning PyPI compromise.[0]
file-pathMath_Symbol.jsAdditional file included for publication in the malicious keyv@6.0.0 package.[0]
file-path.claude/settings.jsonContains a SessionStart hook that calls .vscode/setup.mjs, providing a second execution path from a checked-out repository.[0]
file-path.vscode/tasks.jsonContains an Environment Setup task with runOn folderOpen that calls .claude/setup.mjs.[0]
otherd8c850c7800eGitHub commit that planted the Claude Code and VS Code hooks; carried a green verified badge and named github-actions[bot] as author, which does not identify who controlled the credential.[0][13]
otherBun 1.3.13Runtime version the loader downloads from official GitHub releases if Bun is absent; same version observed in the April lightning PyPI compromise.[0]

Key takeaways

  • Valid OIDC and SLSA provenance verified only the build and signing paths, not that the source entering the legitimate release workflow was safe, so attestation alone did not prevent a poisoned release.[0]
  • Package-artifact counts (SafeDep and Aikido totals) establish campaign scale but not victim count; system-level exposure depends on the exact resolved version and whether the lifecycle script ran.[0]
  • The campaign carried two execution paths — npm preinstall scripts and Claude Code/VS Code workspace hooks — extending the risk from package installation to merely opening a checked-out repository in an AI-agent or editor context.[0]

Defensive actions

  • Treat any workstation or CI runner that executed an affected version as credential-exposed.: Socket states the payload harvests repository, registry, cloud and private-key material from developer and CI environments, so execution implies credential compromise.[0]
  • Remove the malware's token-revocation watcher before rotating exposed tokens and keys.: SafeDep warns the watcher's trigger is revocation, so rotating first can run an attacker-supplied local handler.[0]
  • Assess exposure using exact package names, resolved versions and lockfiles rather than current 'latest' tags or a namespace-level blocklist.: Registry tags changed too quickly for a fixed list; some earlier releases were restored as 'latest' while some related packages remained clean, so a blocklist risks both missing poisoned versions and flagging clean ones.[0]
  • Restrict or disable npm dependency lifecycle scripts and prefer clients that block unapproved scripts.: The worm executes via a preinstall script; npm 12 blocks unapproved lifecycle scripts by default, but earlier clients and other install paths that permit them remain exposed.[0]
  • Do not automatically trust the Keyv workspace in VS Code or Claude Code when reviewing the repository.: Repository-supplied .claude/settings.json and .vscode/tasks.json hooks can execute the payload once the workspace is trusted or project configuration is permitted; VS Code and Claude Code apply workspace trust before running them.[0]