Keyv and friends compromised in active Shai-Hulud supply chain attack
Shai-Hulud: A Massive Supply Chain Breach of Keyv and Caching Ecosystem
On August 4, 2026, a sophisticated supply chain attack—dubbed "Shai-Hulud"—targeted the GitHub account of a prominent maintainer. This breach allowed attackers to inject a credential-stealing worm into the keyv library (which sees million weekly downloads) and several other high-traffic caching utilities.
Because the attacker had direct access to the maintainer's account, they pushed malicious code to the main branch and triggered new releases. Consequently, these poisoned versions were published to the npm registry with valid provenance, having been signed by GitHub Actions.
🚨 The Blast Radius
The attack swept through an entire family of caching tools. The following table outlines the primary compromised packages:
| Package Name | Compromised Version | Monthly Downloads |
|---|---|---|
keyv | 6.0.0 | 604M |
flat-cache | 6.1.24 | 580M |
file-entry-cache | 11.1.6 | 571M |
cacheable-request | 13.0.20 | 137M |
cacheable | 2.5.1 | 30M |
@cacheable/utils | 2.5.1 | 34M |
@cacheable/memory | 2.2.1 | 28M |
cache-manager | 7.2.10 | 16M |
@cacheable/node-cache | 3.1.2 | 6M |
@cacheable/net | 2.1.1 | 3.7K |
ecto | 5.0.1 | 4.5K |
Update (Aug 4, 2026, 13:37 CEST): The worm has spread aggressively. At least 434 packages across 1,381 versions are now infected, totaling over monthly installations.
Other major organizations have also seen their packages compromised via this worm, including:
@deliveroo/reevent(1.0.1)@or-sdk/invitations(1.4.9)@picsart/ai-sdk(3.32.2)@qlik/embed-runtime(1.6.4)picasso.js(2.11.6)
🛠️ Technical Analysis: How it Works
The attack utilizes a multi-stage execution chain to bypass simple detection.
1. The Infection Vector
Every affected package was modified to include two new files: setup.mjs and Math_Symbol.js. The package.json was updated with a preinstall hook:
"scripts": {
"preinstall": "node setup.mjs"
}
2. The Execution Flow
When a user runs npm install, the setup.mjs script executes automatically. Its sole purpose is to fetch the Bun JavaScript runtime and use it to launch the primary payload.
The execution command looks like this:
execFileSync( bun_binary , ['script_dir / Math_Symbol .js'], { stdio: 'inherit', cwd: script_dir })
3. The Payload
Math_Symbol.js is a heavily obfuscated file () that functions as both a credential harvester and a worm. It steals secrets and exfiltrates them to a public GitHub repository titled “Shai-Hulud: Here We Go Again.”
🔍 What is Being Stolen?
The payload contains specialized extractors for various secret stores:
📦 Package Managers & Version Control
- npm Tokens: Scans
~/.npmrcand the filesystem forauthTokenvalues. It validates tokens againstregistry.npmjs[.]org/-/whoamibefore stealing them. - GitHub Tokens: Targets classic PATs (
ghp_...) from~/.config/gh/hosts.yml, environment variables, and filesystem scans.- Special Case: On GitHub Actions runners, it dumps the entire secret store by reading the runner process memory.
- OIDC Theft: Steals
ACTIONS_ID_TOKEN_REQUEST_TOKENandACTIONS_ID_TOKEN_REQUEST_URLto hijack npm publishing rights.
☁️ Cloud & Infrastructure
- AWS Credentials:
- Parses
~/.aws/credentialsand~/.aws/config. - Checks
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, andAWS_SESSION_TOKEN. - Queries the EC2 Instance Metadata Service (IMDSv2 IMDSv1 fallback) and the ECS metadata endpoint.
- Uses
secretsmanager:ListSecretsto drain AWS Secrets Manager across regions.
- Parses
- Kubernetes: Reads the service account token and CA cert from
/var/run/secrets/kubernetes.io/serviceaccount/to query the K8s API for all namespace secrets. - HashiCorp Vault: Checks six sources (Env vars,
~/.vault-token, GitHub runner paths, K8s JWT, and AWS IAM auth). Once a token is found, it enumerates all KV v1 and v2 stores.
🔑 Third-Party & Local Files
- API Keys: Specifically targets Stripe (
sk_,pk_) and Slack (xox[baprs]-...) tokens. - Broad Filesystem Scan: A platform-aware scanner uses glob patterns to find:
-
.env,.env.*, and.envrc - Private keys (
*.pem,*.key,*.p12,*.pfx,*.jks) - SSH keys (
id_rsa,id_ed25519,.ssh/config) - Terraform state and
.tfvars - Docker
config.json - KeePass databases (
*.kdbx) - VPN configs (
*.ovpn) - IDE settings (
.vscode/tasks.json,.claude/settings.json)
-
Files larger than 5 MB are ignored to avoid detection. A regex engine also scans for database connection strings and Azure storage keys.
🖼️ Visual Evidence


Exfiltration Process: Once the credentials are harvested, the payload encrypts the entire bundle before sending it to the attacker's repository.