BYBOWU > Blog > Web development

Shai‑Hulud 2.0: NPM Supply Chain Attack Playbook

blog hero image
If you ship JavaScript, this week is not business as usual. A second wave of the Shai‑Hulud npm campaign was detected on November 24, with compromised packages using preinstall scripts to exfiltrate secrets and poison CI/CD. Here’s a hands‑on playbook to quickly check exposure, rotate credentials with minimal downtime, and lock down your pipeline using npm’s Trusted Publishing and short‑lived tokens. Keep reading for a 90‑minute triage checklist, IOC searches you can run today, an...
📅
Published
Nov 26, 2025
🏷️
Category
Web development
⏱️
Read Time
10 min

The second wave of the Shai‑Hulud campaign hit the npm ecosystem on November 24, 2025, and it’s not subtle. Popular packages were briefly trojanized with preinstall scripts that steal developer and CI/CD credentials, dump data into attacker‑controlled GitHub repos, and in some cases attempt cross‑victim replication. If you maintain JavaScript or run Node in production, treat this as an active npm supply chain attack incident: verify exposure, rotate secrets, and harden your publishing path now.

Below is a concise, field‑tested response you can run in hours, not days. I’ll also show how to move your organization to npm Trusted Publishing (OIDC), which cuts the blast radius by eliminating long‑lived tokens entirely.

Security engineer reviewing npm package scripts and CI dashboards

What actually happened this week?

Between November 21–23, malicious versions of several npm packages were published. On November 24 (UTC), security teams observed a coordinated second wave (“Shai‑Hulud 2.0”) using new payload files (for example, filenames like setup_bun.js and bun_environment.js) to harvest tokens and environment data during the preinstall lifecycle. Stolen material was then pushed to quickly created GitHub repositories labeled with the campaign’s name, while some victims saw malicious workflows injected to exfiltrate additional secrets.

This builds on the first wave disclosed in mid‑September 2025 and the September 23 CISA alert. The tradecraft hasn’t changed in spirit—compromised maintainer credentials, malicious install scripts, secrets harvesting—but the targets broadened and the automation is faster. If you think “we only use these packages in dev,” remember that developer machines and build agents often hold cloud keys, npm publish tokens, and GitHub PATs. That’s why the attacker goes after you, not just your servers.

Fast triage: a 90‑minute checklist your team can run today

Here’s how I’d drive an on‑call response if your org uses npm at any meaningful scale. Timeboxes are aggressive but realistic for a coordinated team.

0–15 minutes: Freeze and scope

  • Announce a temporary internal freeze on publishing npm packages and rotating production credentials without coordination. You want controlled change, not chaos.
  • Lock down CI secrets editing to a small response group. Disable self‑hosted runners that don’t enforce ephemeral containers.
  • Turn on organization audit logging for GitHub and your cloud accounts if it’s not already enabled.

15–45 minutes: Hunt the obvious IOCs

Run these quick checks across developer machines and build agents:

  • Search repos and package-lock.json for suspicious versions published between Nov 21–23 if they map to packages named in current advisories.
  • Scan the dependency tree for packages containing unexpected preinstall/install scripts. In monorepos, don’t forget workspace packages.
  • On GitHub, search your org and user accounts for repos named with the campaign label, branches named after it, or recently added workflows with exfiltration‑like names. Review Actions usage spikes from Nov 21 onward.
  • On build agents, look for stray files commonly associated with the malware (for example, cloud.json, contents.json, environment.json, or similar dumps under temp directories). Review shell history around package installation events.

45–75 minutes: Contain and rotate

  • Revoke any npm classic tokens still lingering. As of November 19, classic tokens are revoked platform‑wide; if you still rely on them locally, you’ve already broken builds and increased risk.
  • Invalidate GitHub PATs used in automation. Replace with repository‑ or environment‑scoped short‑lived tokens, or better: remove them by moving publishes to OIDC (below).
  • Rotate cloud access keys for build identities touched by Node projects. Prioritize keys with broad IAM roles and any keys used by self‑hosted runners.

75–90 minutes: Verify and communicate

  • Confirm that malicious package versions are removed or pinned away in package-lock.json/pnpm-lock.yaml/yarn.lock.
  • Ship a short internal note: what happened, what you rotated, what’s frozen, and what’s next.

