The keyv and cacheable npm Supply Chain Attack: Inside the Mini Shai-Hulud Campaign
TL;DR
What happened: On August 4, 2026, an attacker took over the GitHub account of Jared Wray (jaredwray), the maintainer behind
keyvandcacheable, and published malicious versions of ten widely used npm caching packages.Affected packages:
keyv,cacheable-request,cache-manager,@cacheable/utils,flat-cache,file-entry-cache,cacheable,@cacheable/memory, and@cacheable/node-cache— the nine packages maintained directly by the compromised account, plus hundreds of additional downstream packages swept up through worm-like propagation. Counts are still climbing, with trackers reporting anywhere from roughly 400 to over 2,200 affected packages and artifacts.Scale: ten core packages representing over two billion combined monthly downloads across the affected ecosystem.
Risk: a credential-stealing dropper that harvests npm, GitHub, AWS, and HashiCorp Vault secrets. It exfiltrates them to the domain
npm-cache[.]comand to GitHub repositories set up under compromised identities, and it looks up its command-and-control domain from an Ethereum smart contract so the operator can change infrastructure without touching the payload.Chainguard customers: Not affected. Chainguard uses multiple layers of defense to ensure malicious packages are unavailable for consumption. Malware and greyware scanning, along with configurable cooldowns, kept these packages from reaching customers.
What to do: check your lockfiles for the versions listed below and rotate any exposed credentials immediately.
At around 9:00 UTC on August 4, 2026, an attacker used a compromised GitHub account (jaredwray) to push malicious commits directly to the main branch of the keyv repository, then cut a new release. Within minutes, the same payload appeared across eight more packages maintained by the same account. The investigation is ongoing, and the malware has since turned up in hundreds of unrelated packages as well.
Affected packages
Five core packages have confirmed malicious versions: keyv, cache-manager, cacheable-request, @cacheable/utils, and flat-cache. Four more from the same maintainer are confirmed affected, but no bad version number has been published for them yet: cacheable, file-entry-cache, @cacheable/memory, and @cacheable/node-cache. Beyond those nine, the worm's reach is real but hard to pin down. Reported figures range from roughly 400 to over 2,200 packages, and no source has published a complete, independently verified list. Don't trust the latest tag; check the exact version installed against your lockfile.
Confirmed malicious versions:
keyv@6.0.0cacheable-request@13.0.20cache-manager@7.2.10@cacheable/utils@2.5.1flat-cache@6.1.24
Confirmed affected, exact malicious versions have not yet been published, so treat any version published on or after August 4, 2026, as suspect:
file-entry-cachecacheable@cacheable/memory@cacheable/node-cache
npm restored keyv@5.6.0, flat-cache@6.1.23, cache-manager@7.2.9, and cacheable-request@13.0.19 as latest, confirming these versions are clean. No confirmed clean version has been published yet for file-entry-cache, cacheable, @cacheable/utils, @cacheable/memory, or @cacheable/node-cache. The @keyv/* adapter packages and the Keyv 5.x line were never affected.
One caveat changes what "clean version" means here. The GitHub source repository remained poisoned even after these versions were restored to latest. The payload files were staged across all 19 packages in the keyv monorepo, so a release cut from that tree, even one published by the legitimate maintainer account, could still carry the payload until the source itself is cleaned. Pinning to a known clean version is only a point-in-time fix, not a permanent one.
These packages are also confirmed affected, sharing the same payload and file hashes: @ornikar namespace (28 packages), @qlik/embed-react, @qlik/embed-runtime, @qlik/embed-web-components, @qlik/runtime-module-loader, @nebula.js/nucleus, @hubsync/web-sdk-react, and several others.
What the malware does
Each affected package received two new files, setup.mjs and Math_Symbol.js, along with a preinstall entry in package.json. Running npm install against an affected version triggers setup.mjs automatically, before a developer ever imports the package.
Credential harvesting: the payload reads npm tokens from
.npmrc, GitHub tokens (personal access tokens, session tokens, and OpenID Connect (OIDC) tokens), cloud credentials including AWS keys, Kubernetes service account tokens, database connection strings, and other private-key material, and HashiCorp Vault tokens from theVAULT_TOKENenvironment variable, falling back to an HTTP request if that variable is not set.Credential validation: before exfiltrating anything, the malware validates stolen npm tokens against npm's
registry.npmjs.org/-/whoamiendpoint in real time, filtering out tokens that are already dead.Self-propagation: the payload carries its own logic for modifying, version-bumping, and republishing packages under any npm identity it successfully validates, which is what turns a single compromised maintainer account into a spreading worm rather than a contained incident.
Exfiltration through blockchain infrastructure: stolen data is sent to a dedicated domain and to GitHub repositories created under compromised identities. The malware separately queries an Ethereum smart contract through public RPC endpoints to dynamically resolve its current command-and-control domain, letting the operator rotate infrastructure without changing the payload. That kind of RPC traffic is common in developer environments and easy to miss at the network layer.
Runtime evasion: rather than running under Node.js, the payload downloads the Bun JavaScript runtime from Bun's official GitHub releases and executes through it instead. Because the download comes from a legitimate GitHub URL rather than attacker infrastructure, there's no suspicious domain for egress monitoring or reputation checks to flag.
IDE-triggered execution:
.claude/settings.jsoncontains aSessionStarthook that calls.vscode/setup.mjs, and.vscode/tasks.jsoncontains anEnvironment Setuptask withrunOn: folderOpenthat calls.claude/setup.mjs. The two files cross-reference each other. Opening a cloned copy of thekeyvrepository in VS Code or Claude Code can trigger this chain, with nonpm installrequired. Neither editor runs it unconditionally, though: VS Code blocks automatic tasks in an untrusted workspace by default, and Claude Code applies the same workspace-trust check to repo-supplied settings.
Indicators of compromise
If you installed any affected version (see Affected packages above), treat your environment as compromised.
Network indicators:
npm-cache.com (104.21.35.216)eth-mainnet.nodereal.io (35.175.164.77)go.getblock.io (185.44.207.215)eth.llamarpc.com (172.67.167.200)pypi-get.comjs-mirror.com
File hashes:
Math_Symbol.js/math_init.js: 35a672cf34b996b91f3e1c28cbf3a05a37e036e4setup.mjs(in.claude):686aa40d0fc22c8d569494543a0f891f359f2f99setup.mjs(in.vscode):f525d52ceb966516686b482d3dc0137028cc6a63
Hash values are still being cross-verified across research teams. Some published hashes use different algorithms for what appear to be the same files, so treat file names and paths as the more reliable indicator until this settles.
Package-level indicators:
Injected manifest entry —
package.jsongains"preinstall": "node setup.mjs"Dropper file —
node_modules/<package>/setup.mjs, present in every affected packagePayload file —
node_modules/keyv/Math_Symbol.jsand.claude/math_init.jsin a compromised repo checkout (same payload, identical hash, different names depending on the trigger path)IDE hook files —
.claude/settings.json, .vscode/tasks.json committed in the compromised repo
Other artifacts:
Bun runtime staging directory —
/tmp/bun-dl-*/String constant —
IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients
How to check if you're affected
A lockfile match alone warrants investigation, even if the package appears unused at runtime.
List installed versions of the affected packages
npm ls keyv cacheable-request cache-manager @cacheable/utils flat-cache file-entry-cache cacheable @cacheable/memory @cacheable/node-cache 2>/dev/null
2. Check your lockfiles for affected packages and versions
grep -E "keyv|cacheable-request|cache-manager|@cacheable/utils|flat-cache|file-entry-cache|cacheable|@cacheable/memory|@cacheable/node-cache" package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null
3. Search node_modules for the malicious file artifacts
find . -path "*/node_modules/*" \( -name "Math_Symbol.js" -o -name "setup.mjs" \) 2>/dev/nul
4. Search package.json files for the injected preinstall hook
grep -R '"preinstall": "node setup.mjs"' --include=package.json . 2>/dev/null
5. Check any cloned repo checkouts for the IDE-trigger hook files, separately from your dependency tree
find . \( -path "*/.claude/settings.json" -o -path "*/.vscode/tasks.json" -o -path "*/.claude/math_init.js" \) 2>/dev/null
Immediate steps if you're affected
Don't open a clone of an affected repository in VS Code or Claude Code until you've inspected
.claude/settings.jsonand.vscode/tasks.jsonfor injected hooks — opening the workspace can trigger the payload independent of anynpm install.Do not rotate credentials before checking for persistence artifacts. No destructive trigger has been confirmed for this variant, but prior versions of this malware lineage have shipped exactly this kind of token-revocation-triggered logic. Check for persistence artifacts first.
Downgrade to a known-clean version. npm restored
keyv@5.6.0,flat-cache@6.1.23,cache-manager@7.2.9, andcacheable-request@13.0.19aslatest, and these are confirmed clean. No confirmed clean version exists yet forfile-entry-cache,cacheable,@cacheable/utils,@cacheable/memory, or@cacheable/node-cache, so pin those to a version published before August 4, 2026, that you've verified independently. The source repository remained poisoned even after clean versions were restored, so treat any pinned version as a point-in-time fix, not a permanent one. Confirm withnpm lsafter downgrading.Rotate exposed credentials, in order: npm publish tokens, GitHub personal access tokens and OIDC tokens, Amazon Web Services (AWS) access keys and session tokens, Kubernetes service account tokens, database credentials, and HashiCorp Vault tokens.
Block egress to
npm-cache.com,pypi-get.com, andjs-mirror.com, and treat unexpected outbound traffic to Ethereum RPC endpoints from build and CI/CD environments as suspicious.Audit cloud and source control logs for unauthorized access following package installation, particularly any npm publishes you did not initiate.
Rebuild affected systems rather than relying on package removal alone. Any computer that ran the
preinstallhook should be treated as potentially compromised.Upgrade to npm 12 or newer, or install with
--ignore-scripts. npm 12 blocks unapproved dependency lifecycle scripts by default. Earlier npm clients and other install paths that still allow lifecycle scripts remain exposed to this kind of attack.
Why Chainguard customers were protected
Chainguard Libraries customers were never exposed to this campaign. Not a single customer pulled an affected version of these packages.
Chainguard Libraries applies multiple layers of protection. For the newest and most popular packages, Chainguard builds from verified source. Across the catalog more broadly, malware and greyware scanning and cooldown periods run before any new release becomes available to customers. In this case, scanning and cooldowns were the layers that gave Chainguard the opportunity to catch this campaign before it could reach customers, regardless of how the malicious code entered the package in the first place.
That's the same protection model that's kept Chainguard customers out of every major supply chain incident this year. While the rest of the industry spends the day on triage and credential rotation, Chainguard customers keep shipping.
The bigger picture
This is the third documented wave of the same Shai-Hulud lineage toolkit in 2026. Semgrep traced the identical hook files, the same setup.mjs filename, and the same Bun 1.3.13 download to an April 2026 compromise of the PyTorch Lightning PyPI package. The same lineage hit the @antv ecosystem in May, tied to the TeamPCP group. keyv and cacheable are the latest, in August. A few takeaways stand out.
This isn't a new pattern showing up again. It's the identical toolkit, reused across ecosystems, from PyPI to npm, over a period of months.
Attackers are diversifying their command-and-control infrastructure to blend in with normal developer traffic. This wave's use of Ethereum RPC endpoints follows the same logic as prior waves' use of C2 infrastructure disguised as OpenTelemetry trace ingestion.
Provenance and attestation prove who published a package, not that the publisher's environment was trustworthy. A compromised maintainer account still produces a validly signed, still-malicious release.
No named actor has claimed this campaign. The initial access vector is still unknown, and neither the maintainer, npm, nor GitHub has issued a public statement as of this writing.
Get in touch with our team to learn how Chainguard can protect your software supply chain.
Sources
Cybersecurity News — Keyv npm Package with 127M Weekly Downloads Compromised in Shai-Hulud Attack
Microsoft Threat Intelligence — confirmed compromised package list
Want to learn more about Chainguard?
Attacks like this one, a single compromised maintainer account turning into a self-propagating worm across hundreds of packages, are becoming a regular occurrence in the open source ecosystem. Chainguard Libraries lets your team turn off live access to npm, PyPI, and Maven Central and replace it with secure-by-default endpoints instead. Our dependency catalog applies multiple layers of security controls, including building the newest and most popular packages from verified source, malware and greyware scanning, and cooldowns, so a package only becomes available to your team after it's cleared all of them.
Learn more about Chainguard Libraries and how it protects your software supply chain.
Share this article
Related articles
- security
Why AI-assisted attacks made software supply chain security its own category
Anushka Iyer, Product Marketing Manager
- security
Why zero CVEs matters in mobile airgapped deployments
Mike Barretta, Senior Manager, Solutions Engineering
- security
Mitigating WordPress attacks with containers
Adrian Mouat, Staff DevRel Engineer
- security
Responding to the Five Eyes guidance on AI and cyber risk
Chris Carty, Enterprise Solutions Engineer
- security
AsyncAPI supply chain compromise: npm packages backdoored via GitHub Actions "pwn request" (July 2026)
Quincy Castro, CISO
- security
We're putting our security to the test, and we want your help
Quincy Castro, CISO, and Alex Burrage, Director, Product Security