Cybersecurity

Shai-Hulud npm Worm Hits Mistral SDK; Provenance No Defense

A self-spreading npm worm infected 170+ packages including Mistral's SDK on May 11, 2026, published with valid SLSA provenance and stealing Claude Code configs and cloud creds.

Shai-Hulud npm Worm Hits Mistral SDK; Provenance No Defense — article cover
On this page6 SECTIONS
  1. The Attack Chain: From One Fork to 170+ Packages
  2. The Payload Hunts AI Developers
  3. Why Valid Provenance Didn’t Help
  4. Self-Propagation and the Revocation Trap
  5. What Teams Should Do Now
  6. Sources

On May 11, 2026, a self-spreading supply-chain worm tore through the npm ecosystem. StepSecurity, which tracks the campaign under the name Mini Shai-Hulud and attributes it to a group it calls TeamPCP, described the model plainly: steal CI/CD credentials from one maintainer, then automatically publish infected versions of every package that maintainer controls. TanStack’s post-mortem counted 84 malicious versions across 42 @tanstack/* packages, and the victim list grew past 170 — including Mistral AI’s official SDKs, with spillover onto PyPI.

The detail that should stop every supply-chain program cold: the malicious tarballs carried cryptographically valid SLSA provenance. The worm did not forge anything. It hijacked the projects’ own release pipelines, so the attestations accurately named the real builder, repository, and workflow.

The Attack Chain: From One Fork to 170+ Packages

The timeline is tight. The attacker’s GitHub account (voicproducoes) was created back on March 19 and sat dormant. On May 10 it forked TanStack/router and staged the payload inside a commit. On May 11 the poisoned versions went out in a rapid series: @tanstack/react-router 1.169.5 and 1.169.8, @tanstack/history 1.161.9 and 1.161.12, and dozens more. From there the worm jumped namespaces — @opensearch-project/opensearch 3.6.2, @uipath/apollo-core 5.9.2, @draftlab/auth, safe-action.

Mistral’s exposure was confirmed in its own repo: issue #217 on mistralai/client-ts, opened May 11, flagged @mistralai/mistralai 2.2.4 as compromised. StepSecurity’s list puts the affected versions at 2.2.3 and 2.2.4, with @mistralai/mistralai-azure and @mistralai/mistralai-gcp hit at 1.7.2/1.7.3. On the Python side, mistralai 2.4.6 and guardrails-ai 0.10.1 on PyPI were caught in the same wave.

The injection itself is simple enough to audit for: each poisoned tarball gained an optionalDependencies entry plus a 2.3 MB obfuscated router_init.js at the package root. Tarball size ballooned from roughly 190 KB to 905 KB — a 3.7x jump. Size anomalies remain the cheapest signal you can alert on.

The Payload Hunts AI Developers

The payload’s target list reads like a spec of the modern AI developer machine. It scrapes the GitHub Actions Runner.Worker process through /proc memory to pull the ambient OIDC token. It steals AWS IMDSv2, ECS, and Vault credentials. It harvests more than 100 file paths: cloud keys, SSH keys, crypto wallets, Claude and Kiro configuration files, shell history. It installs persistence in Claude Code, VS Code, and OS-level services, and injects a fake “CodeQL Analysis” workflow that uses toJSON(secrets) to dump every secret in the repository in one shot.

To dodge Node.js-oriented security tooling, the payload runs under Bun 1.3.13. Exfiltration is equally deliberate: part flows through Session’s CDN, part through GitHub dead-drop commits authored as claude@users.noreply.github.com on Dependabot-lookalike branches named with Dune terminology. Whoever built this knows exactly what sits on a developer machine in 2026.

Why Valid Provenance Didn’t Help

Andrew McNamara of Red Hat later dissected the three-step chain on the official SLSA blog. First, TanStack’s bundle-size.yml used the pull_request_target trigger, executing fork-contributed code in the trusted repository’s security context. Second, the malicious code poisoned the pnpm package store under a cache key later restored by the legitimate release workflow — GitHub Actions shares cache scope across trigger types. Third, with id-token: write permission, the attacker scraped the OIDC token from runner memory and published straight to npm, bypassing the workflow’s conditional publish logic.

The conclusion is blunt: a signed artifact is not necessarily a trustworthy one. Provenance records what happened, not whether it was good enough. npm’s built-in provenance effectively reaches SLSA Build L2; this attack broke all three L3 isolation properties at once — a shared cache was poisoned, the signing identity was exposed, and attacker code persisted across builds. There was also a monitoring gap: both malicious CI runs finished with status “failure” yet still published packages. Alerting on publish events from failed runs could have cut the exposure window by hours.

Self-Propagation and the Revocation Trap

The worm looks for npm tokens with bypass_2fa, enumerates every package the maintainer controls, and uses OIDC exchange to publish infected versions — exponential spread by design. Nastier still, one token carries the description “IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner.” The correct order is isolate and image first, rotate credentials second; a hasty revoke may trigger the destructive wipe.

What Teams Should Do Now

If you installed an affected version, assume every secret in that environment is compromised. Downgrade to the last clean version, delete node_modules, and reinstall. Rotate npm tokens, GitHub PATs, cloud keys, and SSH keys; move wallet funds. Audit repositories for injected workflows and review CI runs after 2026-05-11T19:20Z. The structural fixes are concrete: gate or drop pull_request_target, apply least-privilege permissions, pin actions by digest, and treat shared caches as trust boundaries. Finally, treat agent configuration files as secrets — the first things this worm went for were Claude Code and Kiro configs. If you run coding agents in your pipeline, sandboxing and approval policies are the next layer of defense worth building (see our earlier look at how OpenAI deploys Codex safely).

Sources

AI-assisted summary compiled from the sources above, reviewed by a human before publishing.

FOUND_THIS_USEFUL?

Support more practical AI articles, tutorials, and build notes.

BUY_ME_A_COFFEE
SHAREXEMAIL