mythsthe platformsShai-Hulud npm worm2025software supply chain / npm

everyone assumed:

a token on a developer laptop is only as dangerous as the one package it publishes

what was actually true:

The worm used publish tokens stolen from developers to republish up to 20 other packages of the same maintainer, so a developer's token became the supply chain.

how it happened

  1. whoan infected packageruns a script on install
  2. the gapa publish tokenkept on a developer machine or in CI
  3. thenother packagesup to 20 per maintainer in the first wave
  4. the resultmore than 500 packagesfirst wave, per GitHub and CISA
the gap that let it happen the result

what everyone assumed

No source quotes a victim stating this belief, so the title is a constructed proxy for it. It is a reasonable one. A publish token is created for a task: to ship a package. It sits on the laptop or in the pipeline that ships it, and its reach feels like the reach of that package.

The platform’s own later changes point at the same assumption. According to GitHub’s changelog, classic npm tokens never expired, and write-enabled granular tokens used to have no maximum lifetime, with a default of 30 days. A token with no end date on a machine you own looks like a small risk, because the machine is yours and the token only does one job.

what happened

The first wave began on 14 September 2025. ReversingLabs names the package rxnt-authentication 0.0.3, published at 17:58 UTC that day, as the first known compromised package, and Socket’s timeline agrees. How the first maintainer accounts were taken is unknown. ReversingLabs says the nature of the initial compromise is not known, and phishing or vulnerable GitHub Actions are only candidates. Unit 42 tentatively suggests a phishing campaign imitating npm that asked developers to update their MFA login options. That is a hypothesis.

A malicious version ran a script on install. According to Socket, Wiz, Unit 42 and GitHub’s own description, the script ran TruffleHog and read environment variables, the .npmrc file, cloud keys and GitHub tokens from the machine it landed on.

Then the worm used the npm token as a publisher. Socket and StepSecurity describe code that validated stolen npm tokens and used them to publish trojaned versions of up to 20 other packages of the same maintainer. GitHub says the worm worked through compromised maintainer accounts. This is why the sources call it a worm, and why a developer’s token became the way the next packages got infected: the credential collected on one machine did the publishing for the next set of packages.

Stolen GitHub tokens had a second use. According to Wiz and Socket, the worm created a public repository named Shai-Hulud holding the secrets, pushed a workflow that posted secrets to webhook.site, and made private repositories public with the description “Shai-Hulud Migration”.

The largest burst came on 16 September 2025. At 01:14 UTC, according to Socket, a large set of packages, including the CrowdStrike-scoped ones, was published from an account named crowdstrike-publisher.

A second wave followed on 24 November 2025. It was called “Shai-Hulud 2.0” and “Sha1-Hulud: The Second Coming”. Wiz says it may be a different actor. This time the script ran at preinstall, installed Bun, registered a self-hosted runner named SHA1HULUD, tried cloud secret managers and destroyed the home directory if it could not exfiltrate, as described by Wiz, Unit 42 and Aikido. Wiz tested the runner backdoor but did not see it used in real attacks. Aikido reports that the worm now republished up to 100 npm packages, compared with 20 the first time.

PostHog’s own post-mortem shows one route in. On 18 November 2025, a malicious pull request (a proposed code change) ran in an automated workflow and sent out the access token of a bot account. On 23 November, the attacker pushed a changed workflow that dumped all of the repository’s Actions secrets, including the npm publish token. PostHog says a static-analysis alert about that risky workflow had been dismissed when the workflow change was merged on 11 September 2025. Per Aikido, PostHog’s packages were published at 04:11 UTC on 24 November, and PostHog found and removed them, and revoked its publish tokens, at 09:30 UTC. PostHog says it has since moved to pnpm 10, which skips install scripts by default.

step by step

  1. developers hold long-lived tokensPer GitHub's changelog, classic npm tokens never expired and write-enabled granular tokens used to have no maximum lifetime.
  2. 14 September 2025the first known compromised packagerxnt-authentication 0.0.3 is published at 17:58 UTC. How its maintainer account was taken is unknown.
  3. stolen tokens publish the next packagesSocket and StepSecurity describe code that validates stolen npm tokens and publishes trojaned versions of up to 20 other packages of the same maintainer.the assumption breaks here
  4. 23 September 2025CISA issues an alertCISA reports more than 500 packages and tells users to pin to versions from before 16 September.
  5. 24 November 2025a second waveDatadog counts at least 796 unique npm packages, and Aikido reports up to 100 packages per maintainer.
  6. 9 December 2025npm classic tokens are revokedGitHub's changelog dates the revocation of all classic tokens and a 90-day cap on write-enabled granular tokens.

how it surfaced

GitHub says it received the report of the first wave on 14 September 2025. On 15 September, StepSecurity published its post on @ctrl/tinycolor and further packages. On 22 September, GitHub published a plan for the registry: granular tokens that last 7 days, local publishing with 2FA, trusted publishing, and deprecation of classic tokens and TOTP codes. On 23 September, CISA published its alert, citing more than 500 compromised packages, and advised users to pin to versions from before 16 September. GitHub’s own count was also 500+, and Socket’s indicator list ran to 526.

