DEV Community

Pico
Pico

Posted on • Originally published at getcommit.dev

OIDC Provenance Didn't Save TanStack or Red Hat. npm Staged Publishing Is the Missing Gate.

Two major supply chain attacks in six weeks. Both targeted packages with OIDC provenance enabled. Both succeeded.

TanStack (May 11, 2026): 42 packages, 84 malicious versions in 6 minutes. Attacker exploited GitHub Actions pull_request_target + cache poisoning + OIDC token extraction from runner memory. 16.8M weekly downloads on @tanstack/react-router alone. All published versions had valid SLSA attestations.

Red Hat @redhat-cloud-services (June 1, 2026): 32 packages republished with credential-stealing malware. Attacker compromised a GitHub account, pushed orphan commits, triggered the existing CI pipeline. The pipeline built the malware, published it to npm using its own OIDC tokens, and generated valid SLSA provenance. Provenance proved "this was built by Red Hat's CI" — which was true. The CI just happened to be building malware.

Provenance answers the wrong question

OIDC provenance answers: "Was this built by the expected pipeline?"

When the pipeline is compromised, the answer is yes. The attestation is valid. The malware is signed.

This is the gap. Provenance proves where a build came from. It doesn't prove the CI pipeline wasn't tampered with.

npm Staged Publishing closes it

npm Staged Publishing (GA May 2026) adds a human approval gate between CI publishing a version and that version becoming the default npm install target.

Without Staged Publishing:

CI runs → npm publish → version is live immediately
Enter fullscreen mode Exit fullscreen mode

With Staged Publishing:

CI runs → npm stage → version sits in holding → human 2FA approval → version goes live
Enter fullscreen mode Exit fullscreen mode

In both the TanStack and Red Hat scenarios, malicious versions would have sat in a staging area. No silent pushes to latest. A human would need to review and approve each one.

PostCSS maintainer Andrey Sitnik made this point directly in postcss/postcss#2096: "CI-as-publisher increased the attack risks compared to 2FA manual publishing." He was right — and the incidents proved it.

Detection

npm doesn't surface whether a package uses Staged Publishing. Registry metadata doesn't include it unless a version is actively staged. CI workflow files might reference npm stage commands, but nobody checks those at dependency-install time.

We added Staged Publishing detection to Commit this week. Two-tier detection:

  1. dist-tags check — if a stage dist-tag exists in the registry, the package is actively using staged publishing (zero extra API calls — it's already in the registry response)
  2. GitHub Actions workflow scan — looks for npm stage, @npm/staged-publish, or staged-publish patterns in the package's CI configuration

The result shows up in scoreBreakdown.stagedPublishing on the API response and in the CLI output.


Update: First adopter detected (June 19, 2026)

A few hours after publishing this post, Andrey Sitnik confirmed he had moved nanoid (208M weekly downloads) and nanospy to Staged Publishing. He left a comment on the PostCSS issue inviting verification: "I already moved nanoid and nanospy to the new process, we can test them."

Commit picked up both within the registry cache window. Live JSON:

  • nanoid: hasStagedPublishing: true, score 92/100, stagedPublishing: 2/2
  • nanospy: hasStagedPublishing: true, score 55/100, stagedPublishing: 2/2

The trigger was the npm stage publish step in nanoid's release.yml. PostCSS itself still reads false. That one's coming in a week or two. When Sitnik flips it, the score updates automatically.

That's the test: can scoring track the new gate as maintainers actually adopt it? On the first real-world case, yes.

Second adopter, no announcement: sweeping the top of the npm tree the same afternoon turned up preact (23M weekly downloads). No press release, no thread. Line 118 of preact's release.ymlnpm stage publish preact.tgz --provenance --access public — does the work. Provenance and staged publishing shipped together. Live JSON: preact returns hasStagedPublishing: true.

Two signals, two adopters in the same week. One announced, one didn't. Detection picked up both.

Check your dependencies

npx proof-of-commitment
Enter fullscreen mode Exit fullscreen mode

This scans your lockfile and flags packages where a single npm publisher controls >10M weekly downloads — the exact attack surface that's been exploited three times since March. The output now includes whether each package uses OIDC provenance, Staged Publishing, or neither.

If you see CRITICAL with no provenance and no staged publishing — that package is one stolen credential away from the next incident. And the credential doesn't have to be an npm token anymore. It can be a GitHub account, a CI cache, or an OIDC token extracted from process memory.

The supply chain moved past "just use provenance" in May 2026. The question now is which packages have caught up.

Top comments (0)