The 2025 Shai-Hulud worm compromised over 500 npm packages by stealing developer credentials and republishing infected versions. The practices that blunt this kind of attack are install from lockfiles, disable install scripts where you can, use phishing-resistant MFA, publish through short-lived trusted publishing, and rotate secrets fast.
What happened?
CISA's alert of 23 September 2025 describes a self-replicating worm, publicly known as Shai-Hulud, that compromised over 500 packages. After gaining access, the malware scanned the environment for credentials, targeting GitHub personal access tokens and API keys for AWS, Google Cloud and Azure. It exfiltrated what it found, uploaded credentials to a public repository named Shai-Hulud through the GitHub API, then authenticated to npm as the compromised developer, injected code into other packages and published new compromised versions.
The mechanism matters more than the name. A stolen publisher credential let the malware become a trusted maintainer, so every consumer's usual trust in a package name was misplaced.
What did CISA recommend?
The alert lists these steps:
- Review all dependencies, checking package-lock.json or yarn.lock files, including nested ones, and search artifact repositories for cached versions of affected packages.
- Pin to known safe versions from before 16 September 2025.
- Rotate all developer credentials immediately.
- Require phishing-resistant MFA on developer accounts, especially GitHub and npm.
- Monitor for anomalous network behavior and block outbound connections to webhook.site domains.
- Harden GitHub: remove unneeded apps and OAuth applications, audit webhooks and secrets, and enable branch protection, secret scanning alerts and Dependabot security updates.
What does the Node.js project advise?
The official Node.js security guide lists typosquatting, lockfile poisoning, compromised maintainers, malicious packages and dependency confusion as threats. Its mitigations include:
- Prevent npm from running arbitrary scripts with the ignore-scripts option, which can also be set globally.
- Pin exact dependency versions instead of ranges.
- Use lockfiles, which pin every direct and transitive dependency.
- Automate vulnerability checks in CI.
- Use npm ci instead of npm install, which enforces the lockfile.
Per npm's documentation, npm ci fails if package.json and the lockfile disagree, and it removes any existing node_modules folder first. That makes builds repeatable, and a surprise new version cannot slip in.
How does trusted publishing help maintainers?
The npm docs describe trusted publishing, which uses OpenID Connect so a CI workflow can publish without long-lived npm tokens. A stolen token is exactly what a worm of this type needs, and with trusted publishing there is no standing token to steal. The docs add that publishing from GitHub Actions or GitLab CI/CD also generates provenance attestations automatically, and that it requires npm CLI 11.5.1 or later and Node 22.14.0 or higher. After setup, the docs advise restricting token access and revoking tokens you no longer need.
A constructed example
A team's CI runs npm install on every build. A dependency three levels down gets a new patch version containing a postinstall script that reads environment variables. The build agent holds a cloud deploy key as an environment variable. After the team switches to npm ci with the lockfile committed, ignore-scripts turned on for CI, and short-lived cloud credentials issued per job, the same bad version either never installs, or runs without a script and finds no standing key.
How to check or apply this
- Commit lockfiles and use npm ci in CI and deploys.
- Set ignore-scripts for installs that do not need scripts, and allow exceptions by name.
- Avoid long-lived tokens in CI. Prefer short-lived credentials and trusted publishing.
- Turn on phishing-resistant MFA for GitHub and npm.
- Keep an inventory of what versions each service actually installed, so you can answer "were we affected" in minutes.
- Delay adopting brand new versions of critical packages by a few days where practical.
- Have a credential rotation runbook ready before you need it.
Safeguard's dependency inventory and free in-browser scan at gold.safeguard.sh/scan (up to 100 dependencies) give you a fast answer to step 5.
Quick check
Does npm audit catch a worm like this?
Not at the moment of publication. Audit relies on known advisories, which arrive after the discovery. Lockfiles, script controls and credential hygiene work earlier.
Is ignore-scripts enough?
It stops install-time scripts but not malicious code that runs when you import the package. Combine it with pinning, review and least-privilege credentials.
Sources
- CISA, Widespread Supply Chain Compromise Impacting npm Ecosystem
- Node.js, Security Best Practices
- npm Docs, Trusted publishing for npm packages
- npm Docs, npm ci
Last reviewed: 18 October 2026.