For the second wave, Datadog counts at least 796 unique npm packages in its analysis updated on 4 December 2025, and reports the last infected package it observed at 18:00 UTC on 24 November. Wiz and Unit 42 both report over 25,000 malicious repositories. Wiz says GitHub started removing related repositories and revoking leaked tokens at about 09:00 UTC on 26 November.

The platform changed in response. GitHub’s changelog entry dated 9 December 2025 says all npm classic tokens were permanently revoked, npm login now gives a 2-hour session token, write-enabled granular tokens have a maximum of 90 days, and new packages get 2FA by default. That changelog page is marked retired, so check npm’s own documentation for the rules in force today. On 23 December 2025, GitHub’s roadmap for the next two quarters listed bulk OIDC onboarding, more OIDC providers and staged publishing with MFA approval. Those were listed as what comes next, and the post does not say they were delivered.

None of the sources used here gives a financial total, and none records a regulator, court or fine. CISA’s alert is guidance.

A publish token is a distribution channel, and the worm used it as one.

Pick one token your team holds and write down everything it can publish or reach. If that list is longer than the job the token was made for, the list is your exposure.

The same family of credentials appears in other incidents here. The tj-actions page covers a hijacked CI action that printed secrets, npm tokens among them, into build logs, and the Codecov page covers CI environment variables sent to an attacker. Shai-Hulud picks up where those stories stop, with the question of what a thief does with a publish token. The XZ Utils page shows how a maintainer role can be gained over a long period, and here the first-wave entry point is still unknown. For the underlying habits, see the token guide, the GitHub identity guide and the service accounts guide.

picture it at a company like yours

The company here is made up. The other two paths are not: they happened, and each links to the story.

your company
a normal dayA developer at your company keeps a login token on a laptop so the team can publish a small package it maintains.
the gap nobody seesThe token never expires, and nobody has written down which other packages the same login can publish.trusting what a supplier ships
  1. in this story
    the day it mattersA package the developer installs runs a hidden script that finds the token and publishes new versions of the developer's other packages.
    what it costs youOther companies install those updates from a name they trust, and your team spends days finding and replacing every key.
  2. or it goes like this
    howA genuine, signed update from a certified vendor carries a backdoor.
    what leavesa quiet way into your networkwho ends up with ita state intelligence service
    it happenedSolarWinds / Sunburst, 2020
  3. or it goes like this
    howA build helper everyone uses is hijacked and its versions repointed.
    what leavessecret keys, printed into build logswho ends up with itanyone reading public logs
    it happenedtj-actions/changed-files, 2025

sources

  1. CISA: Widespread Supply Chain Compromise Impacting npm Ecosystem
  2. GitHub Blog: Our plan for a more secure npm supply chain
  3. GitHub Changelog: npm classic tokens revoked, session-based auth and CLI token management now available (page marked retired)
  4. GitHub Blog: Strengthening supply chain security: preparing for the next malware campaign
  5. Socket: Ongoing supply chain attack targets CrowdStrike npm packages
  6. StepSecurity: Shai-Hulud worm has infected over 500 npm packages (ctrl/tinycolor)
  7. ReversingLabs: Shai-Hulud npm supply chain attack, what you need to know
  8. Wiz: Shai-Hulud, ongoing package supply chain worm
  9. Wiz: Shai-Hulud 2.0 ongoing supply chain attack
  10. Unit 42, Palo Alto Networks: npm supply chain attack (Shai-Hulud)
  11. Datadog Security Labs: The Shai-Hulud 2.0 npm worm, analysis
  12. Aikido: Shai-Hulud 2.0 strikes again (Zapier, ENS)
  13. PostHog: Post-mortem on the Shai-Hulud 24 Nov attack

← all myths

your turn

what you can do about it

  1. this week

    List every npm token and GitHub personal access token your team holds, and revoke the ones nobody can name.

    Look in the npm token page, in GitHub settings and in your CI secret stores. Revoke any token nobody can name or that has not been used this quarter, and give the rest an expiry date. If you publish packages, check that publishing requires 2FA. If you only consume packages, change CI installs to skip lifecycle scripts where the build allows it. The basis is CISA's advice to rotate developer credentials and enforce phishing-resistant MFA, and PostHog's move to pnpm 10.

  2. go deeper

  3. if you're not sure where to start

    Run the offboarding checklist on one leaver. It covers every account and token to close when someone leaves.

    open the checklist →

the letter · every two weeks

one access gap in your inbox, with the fix.

Every issue takes one way access creeps in, shows a real incident where it went wrong, and ends with one thing to check that week. Written for the people who run the tools, without a security team behind them.

  • 1one mechanism, explained plainly
  • 2one real incident, taken apart
  • 3one step you can take on Monday

get the next issue

One email every two weeks. Your address stays here and is never passed to vendors. One click to unsubscribe. Privacy.