Need a deeper, step‑by‑step remediation mindset? See our earlier rapid incident guidance for library bugs like expr‑eval: the rapid fix playbook. The muscles are the same: fast scoping, decisive rotates, then hardening.

Why this npm supply chain attack spreads so well

Three things make Shai‑Hulud particularly costly:

  • Install‑time execution: The npm lifecycle runs installer scripts by default. If a package you trust turns, you inherit its scripts. CI amplifies the blast radius.
  • Secrets everywhere: Modern builds touch multiple SaaS and clouds. A single developer machine can hold npm publish tokens, GitHub PATs, and AWS keys via local caches and CLIs.
  • Automation speed: Once tokens leak, automated workflows can open PRs, inject Actions, republish trojanized packages, or flip private repos public in minutes.

The fix isn’t “audit harder” or “patch faster.” It’s removing long‑lived secrets from the path and making your CI untrusting by default.

Illustration of a broken dependency link and protective shield

Move to npm Trusted Publishing (OIDC) in 30 minutes

Trusted Publishing (general availability as of July 31, 2025) lets npm accept publishes only from CI workflows you authorize, issued via short‑lived OIDC credentials—no persistent npm tokens to steal. It also adds provenance attestations by default.

Minimal GitHub Actions setup

Here’s a hardened pattern we’ve rolled out for clients. It keeps the workflow permissions tight and forces manual approval in a protected environment.

name: release

on:
  workflow_dispatch:
  push:
    tags:
      - 'v*.*.*'

permissions:
  id-token: write      # for OIDC
  contents: read
  packages: write

env:
  NODE_VERSION: 20

jobs:
  publish:
    runs-on: ubuntu-latest
    environment: npm-publish
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          registry-url: https://registry.npmjs.org
      - run: npm ci --ignore-scripts
      - run: npm audit --omit=dev --audit-level=high || true
      - run: npm test --ignore-scripts
      - name: Build
        run: npm run build --ignore-scripts
      - name: Publish to npm with OIDC
        run: npm publish --provenance --access public

Key points:

  • --ignore-scripts during install and build to neuter malicious lifecycle hooks.
  • OIDC only: No NPM_TOKEN secret. Your package must be configured as a trusted publisher in npm settings for this repo/workflow/environment.
  • Manual approval: Put environment protections on npm-publish. Require review from maintainers and limit who can trigger.

If you’ve been following our coverage of npm token policy shifts, you saw this coming. Our earlier guides on post‑Nov 19 npm token migration and the subsequent CI fixes for the date change walk through the breaking deadlines that removed classic tokens and shortened granular token lifetimes. Shai‑Hulud just turned “we should do this” into “we must.”

Harden your build: six controls that stop the next wave

You can’t prevent a maintainer compromise upstream, but you can make your environment a dead end.

  1. Disable install scripts by default in CI. Use npm ci --ignore-scripts or the equivalent for Yarn/pnpm. Re‑enable only for packages you explicitly allow.
  2. Pin transitives via lockfiles and audits. Commit and continuously verify lockfiles. Fail builds when lockfile drifts off approved ranges.
  3. Adopt scoped, short‑lived credentials. Prefer OIDC‑based publishing. If you must keep tokens, make them granular, 2FA‑enforced, and expiring in days—not months.
  4. Run CI in ephemeral, egress‑restricted sandboxes. For self‑hosted runners, isolate per‑job with clean workspaces and block metadata endpoints by default.
  5. Monitor for IOC names and suspicious workflows. Alert on unexpected repo creation, branch names tied to known campaigns, and newly added workflow files that run on push to main.
  6. Produce and verify provenance. Require --provenance on publishes and verify Sigstore attestations in downstream deployments.

People also ask

How do I check if my org was hit without sifting every repo?

Start with your package registries and CI logs between November 21–24. Look for install attempts of the suspect versions, any failed or unusual preinstall output, and sudden spikes in workflow runs. In GitHub, search across your org for recent repo creations you didn’t expect and for workflow files added in the last 72 hours. If you find even one IOC, treat all builders that touched Node as potentially exposed and rotate credentials.

Does rotating npm tokens alone fix it?

No. If a machine or runner is still compromised—or if you keep long‑lived tokens—attackers can just steal the new ones. That’s why the end‑state is OIDC with trusted publishers and ephemeral credentials, plus --ignore-scripts during installs.

Can I keep using popular packages safely?

