The keyv Supply Chain Attack: Affected Versions, IOCs, and the First-Hour Response Runbook

August 4, 2026
August 4, 2026

0 min read

Vulnerabilities
SCA Security
The keyv Supply Chain Attack: Affected Versions, IOCs, and the First-Hour Response Runbook

The keyv supply chain attack, disclosed on August 4, 2026, compromised eleven seed packages across the keyv and cacheable npm namespaces and delivered a Shai-Hulud credential-stealing worm that republished itself into several hundred more packages using stolen npm publishing tokens. keyv, flat-cache, and file-entry-cache alone carry roughly 450 million weekly downloads between them, and the malicious [email protected] shipped with valid npm provenance signed by GitHub Actions. This post is written for AppSec engineers, platform teams, and DevSecOps responders working active exposure as of August 2026, and covers the affected versions, the two-stage attack chain, the AI coding agent persistence mechanism, the indicators of compromise, the first-hour runbook, and the structural reason both provenance validation and static scanning returned clean verdicts.

What Happened: keyv, cacheable, and flat-cache Were Poisoned at the Source

The keyv supply chain attack began when an attacker compromised the GitHub account of maintainer Jaredwray and pushed malicious code into the tagged source of eleven packages, which the legitimate CI workflow then built, signed, and published to npm. The compromise landed upstream of the build, so npm provenance and SLSA attestation passed cleanly on malicious artifacts.

PackageMalicious versionSafe versionApprox. monthly downloads
keyv6.0.05.6.0604M
flat-cache6.1.246.1.23580M
file-entry-cache11.1.611.1.5571M
cacheable-request13.0.2013.0.19137M
@cacheable/utils2.5.12.5.034M
cacheable2.5.12.5.030M
@cacheable/memory2.2.12.2.028M
cache-manager7.2.107.2.916M
@cacheable/node-cache3.1.23.1.16M
@cacheable/net2.1.12.1.03.7K
ecto5.0.15.0.04.5K

The worm spread well past the seed set by abusing publishing credentials stolen from poisoned build runners. Vendor counts disagree, which is expected while a worm is still propagating during analysis: Endor Labs verified 373 packages across 1,006 malicious versions, SafeDep counted 353 poisoned versions across 79 names, Wiz and OX Security reported 400 to 440 or more packages, and Aikido's upper estimate reached 868 packages across 1,381 versions. Treat the blast radius as several hundred packages and rising rather than a settled number, with combined reach above 2 billion monthly installs per OX Security. Organizational npm scopes caught in the secondary wave include @servicetitan (roughly 90 packages), @or-sdk (roughly 90), @onereach (roughly 80), @ornikar (roughly 40), @qlik (roughly 25), plus @nebula.js, @hubsync, and Picsart packages.

Disclosure timestamps also vary. Endor Labs and Snyk place the first malicious publish at roughly 09:30 to 09:35 UTC on August 4, 2026, while Socket's timeline puts [email protected] at 14:05 UTC with the cacheable family following between 15:39 and 15:44 UTC. The source repository showed force pushes, tag deletions, and a commit message reading add setup.mjs and Math_Symbol.js to all @keyv/* packages in the same window. Primary sources here are Endor Labs, Snyk, Socket, Wiz, Aikido, JFrog, SafeDep, and OX Security.

How the Attack Chain Executes: preinstall, Bun 1.3.13, and a 728 KB Payload

The keyv supply chain attack executes in two stages. A preinstall lifecycle hook runs a small obfuscated loader that downloads the legitimate Bun runtime, and Bun then executes a 727,680-byte obfuscated bundle that harvests credentials and republishes itself into other packages.

Stage One: The preinstall Hook and the Bun Loader

