Skip to content
Security Article

Shai-Hulud Returns, and Provenance Signed the Malware

The worm behind the keyv compromise rode a legitimate GitHub Actions pipeline straight past npm's newest defenses.

Emeka Okafor
Emeka Okafor
Security Editor · Aug 4, 2026 · 5 min read
Shai-Hulud Returns, and Provenance Signed the Malware

On the morning of August 4, a compromised GitHub maintainer account pushed malicious commits to the keyv repository and cut releases. Within hours, poisoned versions of keyv, flat-cache, file-entry-cache, cacheable, cache-manager, and cacheable-request were live on npm — one maintainer's account, a package family pulling on the order of two billion installs a month. You almost certainly don't depend on these directly, and that's the point: ESLint pulls in file-entry-cache, which pulls in flat-cache; got pulls in cacheable-request, which pulls in keyv. This is the plumbing under half the JavaScript ecosystem.

It's the third major Shai-Hulud wave in under a year, and the most instructive one yet — because this time the malware shipped with valid provenance.

The worm, briefly

The mechanics are familiar if you followed the September and November 2025 waves. The poisoned releases add a preinstall script that runs a dropper (setup.mjs), which fetches Bun 1.3.13 from its official GitHub releases and hands off to a ~728 KB compiled bundle (Math_Symbol.js). That payload sweeps the machine or CI runner it lands on: npm tokens from .npmrc, GitHub PATs and OIDC tokens, AWS keys, Kubernetes service accounts, Vault tokens, SSH keys, .env files, even GitHub Actions runner memory. Loot goes to attacker-created GitHub repos bearing the description "Shai-Hulud: Here We Go Again."

Then the worm part: with each stolen npm token, it republishes poisoned versions of that victim's packages, and the blast radius compounds. Counts vary by vendor — SafeDep independently verified 353 poisoned versions across 79 packages, while Aikido, who broke the story, counted 868 packages across 1,381 versions — but confirmed secondary victims include packages from Qlik, Deliveroo, and Picsart. npm was restoring clean latest tags for the core packages by early afternoon UTC, which is genuinely fast. It was still hours of exposure at this download volume.

Provenance signed the malware

Here's the detail that should reframe how you think about supply chain tooling: the attacker didn't publish from a laptop with a stolen token. They pushed to the main branch and let the project's own GitHub Actions release workflow do the publishing. The malicious versions carried valid OIDC and SLSA provenance. The commits wore the green "verified" badge, authored by github-actions[bot].

Every attestation was true. The package really was built from that repo, by that workflow, on GitHub's infrastructure. Provenance answers "was this built where it claims?" — it has never answered "did the maintainer intend this?" The ecosystem spent the last year acting like those were the same question.

There's an uncomfortable causality here. After the September 2025 wave, npm's remediation push — deprecating classic tokens, short-lived granular tokens, trusted publishing via OIDC — deliberately moved trust from npm credentials to GitHub accounts and their CI pipelines. That was the right call against token theft. But it consolidated the target: own the GitHub account and the release pipeline launders your malware into a signed, provenance-attested artifact. Attackers read the same changelogs we do, and they adapted in months while npm clients that still run lifecycle scripts by default will linger in CI images for years.

Your AI tooling is persistence surface now

The genuinely new trick in this wave: the malicious commits planted a SessionStart hook in .claude/settings.json and a runOn: folderOpen task in .vscode/tasks.json, each pointing at a copy of the dropper. Clone an infected repo, open it in VS Code or start a Claude Code session, and the malware re-executes — no npm install required. Workspace trust prompts are the only thing standing in the way, and be honest: when did you last read one before clicking "Yes, I trust the authors"? Semgrep spotted the same hook pattern in an April PyPI compromise of the lightning package, so this is now standard Shai-Hulud tradecraft, not a one-off. Agent config files deserve the same code-review scrutiny as CI workflows, starting today.

Nastier still, the payload installs a token-revocation watcher. Rotating a stolen credential is the trigger: revoke first and an attacker-supplied handler runs locally. The textbook incident response — "rotate everything immediately" — is booby-trapped. Clean the machine, then rotate.

What to do this morning

Check what you actually resolved, not what's tagged now — tags were fixed within hours, your lockfile wasn't:

npm ls keyv flat-cache file-entry-cache cacheable cache-manager cacheable-request
find . -name "Math_Symbol.js" -o -path "*/.claude/setup.mjs" -o -path "*/.vscode/setup.mjs"

If a poisoned version installed anywhere — laptop or runner — treat that environment as credential-exposed. Rebuild it, remove the watcher, then rotate npm, GitHub, cloud, and Vault credentials, in that order. Wiz's published IOCs include the exfil domain npm-cache[.]com and Bun artifacts under /tmp/bun-dl-*.

Structurally, the defenses that worked are the boring ones. pnpm hasn't run dependency lifecycle scripts by default since v10, which neuters the preinstall vector outright; its minimumReleaseAge setting would have kept these versions out of your tree entirely, since every Shai-Hulud wave has been caught within hours. On npm, npm config set ignore-scripts true plus an explicit allowlist for the handful of native modules that genuinely need builds gets you most of the way. Yes, it's friction. It's less friction than an incident.

This keeps happening for a reason

Three waves — hundreds of packages in September 2025, roughly 800 in November with 27,000 exfiltration repos, now this — and the pattern is stable: the worm exploits install-time code execution and hyper-concentrated maintainership, and each ecosystem fix hardens a layer the next wave routes around. Token hygiene didn't stop it; provenance actively camouflaged it.

The honest conclusion is that provenance and trusted publishing are authentication features being marketed as integrity features. They're worth having — they killed a real attack class — but they verify the pipeline, not the person, and a maintainer account with a billion downstream installs and no mandatory review on its release branch is still a single point of failure. Until registries impose publish cooldowns by default and installers stop executing arbitrary code on install, Shai-Hulud — or whoever inherits the playbook — will be back. The worm's own exfil repos said it plainly: here we go again.

Sources & further reading

  1. Keyv and friends compromised in active Shai-Hulud supply chain attack — aikido.dev
  2. Keyv-Linked npm Worm Poisons Hundreds of Packages, Plants Claude Code and VS Code Hooks — thehackernews.com
  3. keyv and cacheable npm Package Hijacked in Supply Chain Attack — wiz.io
  4. The Shai-Hulud 2.0 npm worm: analysis, and what you need to know — securitylabs.datadoghq.com
  5. Shai-Hulud Worm Compromises npm Ecosystem in Supply Chain Attack — unit42.paloaltonetworks.com
Emeka Okafor
Written by
Emeka Okafor · Security Editor

Emeka has spent over a decade tracking threat actors, vulnerability disclosures, and the evolving landscape of application security, bringing a sharp continent-spanning perspective to his reporting. He's known for translating dense CVE advisories into clear, actionable context that developers and security teams alike actually read.

Discussion 0

Join the discussion

Sign in or create an account to comment and vote.

No comments yet

Be the first to weigh in.

Related Reading