Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,20 @@ jobs:
node-version: 24
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: pnpm install

# pnpm@10 delegates `pnpm publish` to the npm CLI; OIDC trusted publishing
# requires npm >=11.5.1, which Node 24's bundled npm only satisfies from
# ~24.6 onward. Install a recent-enough npm so we don't depend on which Node patch resolves.
- name: Ensure npm CLI supports OIDC trusted publishing
run: npm install -g npm@11.5.1

Comment on lines +70 to +75
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 Pre-existing: actions/checkout@v6 and actions/setup-node@v6 use floating semver tags in the publish job while pnpm/action-setup and changesets/action in the same job are pinned to full commit SHAs. Consider pinning these to commit SHAs for consistency, though this PR actually improves the overall risk profile by replacing a long-lived NPM_TOKEN with a short-lived OIDC token.

Extended reasoning...

In the publish job (which holds id-token: write and runs in the release environment), actions/checkout@v6 and actions/setup-node@v6 use floating semver tags, while pnpm/action-setup and changesets/action in the same job are pinned to full commit SHAs. This inconsistency is real and worth noting.

This inconsistency is entirely pre-existing — the floating @v6 tags predate this PR. The PR only adds an npm install step and removes NPM_TOKEN/NODE_AUTH_TOKEN env vars.

The refutation makes a compelling point that must be addressed: this PR actually improves the exfiltration risk profile. Before this PR, a compromised floating action in the publish job could exfiltrate the long-lived NPM_TOKEN secret (valid indefinitely, usable from anywhere). After this PR, the worst-case exfiltration is a short-lived OIDC token that expires quickly and can only be exchanged from within a trusted GitHub Actions context. Short-lived OIDC tokens are strictly less dangerous than long-lived static secrets — so the PR is a net improvement from a security standpoint.

Additionally, id-token: write was already present in the publish job before this PR (added in #1836), so the OIDC token was already exposed to these floating actions. This PR changes nothing about that existing exposure.

Mitigating factors: actions/checkout and actions/setup-node are official first-party GitHub-maintained actions. Compromising their v6 tag would require compromising GitHub's own infrastructure — a substantially higher bar than a third-party action. Many organizations deliberately exempt first-party GitHub actions from SHA-pinning requirements for this reason.

Step-by-step proof of inconsistency: (1) Attacker somehow moves the v6 tag on actions/checkout to a malicious commit. (2) The publish job runs and executes the malicious checkout action. (3) The compromised action can request a GitHub OIDC token via id-token: write. (4) However, the token is short-lived (~15 minutes) and the attacker must use it within that window. Compare this to the pre-PR state where the long-lived NPM_TOKEN was in the environment and would have been trivially exfiltrable to any external service.

Fix: Pin actions/checkout and actions/setup-node to their full commit SHAs, consistent with the other actions in the same job. Low-effort improvement for consistency.

- name: Publish to npm
uses: changesets/action@6a0a831ff30acef54f2c6aa1cbbc1096b066edaf # v1
with:
publish: pnpm run ci:publish
env:
GITHUB_TOKEN: ${{ github.token }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
NPM_CONFIG_PROVENANCE: 'true'
Loading