evanw / evanw/esbuild

Staged Publishing — 242M weekly downloads behind one npm account

Open
#4,486 2 comments 2 reactions 0 assignees View on GitHub
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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.