Staged Publishing — 242M weekly downloads behind one npm account
- Dominant language
- Go
- Stars
- 40.1k
- Forks
- 1.3k
- PR merge metrics
- No merged PRs in 30d
Description
## Context
esbuild has one npm publisher (`esbuild` account) controlling 242M weekly downloads. ~~No OIDC provenance attestations.~~ **Correction:** esbuild has had SLSA v1 provenance attestations since 0.26.0 (November 2025). No Staged Publishing.
As a build tool, esbuild sits in the critical path of virtually every project that uses it — a compromised version doesn't just run malicious code at install time (via the postinstall script discussed in #4085), it can inject code into every application that builds through it.
## What Staged Publishing does
[`npm stage`](https://docs.npmjs.com/cli/v11/commands/npm-stage) adds a human 2FA approval step between `npm publish` and a version becoming the `npm install` target:
```
Without: publish → version is live immediately
With: npm stage publish → version sits in staging → human 2FA approve → version goes live
```
In the TanStack (May 11, 42 packages in 6 minutes) and Red Hat @redhat-cloud-services (June 1, 32 packages) compromises, attackers exploited CI pipelines that had legitimate publish access. Both produced valid SLSA provenance. Staged Publishing would have caught both — malicious versions would have sat in a staging area waiting for human approval that never came.
## esbuild's position
The `esbuild` npm account is the sole publisher. If that account's credentials are compromised — whether through phishing, session hijack, token theft, or CI pipeline exploitation — an attacker can push a new version that goes live immediately to 242M weekly installs.
OIDC provenance confirms that the CI pipeline ran the publish — it does not prevent a compromised pipeline from publishing. This is the exact attack surface that Staged Publishing was built to close.
## Adoption
Andrey Sitnik moved nanoid and nanospy to Staged Publishing after the TanStack and Red Hat incidents, with PostCSS following. The change is a workflow modification — CI runs `npm stage publish` instead of `npm publish`, and the maintainer approves each release with 2FA in the npm web UI.
## Not a vulnerability
This isn't a security bug in esbuild. It's a structural observation about the npm publishing pipeline: one account, no staging gate, 242M weekly downloads. The mitigation is a configuration change on the npm side.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with npm stage documentation and esbuild's release CI workflow, which the issue says should use `npm stage publish` instead of `npm publish`. Confirm how human 2FA approval would fit the existing release process and verify that a staged version does not become the npm install target until approval.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- release, security
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100