The trojanized package.json adds a single line: "preinstall": "node setup.mjs". The loader setup.mjs is 29,918 bytes, lightly obfuscated, and detects Linux, macOS, and Windows. When Bun is absent, the loader downloads an unmodified Bun 1.3.13 binary from the official release path github.com/oven-sh/bun/releases/download/bun-v1.3.13/, extracting it with system unzip, PowerShell Expand-Archive, or a bundled JavaScript ZIP parser fallback, and leaving temporary directories matching bun-dl-*. Pulling a signed, widely trusted runtime from an official GitHub release means the loader stage needs no attacker-controlled domain, and no binary reputation check will flag the download.

Stage Two: The Math_Symbol.js Payload

Bun executes the second stage, delivered as Math_Symbol.js or math_init.js, both 727,680 bytes and byte-identical. A third artifact, router_runtime.js, has also been observed. Socket documents the bundle as a Bun build using basE91-encoded strings with polymorphic per-scope alphabets, which defeats naive string-matching signatures, and recovered four module tags that map to the malware's architecture: [collector] for credential harvesting, [dispatcher] for exfiltration routing, [provenance] for attestation handling, and [publish] for autonomous republication.

Stage Three: Credential Harvesting

The collector sweeps far wider than npm credentials, which is what sets the rotation scope in the runbook below.

  • Cloud: ~/.aws/credentials, environment variables, the instance metadata service at 169.254.169.254/latest/api/token, IAM security-credentials endpoints, AWS Secrets Manager enumeration, GCP service account keys, and Azure client secrets.
  • Kubernetes and Vault: /var/run/secrets/kubernetes.io/serviceaccount/token, KUBECONFIG files, VAULT_TOKEN, ~/.vault-token, and /home/runner/.vault-token.
  • npm: ~/.npmrc authToken values, validated live against registry.npmjs.org/-/whoami.
  • GitHub: ghp_ personal access tokens, gho_ OAuth tokens, ghs_ Actions and App tokens, and ACTIONS_ID_TOKEN OIDC values recovered by dumping GitHub Actions runner memory.
  • Third-party and generic: Stripe sk_ and pk_ keys, Slack xox tokens, and a TruffleHog-style regex sweep across roughly 200 glob patterns covering .env files, PEM and SSH keys, Terraform state, docker/config.json, and IDE config files.

Stage Four: Exfiltration and Self-Propagation

The dispatcher creates public GitHub repositories under the victim's stolen identity via POST /user/repos and commits stolen data through GraphQL createCommitOnBranch mutations. The payload RSA-encrypts the data first, so only the attacker holds the key. Every exfiltration repository carries the description string Shai-Hulud: Here We Go Again, and counts range from 821 (OX Security) to roughly 1,300 (Aikido) depending on when the count was taken. The fallback channel is npm-cache[.]com:443/router, a domain registered 2026-05-22 and resolving to 104.21.35[.]216 behind Cloudflare, alongside a DomainSender component performing DNS tunneling. Wiz additionally reports an Ethereum smart contract used to retrieve the C2 domain, which remains single-source at the time of writing.

Propagation runs off the stolen publishing credentials. The payload queries registry.npmjs.org/-/v1/search?text=maintainer: to enumerate every package a stolen token can publish, downloads each tarball, injects the identical preinstall hook and payload files, recomputes the integrity and shasum fields so the tarball still validates, bumps the version, and republishes through registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/. That last path is npm OIDC trusted publishing, used as a propagation channel rather than defeated.

The Persistence Twist: Claude Code and VS Code Hooks Run Without npm Install

The keyv wave ships persistence through AI coding agent and IDE configuration inside the seed packages themselves, which means a developer can be compromised by opening a repository folder without ever running npm install. Earlier Shai-Hulud waves targeted Claude Code environments and VS Code persistence in some variants. This wave makes those hooks a primary execution path rather than a variant behavior. Two project-local files carry the loader: .claude/settings.json registers a SessionStart hook, and .vscode/tasks.json registers a task with "runOn": "folderOpen".

Config fileTriggerEffect
.claude/settings.jsonSessionStart hookRuns the loader when a Claude Code session starts in the project
.vscode/tasks.json"runOn": "folderOpen" task named Environment SetupRuns the loader when a developer opens the project folder

