security(release): drop .npmrc NPM_TOKEN write, rely on npm trusted publishing#14011
Conversation
…ublishing The previous publish flow wrote a static `_authToken` from the `NPM_TOKEN` secret into `~/.npmrc` before invoking `pnpm publish`. On pnpm < 11.0.7 a configured `_authToken` takes precedence over OIDC trusted publishing, so even though all `@rspack/*` packages have a trusted publisher binding configured on npmjs.com, releases were still authenticating with a long-lived token. The static-credential surface is exactly what enables the TanStack 2026-05-11-style attack: anything that runs in the `npm`/`npm-canary` environment can exfiltrate `process.env.NPM_TOKEN` and publish arbitrary versions. Deleting the `.npmrc` write makes OIDC the only auth path. `pnpm publish` now mints a per-run token via the workflow's `id-token: write` permission and authenticates against the trusted publisher binding configured on npmjs.com. Provenance (`--provenance` flag) is unaffected; it was already using OIDC for signing and is independent of publish auth. After merge, the `NPM_TOKEN` secret in the `npm` and `npm-canary` GitHub environments has no remaining consumer and should be removed from environment secrets to fully retire the credential.
There was a problem hiding this comment.
Pull request overview
This PR hardens the npm release pipeline by removing the step that writes a long-lived npm auth token into ~/.npmrc, so publishing can rely on npm Trusted Publishing (OIDC) instead of a static NPM_TOKEN.
Changes:
- Remove
~/.npmrc_authTokencreation logic from the release publish script. - Ensure
pnpm publishuses the workflow-provided OIDC flow (Trusted Publishing) rather than a repository/environment secret.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
rspack/scripts/release/publish.mjs
Lines 50 to 52 in 4e6e2bc
In the stable release path I checked, .github/workflows/release.yml calls reusable-release-npm.yml, and npm’s trusted-publishing troubleshooting docs state that for workflow_call / workflow_dispatch publishes, validation checks the calling workflow rather than the workflow containing npm publish (https://docs.npmjs.com/trusted-publishers/). After this change removes the static .npmrc token fallback, pnpm publish here will 401 for stable releases if the npm packages are bound to reusable-release-npm.yml instead of the caller release.yml; please update the trusted-publisher bindings or keep a fallback until that migration is complete.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
📦 Binary Size-limit
🙈 Size remains the same at 61.95MB |
Rsdoctor Bundle Diff AnalysisFound 6 projects in monorepo, 0 projects with changes. 📊 Quick Summary
Generated by Rsdoctor GitHub Action |
Merging this PR will not alter performance
Comparing Footnotes
|
Summary
~/.npmrc_authTokenwrite fromscripts/release/publish.mjs.pnpm publishnow authenticates against npmjs.com via the workflow's OIDC token (npm trusted publishing), instead of a long-livedNPM_TOKENsecret.Why this is needed even though
--provenanceis already set--provenanceand trusted publishing are independent:--provenance: signs a SLSA attestation about the build using the workflow's OIDC token. Already enabled._authTokento shadow it.On
pnpm < 11.0.7(rspack is onpnpm@10.33.4), a configured_authTokentakes precedence over OIDC. Therefore, despite trusted publishers being configured on npmjs.com, releases have been authenticating with the long-livedNPM_TOKENall along. Removing the.npmrcwrite is what actually flips the release flow to OIDC.Prerequisite (must already be true before merge)
Every
@rspack/*package on npmjs.com has a Trusted Publisher binding for:web-infra-dev/rspack+ workflowreusable-release-npm.yml+ environmentnpmweb-infra-dev/rspack+ workflowrelease-canary.yml+ environmentnpm-canaryweb-infra-dev/rspack+ workflowrelease-debug.yml+ environmentnpm-canaryWithout these bindings on every workspace package (12+:
@rspack/core,@rspack/cli,@rspack/bindingplus the 10@rspack/binding-<platform>packages, etc.), pnpm 10.x has no fallback to static token and the corresponding package'spnpm publishwill 401.Diff
12 deletions, 0 additions.
Test plan
dry_run: true(manually via workflow_dispatch). Confirmpnpm publishin the log negotiates auth via OIDC — log line should reference the trusted publisher / OIDC handshake rather than reading_authTokenfrom~/.npmrc. If any package 401s, that package's trusted-publisher binding on npmjs.com is missing or misconfigured — fix before merge.latesttag to confirm a real publish flows through.npm view @rspack/binding-wasm32-wasi --json→dist.attestations).Required after-merge actions
NPM_TOKENsecret from thenpmGitHub environment.NPM_TOKENsecret from thenpm-canaryGitHub environment.After both deletions, the long-lived credential has zero remaining consumers in the repo (
grep -rn NPM_TOKEN .should match only documentation or this PR's history).Follow-ups (separate PRs)
pnpmto>= 11.0.7so OIDC takes precedence over any future_authTokenregression, and so failed OIDC negotiation transparently falls back to a static token (defense in depth).publish.mjsthat hard-fails ifprocess.env.NPM_TOKENis set, preventing accidental reintroduction.