CriticalSupply chainOngoing

Shai-Hulud npm worm: check installs, rotate tokens, kill rogue runners

A self-replicating npm worm runs at install time, steals developer and CI secrets, and registers rogue GitHub runners. New waves hit from April to August 2026.

Published
Updated
Exploitation
Yes, mass compromise of npm packages and developer machines

What happened

Shai-Hulud is a family of self-replicating npm malware. Once it steals a maintainer’s npm token, it publishes poisoned versions of every package that maintainer can publish, so each infection seeds the next.

  • September 2025: the first wave compromised hundreds of npm packages. Counts vary by vendor (Sonatype).
  • 21 to 23 November 2025: the second wave, “Shai-Hulud 2.0” or “The Second Coming”. Wiz said it exposed secrets from more than 25,000 developers in three days (The Register). Researchers later counted roughly 400,000 raw secrets across about 30,000 GitHub repositories. More than 60% of leaked npm tokens were still valid a week later.
  • June 2026: the “Miasma” wave hit about 300 npm components (Sonatype).
  • Late April 2026 onward: the “Mini Shai-Hulud” campaign, active since late April according to Tenable (Tenable). It included the May 2026 TanStack compromise that reached GitHub and Grafana (see our TanStack advisory), and an August 2026 wave in the keyv and cacheable packages that hit 2,225 package versions (Sonatype).

How the 2.0 variant works

  1. A poisoned package adds a preinstall script, node setup_bun.js, so it runs during npm install, before you review or test anything. It runs even if the install later fails.
  2. setup_bun.js installs the Bun runtime if it is missing and launches bun_environment.js.
  3. The payload searches the machine for credentials, reportedly using the legitimate TruffleHog scanner: npm tokens, GitHub tokens, and AWS, GCP and Azure keys.
  4. It registers the machine as a self-hosted GitHub Actions runner named SHA1Hulud, which gives the attacker a way back in, and publishes stolen secrets to public GitHub repositories whose description references “Sha1-Hulud: The Second Coming”.
  5. With a stolen npm token, it republishes the victim’s own packages with the same payload.

Indicators are summarised from vendor research and a public Sigma detection rule (detection.fyi Sigma rule). Later waves change file names, so check your security vendor’s current indicator list as well.

Am I affected?

Run these in each project and on each developer machine and CI image:

bash
# Loader files from the 2.0 wave inside installed dependencies
find . -path '*/node_modules/*' \( -name setup_bun.js -o -name bun_environment.js \) 2>/dev/null

# The preinstall hook in installed package manifests
grep -rl --include=package.json '"preinstall": *"node setup_bun.js"' node_modules 2>/dev/null

# Leftovers in your home directory (Bun installed by the payload, stray copies)
find ~ -maxdepth 4 -name bun_environment.js 2>/dev/null

Then check GitHub, where the worm leaves its own traces:

bash
# Repositories in your account whose description mentions Hulud
gh repo list YOUR_USER --limit 1000 --json name,description \
  --jq '.[] | select((.description // "") | test("hulud"; "i")) | .name'

# Self-hosted runners registered on an organisation
gh api /orgs/YOUR_ORG/actions/runners --jq '.runners[] | [.name, .os, .status] | @tsv'

Any runner named SHA1Hulud, or any runner you cannot account for, is a confirmed compromise.

What to do now

  1. Isolate and rotate. On any machine or runner that installed an affected version, rotate npm tokens, GitHub personal access tokens and SSH keys, cloud keys, and every CI secret that machine could read. Revoke first, then reissue.
  2. Remove persistence. Delete unknown self-hosted runners and attacker-created repositories, and review recent GitHub Actions workflow changes.
  3. Reinstall cleanly. Delete node_modules, pin to known-good versions in your lockfile, and run npm ci, which installs exactly what the lockfile says.
  4. Turn install scripts off by default, and allow them only for the packages that need them:
bash
npm config set ignore-scripts true
  1. Stop publishing with long-lived tokens. Use npm trusted publishing (OIDC from your CI) so there is no reusable token to steal, and require two-factor authentication for maintainers.
  2. Add a cooling-off period. Most poisoned versions are caught within hours or days. A minimum release age before your tooling adopts a new version, which pnpm supports with minimumReleaseAge, avoids most of them.

Why this is part of the Vulnpocalypse

The Vulnpocalypse is usually told as a story about AI finding bugs. Shai-Hulud shows the other half: attacks that need no bug at all, spread at machine speed, and land on developers first. A single npm install handed attackers the cloud keys and CI tokens of teams that had patched every CVE. Developer machines and CI runners are now part of your attack surface.

Sources