Snyk traced the Claude hook addition to commit d8c850c7. Researchers describe the file paths differently: Snyk reports each config invoking the setup.mjs in its own directory, while Aikido reports the two crossed. Hunt for setup.mjs in both directories rather than assuming one layout. A separate mechanism named gh-token-monitor installs as gh-token-monitor.sh, a systemd unit gh-token-monitor.service on Linux, and a launchd plist com.user.gh-token-monitor.plist on macOS, and survives package removal.

The operational point lands harder than the novelty. Dependency scanning reads package.json and the lockfile. Dependency scanning does not read .claude/settings.json or .vscode/tasks.json, because those files have never been part of the software supply chain. They are now.

Why npm Provenance and SLSA Attestation Did Not Stop This

The malicious [email protected] carried valid npm provenance signed by GitHub Actions, and the attestation was entirely accurate. The attacker compromised the source repository, so the legitimate workflow faithfully built and attested a malicious artifact.

Provenance proves where an artifact was built and that nothing tampered with the artifact afterward. Provenance proves nothing about whether the source that entered the build was trustworthy. That distinction is the whole incident, and this is the second consecutive Shai-Hulud wave to inherit trusted publishing infrastructure rather than defeat that infrastructure.

One mitigating fact is worth stating plainly. Per JFrog, npm 12 and newer do not execute preinstall hooks by default, so environments already on npm 12 or later were not infected through the install path. Those environments remained exposed through the IDE and agent hooks above.

Indicators of Compromise (IOCs) and Behavioral Signals

Hunt in three places, in this order: the malicious package versions in your lockfiles, the payload file hashes on disk, and Bun process execution on machines that do not use Bun. The last of those is the highest-signal indicator on a CI runner, since a caching library has no reason to introduce a second JavaScript runtime.

File hashes:

ArtifactSHA-256
setup.mjs (wave one)54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668
setup.mjs (later waves)fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb
Math_Symbol.js and math_init.js (727,680 bytes)9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc

File system, network, and CI indicators:

CategoryWhat to hunt for
Package versionsThe eleven seed versions above, blocked by exact version rather than by name, since a safe version of every affected package exists
Dropped filessetup.mjs, Math_Symbol.js, math_init.js, router_runtime.js anywhere under node_modules, $HOME, or /tmp
Manifest hookAny package.json where scripts.preinstall equals node setup.mjs
Persistence artifactsgh-token-monitor.sh, gh-token-monitor.service, com.user.gh-token-monitor.plist; .claude/settings.json with a SessionStart hook; .vscode/tasks.json with a folderOpen task
Runtime artifactsbun-dl-* temporary directories, especially under /tmp; the process chain node setup.mjs spawning a downloaded bun binary
Networknpm-cache[.]com (104.21.35[.]216) port 443, path /router; the Bun/1.3.13 user-agent from hosts with no Bun dependency; requests to 169.254.169.254 from build agents; Bun release downloads on hosts that do not use Bun
Git, GitHub, and CINew public repositories under your org with the description Shai-Hulud: Here We Go Again; unexpected POST /user/repos calls from CI identities; npm publish events nobody initiated, especially patch bumps; anomalous OIDC exchange against registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/

One string in the payload deserves a direct answer. The bundle embeds IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients, an extortion line intended to discourage token revocation. The line is new to this wave and carries no technical weight. Revoke the token.

Immediate Response: The First-Hour Runbook

