The Incident
On 2026-08-04 at 09:35 UTC, an attacker in control of the GitHub account of Jared Wray (jaredwray) pushed malicious commits directly to the main branch of the keyv repository and cut keyv@6.0.0. npm accepted the release with a valid GitHub Actions OIDC provenance attestation and SLSA signature — trusted publishing did exactly what it was designed to do; the identity behind the publish credential had already been compromised. Over the next three hours and forty-three minutes (until roughly 13:18 UTC) a self-replicating worm — variously named ChainDrop, Mini Shai-Hulud, or Shai-Hulud “Here We Go Again” — spread to a confirmed footprint of 444 package names across 2,234 versions belonging to twelve unrelated organizations, powered by npm publish tokens the payload harvested from developer workstations as they installed poisoned versions of keyv (~600M downloads/month), flat-cache (~580M/month), file-entry-cache, cacheable, cacheable-request, and cache-manager. Aikido raised the alarm on X during the active push; Semgrep, Wiz, Snyk, Socket, SafeDep, Datadog Security Labs, JFrog, Elastic Security Labs, StepSecurity, Endor Labs, Chainguard, and the SANS Internet Storm Center published analyses within twenty-four hours. No CVE was assigned; Singapore’s CSA issued advisory AD-2026-009. Semgrep and Chainguard trace the same setup.mjs dropper and identical IDE-hook plants to the April 2026 Lightning PyPI compromise SecurityV0 covered on 2026-05-01 and the May 2026 @antv npm campaign — the same TeamPCP-adjacent lineage, now on its third major wave.
MITRE ATT&CK coverage: T1195.002 Compromise Software Supply Chain, T1546 Event Triggered Execution, T1552.001 Credentials in Files, T1102 Web Service (used for the Ethereum-smart-contract C2 channel), T1053 Scheduled Task/Job (the LaunchAgent / systemd token watcher).
The Authority Path That Failed
Two authority failures compound. The initiating failure is a maintainer-account takeover: whoever pushed to jaredwray/keyv held the org’s full publish scope for the keyv, cacheable, flat-cache, file-entry-cache, and cache-manager namespaces on npm. The trust anchor on the publish side was “commits merged to main by an authorized principal, released by the project’s OIDC-linked GitHub Actions workflow.” Trusted Publishing bound the release credential to a specific repository and workflow; it did not verify that the human behind the commits was still the human the account represented. The SLSA signature on keyv@6.0.0 attests to the pipeline that built it, not to the intent of the code that came out.
The propagating failure is the SV0-shaped one. The dropped payload plants two persistence artefacts inside every installed project: .claude/settings.json registers a Claude Code SessionStart hook that executes node .vscode/setup.mjs, and .vscode/tasks.json defines an Environment Setup folder-open task that executes node .claude/setup.mjs. When a developer next opens the project in Claude Code — or in VS Code with trusted-workspace tasks enabled — the environment executes attacker-supplied JavaScript under the developer’s local account through configuration the operator did not author. The gap between what the operator declared (an editor-assistant reading files) and what the runtime exercised (a shell interpreter, at session start, against an in-repo config file) was auditable at deploy time; every installed dependency that carried a .claude/settings.json or a folder-open .vscode/tasks.json was a de-facto execution surface, whether or not the maintainer had been compromised yet. From that foothold the payload also installs com.user.gh-token-monitor.plist (macOS LaunchAgent) or gh-token-monitor.service (Linux systemd), which polls the GitHub API every sixty seconds with a stolen token and detonates an attacker-supplied handler the moment the token starts returning 4xx — inverting the standard “revoke first” incident-response reflex.
SecurityV0 Perspective
This maps to unproven_execution (ASI05). The Claude Code SessionStart hook attaches a code-execution tool — node against an in-repo file — to an agent’s session with no prompt, no confirmation, and no operator-authored declaration. That is the Langflow/CSV-Agent+REPL shape, moved one layer up the stack: instead of a framework default silently binding a REPL, a backdoored dependency rewrites the agent’s startup contract via a config file the framework treats as authoritative. Prior SecurityV0 Shai-Hulud coverage (Lightning PyPI, Hades PyPI, AsyncAPI npm bot PAT) has framed this family as nhi_compromise (ASI06) — accurate for the publish side, and still the secondary angle here — but the August variant is the moment the payload’s persistence surface moves inside the AI coding agent’s session configuration, and that reframes the primary control question.
What To Do
- Pin the keyv/cacheable family to pre-2026-08-04 versions.
keyv@6.0.0, and every version ofcacheable,flat-cache,file-entry-cache,cacheable-request,cache-manager, and@cacheable/utilspublished inside the 09:35–13:18 UTC window on 2026-08-04, are the confirmed malicious releases. Refuse>=ranges in CI for this set, downgrade any transitive resolution that landed on those tags, and cross-reference the CSA AD-2026-009 advisory. Lockfile-drift alerts should treat any bump into the keyv/cacheable namespace on that date as a security event, not a maintenance one. - Inventory
.claude/settings.jsonand.vscode/tasks.jsonon every workstation and CI runner. Diff against a known-good baseline. UnexpectedSessionStarthooks,runOn: folderOpenentries, or command strings invokingnode .claude/setup.mjsornode .vscode/setup.mjs(or any sibling filename) are incident indicators. Alert on any creation or modification of those files inside the 2026-08-04 window, and treat the file’s hash — not “does it exist” — as the control. - Kill the token watcher before revoking any credential. Grep
~/Library/LaunchAgents/forcom.user.gh-token-monitor.pliston macOS and~/.config/systemd/user/forgh-token-monitor.serviceon Linux.launchctl unload(orsystemctl --user disable) and delete the unit file first; only then revoke and rotate the associated npm, GitHub, AWS, Vault, and cloud credentials. The watcher self-clears at twenty-four hours, but “wait it out” is not the plan — the plan is to remove the trigger before you become the trigger. - Rotate every credential reachable from a host that installed a keyv-family release in the malicious window. In scope:
.npmrcpublish tokens (revoke and reissue, do not rotate in place), GitHub CLI tokens, fine-grained and classic PATs, AWS/Azure/GCP SDK credentials, HashiCorp Vault tokens, kubeconfigs, SSH keys, database connection strings, Stripe and Slack API tokens, and crypto wallet material. Order matters: watcher first (previous bullet), then credentials; the payload reads secrets at install time, not just on first agent session. - Treat AI-agent and IDE config drops from dependencies as install-time scripts. Whatever policy your organization applies to
preinstallandpostinstall— sandboxing, disabling, or install-time review — must extend to.claude/settings.jsonhooks and.vscode/tasks.jsonfolder-open tasks that dependencies write into project directories. A.claude/settings.jsonwritten bynpm installis functionally apostinstallscript that fires on the developer’s next agent session; scan for it, gate it, and refuse to open projects whose IDE-agent config appeared with a dependency install.
Sources
- Snyk — Inside the keyv npm Supply Chain Compromise
- Semgrep — ChainDrop npm Worm Hits 400+ Packages
- Wiz — keyv and cacheable npm Package Hijacked
- Datadog Security Labs — Worm Compromises Hundreds of Popular npm Packages
- SafeDep — npm Worm Poisons keyv, cacheable, and 400+ Other Packages
- SafeDep — Malicious npm Packages Backdoor Claude Code Sessions
- Elastic Security Labs — Shai-Hulud Strikes Again: CHAINDROP
- Iron Castle / SANS Internet Storm Center — Don’t Revoke That Token Yet
- Chainguard — Inside the Mini Shai-Hulud Campaign: keyv and cacheable
- Singapore CSA — Advisory AD-2026-009
- The Hacker News — Keyv-Linked npm Worm Poisons Hundreds of Packages
- MITRE ATT&CK: T1195.002, T1546, T1552.001, T1102, T1053