Between November 21-23, a self-propagating worm called Shai-Hulud 2.0 tore through the npm ecosystem, compromising 796 packages and exposing secrets from over 25,000 GitHub repositories in just hours. The npm supply chain attack exploited preinstall lifecycle scripts to steal credentials from 350+ developers—including those at Zapier, Postman, PostHog, and ENS Domains—exposing 11,858 unique secrets with 2,298 still valid days later.
This wormable attack spreads automatically without a command-and-control server, targeting the npm packages and CI/CD pipelines millions of JavaScript developers rely on daily. Moreover, it’s the second wave in three months. npm’s fundamental security model remains broken, and this proves it.
How the Shai-Hulud Worm Spreads: Exploiting Preinstall Scripts
Shai-Hulud 2.0 abuses npm’s preinstall lifecycle scripts, which execute arbitrary code before package installation completes—even if installation fails. The moment a developer runs npm install on a compromised package, the attack chain begins.
The malware installs the Bun JavaScript runtime (an evasion tactic to avoid Node.js monitoring), then launches an obfuscated payload as a detached background process. Additionally, it scans for credentials in ~/.npmrc, ~/.aws/, ~/.azure/, and environment variables, using TruffleHog to aggressively extract secrets. Stolen data gets dumped into a public GitHub repository named “Shai-Hulud” under the victim’s account—exposing everything for anyone to find.
The worm then self-replicates. Using stolen npm tokens, it authenticates to the registry, identifies up to 100 packages maintained by the compromised developer, injects malicious code into those packages, and publishes new versions automatically. At its peak, the attack compromised roughly 1,000 new repositories every 30 minutes. Consequently, no command-and-control server was needed. The worm reads its own code and spreads.
Major Companies Became Unwitting Attack Vectors
High-profile victims included PostHog, Postman, Zapier, and ENS Domains. PostHog was targeted directly via a pull request exploit against their GitHub Actions workflow on November 18. Attackers modified a script to send secrets to a webhook, stole the bot’s GitHub Personal Access Token, then grabbed the npm publishing token. PostHog’s packages became worm carriers.
Postman unpublished all infected packages by 3:30 AM PT on November 24 and confirmed their app and cloud services weren’t compromised. PostHog published a transparent post-mortem, admitting, “This kind of attack… simply wasn’t something we’d prepared for.” Both companies acted fast, but the damage was done. Their packages—trusted by millions—had become malware distribution points.
The numbers tell the story: 796 npm packages with over 20 million weekly downloads collectively, 25,000+ GitHub repos compromised, and 2,298 secrets still valid 24-48 hours after the attack. That’s a 19.4% exposure rate. One in five stolen credentials was still active days later.
npm Security Model Is Fundamentally Broken
This is the second Shai-Hulud wave in three months. The first attack hit in September 2025 with over 500 packages. npm knows preinstall scripts are dangerous—they allow arbitrary code execution before any security scans run—but won’t disable them by default. Furthermore, backward compatibility concerns trump security.
Compare this to pnpm, which disables post-install scripts by default and requires developers to explicitly whitelist packages that need them. pnpm operates on a “default-deny” principle. npm? Default-allow. Trust every package until proven malicious. That’s how you get worms that spread in hours.
The JavaScript community is frustrated. “Preinstall scripts are a fundamental design flaw,” developers are saying. And they’re right. Every lifecycle script is a potential backdoor. Until npm changes this—and they won’t—attacks like Shai-Hulud will continue.
What Developers Must Do Now
If you installed npm packages between November 21-23, check immediately. Search GitHub for “Shai-Hulud” repositories under your organization. Review your npm packages published during that window. Additionally, audit CI/CD logs for unexpected installs.
Rotate all credentials: npm tokens, GitHub Personal Access Tokens, SSH keys, and cloud provider credentials (AWS, Azure, GCP). Invalidate CI/CD secrets and environment variables. Pin dependencies to pre-November 21 versions and review your lockfiles for unexpected changes.
For immediate defense, disable lifecycle scripts in CI/CD pipelines:
npm ci --ignore-scripts
Long-term, consider migrating to pnpm. It’s three times faster than npm, uses less disk space, and disables scripts by default. Moreover, enable two-factor authentication on your npm account using WebAuthn (not TOTP). Better yet, use npm’s trusted publishing feature for token-less authentication via OIDC—no tokens means nothing to steal.
And minimize dependencies. Every package you install is a potential attack vector. Sometimes copying a few lines of code is safer than pulling in another dependency.
JavaScript Supply Chain Crisis Won’t Fix Itself
npm’s ecosystem structure creates a massive attack surface. Tiny packages with thousands of dependents, each one a potential infection vector. However, there’s no code review before publication. No malware scanning. Just trust.
Security vendors are publishing detection rules and indicators of compromise. GitHub is revoking tokens and privatizing malicious repos. But it’s all reactive. Attacks happen in hours. Detection takes days. Remediation is manual.
Until npm fundamentally redesigns its security model—permission systems, sandboxing, code signing—these attacks will continue. The ecosystem is structurally vulnerable. Don’t wait for npm to save you. They won’t disable lifecycle scripts. They won’t enforce 2FA. They won’t scan packages before publication.
Protect yourself. Switch to pnpm. Use --ignore-scripts. Rotate your credentials. Take control.