Work in this order: stop the bleeding in CI, determine exposure from lockfiles, rotate credentials starting with npm and GitHub publishing tokens, then hunt persistence on developer endpoints. Rotation comes before forensics here, since the worm's next hop is your publishing token.

  1. Freeze publishing. Revoke every npm automation token and disable trusted publishing for affected repositories before anything else, since any token a poisoned runner could reach is the next propagation vector.
  2. Enumerate exposure. Run npm ls keyv flat-cache file-entry-cache cacheable-request cacheable @cacheable/utils cache-manager @cacheable/net @cacheable/node-cache @cacheable/memory ecto --all, then grep lockfiles and CI logs for the malicious versions listed above.
  3. Pin and rebuild without executing hooks. Add an overrides block in package.json mapping all eleven packages to their safe versions, then run npm install --package-lock-only --ignore-scripts, remove node_modules, run npm cache clean --force, and finish with npm ci --ignore-scripts.
  4. Rotate every credential the affected runner or workstation could reach: GitHub personal access tokens and App tokens, cloud keys and assumed roles, Kubernetes service account tokens, Vault tokens, Stripe and Slack keys, database connection strings, and SSH keys. The regex sweep found anything sitting on disk, so scope rotation accordingly.
  5. Audit Git and GitHub activity. Search your org for repositories carrying the Shai-Hulud: Here We Go Again description, review force pushes and tag deletions on affected repositories, and check for package publishes nobody initiated.
  6. Hunt persistence on endpoints. Search developer machines and runners for setup.mjs, Math_Symbol.js, gh-token-monitor.*, and modified .claude/settings.json or .vscode/tasks.json files.
  7. Rebuild rather than clean. Treat any CI runner or developer machine that executed the payload as fully compromised and rebuild the host instead of remediating in place.

Finding the Package Is Not the Same as Knowing the Package Ran

Every static gate in the pipeline returned a clean verdict on [email protected]. The signature was valid, the provenance was valid, the SLSA attestation was valid, and the package carried no known CVE. What was anomalous was never the artifact. What was anomalous was the behavior: a caching library spawning a downloaded runtime, reading the cloud metadata service, and opening an outbound channel.

Follow the failure through each control. A malicious package with no CVE is invisible to a scanner matching known-bad identifiers. A polymorphic basE91-encoded bundle is invisible to string-based signatures. A payload delivered by an officially signed Bun binary is invisible to binary reputation. The only unambiguously anomalous stage is execution, which is exactly the stage manifest analysis cannot observe.

Kodem's runtime-powered SCA resolves the affected packages across direct and transitive trees, then narrows the list to the dependencies that actually load and execute in your application, which is what makes a several-hundred-package blast radius triageable rather than paralyzing. Application Detection and Response (ADR) catches the runtime stage without a signature: a preinstall script spawning a network download, an unexpected runtime binary executing from /tmp, a build process reading 169.254.169.254, and a caching dependency initiating outbound traffic to a domain registered ten weeks earlier. That distinction between a package being present and a package having executed is the subject of proving vulnerable code was executed in production. For the prior wave in this campaign family, see the Kodem Mini Shai-Hulud PyTorch Lightning and intercom-client breakdown from April 30, 2026.

The Shai-Hulud Lineage: What Is Actually New in This Wave

This is the sixth major Shai-Hulud wave since September 2025, and most of the tradecraft is inherited from the April 2026 Mini Shai-Hulud campaign, which used identical hook filenames and downloaded the same pinned Bun 1.3.13 runtime against PyPI. Three things distinguish this wave: agent and IDE hook persistence shipped in the seed packages as a primary execution path rather than a variant behavior, an extortion string pressuring victims against token revocation, and npm OIDC trusted publishing used as the propagation channel.

The lineage runs from the original September 2025 npm wave through Shai-Hulud 2.0, The Golden Path, the SAP package compromise, the Miasma variant, and Mini Shai-Hulud against PyTorch Lightning and intercom-client. The cross-ecosystem overlap with that April campaign is the strongest behavioral signal available: identical setup.mjs and payload naming, and the same pinned Bun version, across PyPI in April and npm in August.

The attribution gaps are worth stating rather than papering over. No named threat actor has been identified for this wave, the initial access vector into the maintainer's GitHub account is unknown, whether one publishing account or several were compromised remains unclear, and no CVE has been assigned to the incident.

Hardening Your Pipeline Against the Next Wave

