On September 14, 2025, attackers published a trojanized version of @ctrl/tinycolor — a color-manipulation library with roughly 2 million weekly downloads — to the npm registry. Within 24 hours, the malicious postinstall script had spread to around 200 packages; within days, over 500, including packages maintained by CrowdStrike. Researchers named the campaign Shai-Hulud, after the sandworms in Dune, because it moved like one: burrowing through the dependency tree unassisted, using each compromised maintainer's own npm token to infect the next package. CISA issued an alert on September 23, 2025. Then, on November 24, 2025, a second wave — "Shai-Hulud 2.0," self-labeled "Sha1-Hulud: The Second Coming" in its own exfiltration repos — hit 796 unique packages across 1,092 versions and touched more than 25,000 GitHub repositories. Here is what happened, how it actually propagates, and what it means for teams pulling packages from npm.
What is the Shai-Hulud npm worm?
Shai-Hulud is a self-replicating malware campaign that compromises npm packages, harvests credentials from every machine that installs them, and then uses those stolen credentials to automatically publish infected versions of other packages. It was first identified on September 15, 2025, when Socket, Aikido, and other researchers flagged an obfuscated bundle.js payload injected via a postinstall script into @ctrl/tinycolor and dozens of packages published by the same maintainer. Because npm runs install scripts by default, the payload executed the instant npm install ran — no click, no download of a separate binary, nothing beyond a normal dependency install. That single design choice in the npm CLI is the reason a small color library, three dependency hops removed from anyone's direct package.json, could end up compromising build credentials belonging to CrowdStrike, one of the world's largest cybersecurity vendors.
How did the worm spread through npm packages?
Shai-Hulud spread by stealing valid npm publish tokens from infected machines and using them to trojanize every other package the stolen token had rights to. Checkmarx's teardown of the payload documented a function named NpmModule.updatePackage that enumerated the packages a compromised maintainer could publish to, downloaded each one, injected the same malicious bundle.js, repacked it, and republished it under the legitimate maintainer's identity — all automatically, with no attacker keystrokes involved after the initial token theft. That is what separates a worm from a one-off supply chain compromise: each new victim became a new launch point, so the infection graph grew exponentially rather than linearly. Packages with over 2 billion combined weekly downloads were affected across the two waves, according to Securelist's and Koi Security's tracking of the campaign. Crucially, the trojanized versions were not obviously malicious in a registry listing — they carried legitimate-looking version bumps (a real maintainer's account publishing what looks like a routine patch release), which is why the worm spread for roughly 24 hours before defenders had a confirmed indicator-of-compromise list to check against.
What did Shai-Hulud actually steal?
Shai-Hulud stole npm, GitHub, and cloud credentials by running the open-source secret scanner TruffleHog against the infected host. Per analysis from Semgrep, Sysdig, and Zscaler ThreatLabz, the payload downloaded a real TruffleHog binary, pointed it at the local filesystem and environment variables, and harvested NPM_TOKEN, GITHUB_TOKEN, and AWS, GCP, and Azure keys wherever it found them. It then validated stolen npm tokens with npm whoami, called GitHub's API to create a new public repository — typically named "Shai-Hulud" — and dumped the harvested secrets into that repo in the clear, for anyone to find. On CI runners, the malware also wrote a persistence GitHub Actions workflow file so it could keep exfiltrating secrets from future builds even after the original infected package version was removed.
What is Shai-Hulud 2.0, "The Second Coming"?
Shai-Hulud 2.0 is a more aggressive rewrite of the original worm that surfaced on November 24, 2025, using the Bun JavaScript runtime instead of Node.js to dodge detection tooling built around Node's behavior. According to Datadog Security Labs and Check Point, this variant injects two files — setup_bun.js and bun_environment.js — via a preinstall script, silently installs the Bun runtime on the victim's machine, and uses it to execute an obfuscated payload that repeats the original credential-harvesting and self-propagation logic. Aikido Security clocked the start of the wave precisely: PostHog packages began getting infected at 4:11:55 AM GMT on November 24, 2025, and Postman packages less than an hour later, at 5:09:25 AM GMT the same morning. By the time researchers had a full picture, the victim list included Zapier's official integration-building toolkit, ENS Domains' libraries for resolving .eth addresses (used by wallets and DApps across Ethereum), AsyncAPI's CLI, and Postman and PostHog's own SDKs — 492 packages by one count, representing 132 million combined monthly downloads, with over 27,000 GitHub repositories created to store leaked secrets. Sysdig's independent tracking put the blast radius even higher, at more than 25,000 affected repositories, and the community-tracked package count reached 796 unique packages and 1,092 versions before registry takedowns caught up. Its exfiltration repos carry the description "Sha1-Hulud: The Second Coming" — the attackers' own branding, left in plain sight in the leaked data.
How can teams detect and respond to Shai-Hulud infections?
Teams can detect Shai-Hulud by checking installed dependency versions against the published indicator-of-compromise lists from CISA, Socket, and Aikido, and by auditing GitHub for unexpected public repositories named "Shai-Hulud" or workflow files added without a corresponding pull request. Because the worm operates by republishing legitimate-looking patch versions (for example, shipping a 2.4.1 immediately after a maintainer's real 2.4.0), version pinning alone does not stop initial exposure — teams need to diff exact resolved versions in their lockfiles against known-bad releases, not just package names. Immediate response steps documented across CISA's alert and vendor writeups: rotate every npm, GitHub, and cloud credential that touched an affected build environment, revoke any GitHub Actions workflow the attacker may have added, search GitHub org-wide for repositories named "Shai-Hulud" or described as "Sha1-Hulud: The Second Coming" and delete them after preserving evidence, and treat any CI runner that installed an infected version as compromised rather than merely "cleaned." Because the second wave used a preinstall script and a bundled Bun runtime instead of the first wave's postinstall and Node.js payload, teams that only updated detection rules for wave one were blind to wave two two months later — a reminder that IOC lists age out fast in an actively evolving worm.
How Safeguard Helps
Safeguard is built to catch campaigns like Shai-Hulud before a trojanized postinstall script ever reaches a production build. Griffin AI continuously watches new package publishes and dependency updates for worm-like behavior — obfuscated install scripts, secret-scanner binaries fetched at install time, and outbound calls to GitHub's repo-creation API — and flags them ahead of standard vulnerability-database disclosure. Reachability analysis tells teams whether an affected package like @ctrl/tinycolor is actually invoked in a way that would trigger its install script in their build, cutting through alert noise when hundreds of packages are implicated at once. Safeguard generates and ingests SBOMs so security teams can query, in seconds, every service that resolved an infected version across every repo — not just the ones someone remembers to check — and it opens auto-fix pull requests that pin dependencies to clean versions and strip the malicious release from lockfiles, closing the exposure window from days to minutes.