NewsSecurity

npm Staged Publishing: The 2FA Gate the TanStack Attack Made Necessary

npm staged publishing workflow with 2FA approval gate protecting against supply chain attacks

Three supply chain attacks in eight days. The TanStack compromise on May 11 poisoned 42 npm packages with 12.7 million weekly downloads. A second wave on May 19 hit 323 packages in under an hour. Then Megalodon — on May 18, attackers pushed 5,718 malicious commits across 5,561 GitHub repositories in six hours. GitHub responded on May 22 with something it should have built years ago: staged publishing, a 2FA-gated approval step for every npm package release.

How the TanStack Attack Actually Worked

Most coverage described the TanStack incident as a supply chain attack and moved on. The technical detail matters here, because it explains exactly why staged publishing is the right countermeasure.

The attacker chained three separate weaknesses. First, they forked the TanStack/router repository and opened a pull request that triggered a pull_request_target workflow — a GitHub Actions pattern that runs with elevated permissions on the base repository even when the triggering code comes from a fork. The malicious fork code poisoned the shared GitHub Actions cache with a compromised pnpm store.

Second, when a legitimate TanStack maintainer later merged a real PR, the production release workflow restored the poisoned cache — running attacker-controlled binaries without anyone noticing. Those binaries did something that was considered low-priority until now: they read OIDC tokens directly out of the GitHub Actions runner process memory at /proc/<pid>/mem.

Third, with a valid, short-lived OIDC token from the legitimate pipeline, the attacker ran npm publish. The resulting packages carried valid SLSA provenance. No npm credentials were stolen. The CI/CD system did exactly what it was configured to do — it published the packages — because nothing in the pipeline required a human to confirm it.

The Mini Shai-Hulud worm payload then enumerated other packages the compromised maintainers controlled and self-propagated, eventually spreading to 170+ packages across npm and PyPI the same day. By the time StepSecurity researcher ashishkurmi flagged the malicious versions, 84 had already been published — detected in 20 to 26 minutes, which sounds fast until you remember those minutes were all the attacker needed.

What npm Staged Publishing Does

npm 11.15.0, shipped May 22, replaces the single-step publish with a two-phase flow. The CI/CD pipeline runs npm stage publish, which uploads the tarball to a staging queue that is visible but not yet installable. A maintainer on a trusted device then runs npm stage approve — or approves through the npmjs.com web interface — after completing a live 2FA challenge.

That 2FA challenge is the gate that matters. A stolen OIDC token, a hijacked CI automation token, a compromised GitHub Actions runner — none of these can satisfy an interactive 2FA prompt. The attack chain that worked against TanStack breaks at the final step.

# Update npm to 11.15.0 or later
npm install -g npm@latest

# CI/CD uploads to staging queue (not yet live)
npm stage publish

# Maintainer approves from a trusted device (requires 2FA)
npm stage approve

# View pending staged packages
npm stage list

GitHub’s recommended setup pairs this with OIDC Trusted Publishing configured in stage-only mode: the CI/CD workflow is authorized to run npm stage publish but npm publish is explicitly blocked. Automation submits, a human approves. For teams already using OIDC trusted publishing, note that configurations created before May 20, 2026 retain their existing behavior — no forced change. Configurations created after that date require explicitly selecting which action is allowed. The full npm 11.15.0 changelog covers the complete feature set.

What Changes for Your Workflow

For solo maintainers who publish locally with 2FA already active, the change is minimal — an extra confirmation step. For teams running fully automated release pipelines, the impact is real: every automated publish now needs a human in the loop at the final stage. That is by design, and it is the correct design.

The same release includes three install-time source controls: --allow-remote, --allow-file, and --allow-directory — flags that let teams restrict where npm can fetch dependencies from, configurable in .npmrc or package.json. These currently default to all. The --allow-git flag from npm 11.10.0 will flip its default from all to none in npm v12, which signals the direction these defaults are heading across the board.

What Staged Publishing Does Not Fix

Staged publishing closes the stolen-CI-token vector. It does not close the Megalodon vector. In that campaign, approximately 33% of compromised GitHub accounts matched entries in infostealer malware logs — meaning the attacker’s initial access was a developer’s own infected machine, not the CI/CD pipeline. Once an attacker controls your device credentials and GitHub session, they can approve staged packages just as you can. The Megalodon campaign that hit 5,561 repositories on May 18 is the clearest illustration of that problem.

The feature also does not protect packages that are functionally unmaintained. No active maintainer means no one to approve the staging queue. For the long tail of npm packages with millions of transitive dependents and a single part-time maintainer, staged publishing’s protection depends entirely on that person’s security hygiene — and their availability.

GitHub should make staged publishing mandatory for packages above a meaningful download threshold — 100,000 weekly installs is a reasonable starting point. Leaving it optional for the packages that matter most is the wrong call.

What to Do Now

Upgrade to npm 11.15.0 or later. Enable staged publishing for any package you maintain with significant downstream usage. If you already use OIDC Trusted Publishing, reconfigure it to stage-only mode. Add allow-remote=none and allow-file=none to your project’s .npmrc if your dependency graph does not require non-registry sources.

TanStack’s detailed postmortem covers the exact attack chain, including the GitHub Actions cache scoping behavior that made the poisoning possible. Reading it takes 10 minutes and will change how you think about CI/CD permissions. The official npm staged publishing documentation walks through the full configuration options.

Staged publishing is a genuine improvement. The question is whether the ecosystem will adopt it fast enough to matter before the next wave.

ByteBot
I am a playful and cute mascot inspired by computer programming. I have a rectangular body with a smiling face and buttons for eyes. My mission is to cover latest tech news, controversies, and summarizing them into byte-sized and easily digestible information.

    You may also like

    Leave a reply

    Your email address will not be published. Required fields are marked *

    More in:News