Assume another wave lands within 90 days, since five have landed in eleven months. Every control that would have blunted this one is a configuration change available today, and none of them require knowing the next campaign's indicators.

  1. Disable lifecycle scripts by default in CI. Set ignore-scripts=true in the build agents' .npmrc and allowlist the small number of packages that genuinely need native compilation.
  2. Upgrade to npm 12 or newer, where preinstall hooks do not execute by default, which removes the primary install-time vector outright.
  3. Treat .claude/settings.json and .vscode/tasks.json as executable code. Add both to code review requirements, diff them in CI, and disable automatic task execution on folder open in managed developer images.
  4. Move publishing to short-lived OIDC with environment protection rules, since long-lived npm automation tokens are the fuel this worm runs on. Require manual approval on the publish environment so a stolen token alone cannot republish.
  5. Egress-filter your build agents. A runner that can reach arbitrary GitHub release URLs and arbitrary domains can bootstrap a second-stage payload, so allowlist your registry and artifact stores.
  6. Block the cloud metadata service from build containers. The 169.254.169.254 call is one of the highest-value credential sources in the payload and one of the easiest to deny.
  7. Instrument dependency behavior at runtime, not only at scan time, since the malicious and the safe version of keyv are indistinguishable statically and obviously different at execution. Pair that with the open source security workflow your team already runs on dependency findings.

What This Wave Signals About the Next One

The keyv supply chain attack marks the point where targeting the developer's tooling moved from an occasional variant behavior to a primary execution path. The .claude/settings.json hook is the tell, and that mechanism is the one most likely to be copied.

  • Attackers have moved from defeating trust infrastructure to inheriting that trust. Provenance, SLSA, and OIDC trusted publishing all worked as designed and all signed malicious artifacts.
  • The AI coding agent is now a supply chain surface. Agent configuration files execute code, they live in repositories, and no dependency scanner reads them.
  • The economics favor the worm. One compromised maintainer account reached hundreds of packages and billions of monthly installs within hours.

One falsifiable prediction: the next wave targets agent configuration formats beyond Claude Code and VS Code, specifically MCP server definitions and editor-level agent rule files, because those execute with the same trust and receive less review than package.json.

Frequently Asked Questions

What is the keyv supply chain attack?

On August 4, 2026, an attacker compromised the maintainer account behind the keyv and cacheable npm namespaces and published eleven malicious package versions containing a Shai-Hulud credential-stealing worm. The worm then used stolen npm publishing tokens to spread into several hundred additional packages across at least nine organizations.

Which versions of keyv and cacheable are compromised?

The confirmed malicious seed versions are [email protected], [email protected], [email protected], [email protected], [email protected], @cacheable/[email protected], @cacheable/[email protected], [email protected], @cacheable/[email protected], @cacheable/[email protected], and [email protected]. Roll each one back to the immediately preceding version listed in the affected-packages table above.

Is this the same Shai-Hulud worm as previous npm attacks?

Yes. The exfiltration repositories carry the description string Shai-Hulud: Here We Go Again, and the payload shares hook filenames and the pinned Bun 1.3.13 runtime with the April 2026 Mini Shai-Hulud campaign against PyPI. No named threat actor has been publicly identified for this wave.

How do I know if my environment was affected?

Grep your lockfiles and CI logs for the malicious versions, search for setup.mjs and Math_Symbol.js on disk, and look for Bun process execution or bun-dl-* directories on hosts that do not use Bun. Also check whether any repository under your org was created with the Shai-Hulud: Here We Go Again description.

What credentials does the malware steal?

AWS, GCP, and Azure credentials including instance metadata, Kubernetes service account tokens, HashiCorp Vault tokens, npm tokens, GitHub personal access tokens and Actions OIDC tokens harvested from runner memory, Stripe and Slack keys, and any secret matched by a TruffleHog-style regex sweep across roughly 200 file patterns. Rotate all of them.

Can I be infected without running npm install?