Yes, with guardrails. Pin versions, block lifecycle scripts in CI, and verify integrity with provenance. Most maintainers fix fast; our job is to assume occasional compromise and make it a non‑event.

Data points and dates that matter

  • September 15–23, 2025: First wave analyzed publicly; CISA issued an alert on September 23.
  • November 21–23, 2025: Malicious trojanized versions for the second wave appeared in the registry.
  • November 24, 2025: Second‑wave activity confirmed, with new payload filenames seen in the wild and mass creation of attacker‑labeled GitHub repositories.
  • November 5 & 19, 2025: npm tightened token policies and revoked classic tokens, pushing maintainers toward granular tokens and OIDC‑based Trusted Publishing.

A practical IOC search kit

Drop these into your shell and SIEM to cover 80% of quick wins:

# 1) Find lifecycle scripts in the current repo
jq '.. | objects | select(has("scripts")) | .scripts | to_entries[] | select(.key|test("install|preinstall|postinstall"))' package.json 2>/dev/null

# 2) Scan lockfiles for suspect date ranges (approximate)
grep -R "2025-11-2[1-3]" node_modules/**/package.json 2>/dev/null || true

# 3) Org-wide: find newly created GitHub repos in the last 5 days
# (Use the GitHub API or your SIEM connector; pseudo-call below)
# gh api orgs/ORGNAME/repos --paginate | jq 'select(.created_at > "2025-11-21T00:00:00Z")'

# 4) Look for unexpected workflow additions on main/default branches
# (Audit via GitHub logs or a code search for *.yml added recently.)

# 5) Hunt common dump artifacts on runners (Linux)
sudo find /tmp -maxdepth 2 -type f -name '*environment*.json' -o -name 'cloud.json' -o -name 'contents.json'

Risk tradeoffs and edge cases

Two gotchas we keep seeing:

  • Dev “convenience scripts” in CI. Teams often allow postinstall to run docs generation or dev‑only tooling on shared runners. That’s effectively running unvetted code on your production build fabric. Move those to local dev or gated jobs.
  • Monorepo leakage. A compromised package in one workspace can touch shared caches, shared credentials, or sibling apps if your pipeline reuses the same runner or network namespace. Enforce per‑job isolation and wipe workspaces.

What to do next (today and this week)

Today:

  • Freeze publishing, run the 90‑minute triage, and rotate any credential that touched Node builds since Nov 21.
  • Switch CI installs to --ignore-scripts and pin lockfiles.
  • Stand up Trusted Publishing for your top three packages and prove you can publish without a token.

This week:

  • Roll out OIDC publishing org‑wide with environment approvals and provenance verification.
  • Move self‑hosted runners to ephemeral containers/VMs with blocked metadata endpoints.
  • Codify an SBOM‑driven dependency review in PR checks and add an allow‑list for packages permitted to run lifecycle scripts.

Zooming out

Supply‑chain attacks aren’t going away. But the path to resilience is clear: ephemeral credentials, provenance, strict CI execution, and quick rotations. If you’re not there yet, Shai‑Hulud 2.0 is your forcing function. If you want help pressure‑testing your pipeline or rolling out OIDC across repos, our team does this weekly. Start at software supply‑chain hardening and incident readiness, browse relevant case studies in our portfolio, and subscribe on the blog for follow‑ups as the situation evolves.

One last reminder: this campaign thrives on speed and default settings. Change both, and you turn a headline‑grabbing npm incident into a minor internal postmortem.

Developer editing GitHub Actions workflow YAML for secure publishing
Written by Roman Sulzhyk · BYBOWU
3,441 views

Work with a Phoenix-based web & app team

If this article resonated with your goals, our Phoenix, AZ team can help turn it into a real project for your business.

Explore Phoenix Web & App Services Get a Free Phoenix Web Development Quote

Get in Touch

Ready to start your next project? Let's discuss how we can help bring your vision to life

Email Us

[email protected]

We typically respond within 5 minutes – 4 hours (America/Phoenix time), wherever you are

Call Us

+1 (602) 748-9530

Available Mon–Fri, 9AM–6PM (America/Phoenix)

Live Chat

Start a conversation

Get instant answers

Visit Us

Phoenix, AZ / Spain / Ukraine

Digital Innovation Hub

Send us a message

Tell us about your project and we'll get back to you from Phoenix HQ within a few business hours. You can also ask for a free website/app audit.

💻
🎯
🚀
💎
🔥