Yes. The campaign plants a SessionStart hook in .claude/settings.json and a folderOpen task in .vscode/tasks.json, either of which executes the loader when a developer or AI coding agent opens the project directory.

What is the fastest way to stop the worm from spreading further?

Revoke npm automation tokens and disable trusted publishing for affected repositories immediately, ahead of triage. Every stolen publishing token is a propagation hop, and the extortion string in the payload warning against revocation carries no technical weight.

References

  1. Aikido Security. August 2026. Keyv and friends compromised in npm supply chain attack. Aikido.
  2. Endor Labs. August 4, 2026. npm malware compromises keyv and cacheable with 500M weekly downloads and spreads to hundreds of packages. Endor Labs.
  3. JFrog Security Research. August 2026. Major Shai-Hulud campaign strikes npm again, affecting keyv and 400+ packages. JFrog.
  4. Kodem Security. April 30, 2026. Mini Shai-Hulud Strikes PyTorch Lightning and Intercom Client: Inside the Cross-Ecosystem Supply Chain Attack. Kodem Security.
  5. OX Security. August 2026. A Massive Shai-Hulud Campaign Hits npm: 440+ Packages Compromised, Over 2B Monthly Downloads. OX Security.
  6. SafeDep. August 2026. npm Worm Poisons 400+ Packages Across Nine Organisations. SafeDep.
  7. Snyk. August 2026. Inside the keyv npm compromise: preinstall malware, trusted provenance, and IDE hooks. Snyk.
  8. Socket. August 2026. Popular npm packages in the keyv and cacheable namespaces compromised in active supply chain attack. Socket.
  9. The Hacker News. August 2026. Keyv-Linked npm Worm Poisons Hundreds of Packages, Plants Claude Code and VS Code Hooks. The Hacker News.
  10. Wiz. August 2026. keyv and cacheable npm package hijacked in supply chain attack. Wiz.
Table of contents

Related blogs

Reachability Predicts. Runtime Proves.

Reachability Predicts. Runtime Proves.

Reachability tells you code can execute. Runtime tells you it did.

July 20, 2026

7

When the AI Edits Its Own Trust Boundary: Remote Code Execution Vulnerability in AWS's Agentic IDE

When the AI Edits Its Own Trust Boundary: Remote Code Execution Vulnerability in AWS's Agentic IDE

We found a vulnerability in Kiro, AWS's agentic IDE, that breaks this promise. By planting hidden instructions in a web page Kiro reads, an attacker can make Kiro rewrite its own MCP (Model Context Protocol) server configuration file and gain arbitrary code execution on the developer's machine.

July 19, 2026

14

What Is an SBOM? (Software Bill of Materials)

What Is an SBOM? (Software Bill of Materials)

A software bill of materials (SBOM) inventories every component in your software. What an SBOM includes, why it matters, and how runtime makes it actionable.

July 15, 2026

5

Stop the waste.
Protect your environment with Kodem.

A Primer on Runtime Intelligence

See how Kodem's cutting-edge sensor technology revolutionizes application monitoring at the kernel level.

5.1k
Applications covered
1.1m
False positives eliminated
4.8k
Triage hours reduced

Platform Overview Video

Watch our short platform overview video to see how Kodem discovers real security risks in your code at runtime.

5.1k
Applications covered
1.1m
False positives eliminated
4.8k
Triage hours reduced

The State of the Application Security Workflow

This report aims to equip readers with actionable insights that can help future-proof their security programs. Kodem, the publisher of this report, purpose built a platform that bridges these gaps by unifying shift-left strategies with runtime monitoring and protection.

3D book mockup of Kodem's State of the Application Security Workflow 2025 report

Get real-time insights across the full stack…code, containers, OS, and memory

Watch how Kodem’s runtime security platform detects and blocks attacks before they cause damage. No guesswork. Just precise, automated protection.

Kodem issues list with a magnified view of insight icons: runtime, ingress, and exploitability
Combined author
Kodem Security Research Team
Publish date

0 min read

Vulnerabilities

SCA Security