Skip to content

ci: publish to npm via OIDC trusted publishing#99

Open
coderdan wants to merge 1 commit into
mainfrom
ci/npm-oidc-trusted-publishing
Open

ci: publish to npm via OIDC trusted publishing#99
coderdan wants to merge 1 commit into
mainfrom
ci/npm-oidc-trusted-publishing

Conversation

@coderdan
Copy link
Copy Markdown
Contributor

@coderdan coderdan commented Jun 2, 2026

Why

@cipherstash/protect-ffi and its six @cipherstash/protect-ffi-<platform> binary packages publish from this repo using a long-lived NPM_TOKEN — the same class of token npm just revoked on cipherstash/stack, which broke that repo's releases. This migrates to npm OIDC trusted publishing (short-lived, no stored secret) with provenance.

Changes

release.ymlpublish job

  • Add id-token: write.
  • Drop NODE_AUTH_TOKEN and the Neon setup action. The Neon setup runs setup-node with registry-url, which writes a //registry.npmjs.org/:_authToken=… line that shadows OIDC. The publish job only ships prebuilt tarballs, so it needs no Rust toolchain or npm ci — replaced with a plain setup-node (Node 22, no registry-url) + npm install -g npm@^11.5.1 (OIDC needs npm ≥ 11.5.1 / Node ≥ 22.14).
  • Publish with npm publish --access public --provenance.
  • Run on ubuntu-latest (github-hosted): npm only accepts provenance from github-hosted runners (self-hosted → E422). The Rust build matrix stays on Blacksmith.

release.ymlsetup job

  • Remove the NPM_TOKEN presence check (it would fail the release once the secret is removed).

repository fields

  • Add repository (url + per-package directory) to the root package and all six platforms/*/package.json. Provenance validation requires repository.url to match this repo.

Required on npmjs.com before this can publish

Configure a Trusted Publisher (org cipherstash, repo protectjs-ffi, workflow release.yml) for all seven packages:
@cipherstash/protect-ffi, @cipherstash/protect-ffi-darwin-x64, -darwin-arm64, -win32-x64-msvc, -linux-x64-gnu, -linux-arm64-gnu, -linux-x64-musl.

The current NPM_TOKEN-based pipeline keeps working until then, so this can merge and be exercised on the next workflow_dispatch release once the publishers are set up. ⚠️ Verify a release after merge — this pipeline publishes prebuilt tarballs downloaded from the GitHub Release, and provenance-from-tarball is exercised here for the first time.

Summary by CodeRabbit

  • Chores
    • Enhanced npm publishing security by transitioning to OIDC-based trusted publishing, eliminating the need for stored authentication tokens.
    • Added and standardized repository metadata across all package distributions, including git repository references, bug tracker links, and homepage information for improved package discovery and project transparency.

Migrate the release pipeline off the long-lived NPM_TOKEN (the same kind of
token that was just revoked on cipherstash/stack) to npm OIDC trusted
publishing with provenance.

- publish job: add `id-token: write`; drop NODE_AUTH_TOKEN and the Neon setup
  action (its setup-node `registry-url` writes a token .npmrc that shadows
  OIDC). Use a plain setup-node (no registry-url) on Node 22, upgrade npm to
  >= 11.5.1, and publish with `npm publish --access public --provenance`.
- publish job runs on github-hosted ubuntu-latest: npm only accepts provenance
  from github-hosted runners. The Rust build matrix stays on Blacksmith.
- setup job: drop the NPM_TOKEN presence check that failed the release when the
  secret was absent.
- add `repository` (url + per-package directory) to the root package and all
  six platform packages — provenance validation requires repository.url to
  match the building repo.

Requires a trusted publisher on npmjs.com for @cipherstash/protect-ffi and each
of the six @cipherstash/protect-ffi-<platform> packages (org: cipherstash,
repo: protectjs-ffi, workflow: release.yml).
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR transitions npm publishing from NPM_TOKEN secret-based authentication to OIDC trusted publishing with provenance attestation in the release workflow, and adds repository metadata (type, URL, and directory paths) to the root and all six platform-specific package manifests.

Changes

Release workflow and package metadata updates

Layer / File(s) Summary
OIDC trusted publishing in release workflow
.github/workflows/release.yml
Setup job removes NPM_TOKEN validation; publish job switches to ubuntu-latest with id-token: write permission, Node.js 22 setup, npm version upgrade for OIDC support, and publishes each tarball with --provenance flag instead of NODE_AUTH_TOKEN.
Repository metadata across packages
package.json, platforms/darwin-arm64/package.json, platforms/darwin-x64/package.json, platforms/linux-arm64-gnu/package.json, platforms/linux-x64-gnu/package.json, platforms/linux-x64-musl/package.json, platforms/win32-x64-msvc/package.json
Root and all platform package manifests now include repository metadata with type: "git", GitHub URL, and platform-specific directory fields for source traceability.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • calvinbrewer

Poem

🐰 Security's tale in trusted ways,
OIDC tokens now light the days,
Provenance signed with every pack,
Metadata trails lead safely back!
Hop along this safer stack.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately reflects the main change: migrating npm publishing from token-based auth to OIDC trusted publishing.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/npm-oidc-trusted-publishing

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 118-129: The workflow uses movable tag refs for third-party
actions (actions/checkout@v4, actions/setup-node@v4, and
actions/create-github-app-token@v1); replace each tag ref with the corresponding
immutable commit SHA (e.g., actions/checkout@<commit-sha>) by looking up the
recommended commit SHA for each action on its GitHub repo or marketplace and
updating the uses entries, then verify the workflow parses and runs with the
pinned SHAs.
- Around line 113-120: Update the publish job permissions to use
permissions.contents: read instead of write, and modify the actions/checkout@v4
step to include with: persist-credentials: false so checkout credentials are not
carried into later steps; locate the permissions block containing
permissions.contents and the Checkout Code step using actions/checkout@v4 and
make those two changes (reduce contents permission and add persist-credentials:
false).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8cb3ff23-200b-4547-a93a-312572bddb9b

📥 Commits

Reviewing files that changed from the base of the PR and between 88a969c and d4c70c7.

📒 Files selected for processing (8)
  • .github/workflows/release.yml
  • package.json
  • platforms/darwin-arm64/package.json
  • platforms/darwin-x64/package.json
  • platforms/linux-arm64-gnu/package.json
  • platforms/linux-x64-gnu/package.json
  • platforms/linux-x64-musl/package.json
  • platforms/win32-x64-msvc/package.json

Comment on lines 113 to 120
permissions:
contents: write
id-token: write # Required for npm OIDC trusted publishing
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ needs.setup.outputs.ref }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the publish job's permissions and checkout credential handling.
sed -n '104,130p' .github/workflows/release.yml
rg -n -C2 'contents:|persist-credentials' .github/workflows/release.yml

Repository: cipherstash/protectjs-ffi

Length of output: 1669


Harden publish permissions and disable checkout credential persistence

The publish job in .github/workflows/release.yml grants permissions.contents: write, but it only needs to read the repository/release assets. The actions/checkout@v4 step does not set persist-credentials: false, so checkout credentials aren’t explicitly prevented from being carried into later steps. Reduce contents to read and disable credential persistence on checkout.

Suggested hardening
   publish:
     name: Publish
@@
     permissions:
-      contents: write
+      contents: read
       id-token: write # Required for npm OIDC trusted publishing
@@
       - name: Checkout Code
         uses: actions/checkout@v4
         with:
           ref: ${{ needs.setup.outputs.ref }}
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
permissions:
contents: write
id-token: write # Required for npm OIDC trusted publishing
steps:
- name: Checkout Code
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ needs.setup.outputs.ref }}
permissions:
contents: read
id-token: write # Required for npm OIDC trusted publishing
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
ref: ${{ needs.setup.outputs.ref }}
persist-credentials: false
🧰 Tools
🪛 zizmor (1.25.2)

[error] 118-118: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 113 - 120, Update the publish job
permissions to use permissions.contents: read instead of write, and modify the
actions/checkout@v4 step to include with: persist-credentials: false so checkout
credentials are not carried into later steps; locate the permissions block
containing permissions.contents and the Checkout Code step using
actions/checkout@v4 and make those two changes (reduce contents permission and
add persist-credentials: false).

Comment on lines +118 to +129
uses: actions/checkout@v4
with:
ref: ${{ needs.setup.outputs.ref }}
- name: Setup Neon Environment
uses: ./.github/actions/setup
# OIDC trusted publishing needs Node >= 22.14 and npm >= 11.5.1, and must
# NOT have a token .npmrc. Use setup-node WITHOUT registry-url (the Neon
# setup action sets registry-url, which writes a //registry/:_authToken
# line that shadows OIDC) — this job only publishes prebuilt tarballs, so
# it needs no Rust toolchain or npm install.
- name: Install Node.js
uses: actions/setup-node@v4
with:
use-rust: false
node-version: 22
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify which action references in the release workflow are still tag-based.
rg -n 'uses:\s+[^@]+@v[0-9]+' .github/workflows/release.yml

Repository: cipherstash/protectjs-ffi

Length of output: 236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/release.yml"

echo "=== uses: lines in release.yml ==="
rg -n '^\s*-\s+uses:|^\s*uses:' "$FILE" -S || true

echo
echo "=== uses@ references (extract owner/name@ref) ==="
rg -n 'uses:\s*([^@]+)@([^\s]+)' "$FILE" -S | sed -E 's/^[0-9]+:.*uses:\s*([^@]+)@([^ ]+).*/\1@\2/'

echo
echo "=== Check for pinned SHAs vs tags (40-hex) ==="
python3 - <<'PY'
import re, pathlib
p=pathlib.Path(".github/workflows/release.yml")
txt=p.read_text()
pat=re.compile(r'uses:\s*([^@]+)@([^\s]+)')
sha=re.compile(r'^[0-9a-fA-F]{40}$')
for m in pat.finditer(txt):
    ref=m.group(2)
    status="PINNED_SHA" if sha.match(ref) else "TAG_OR_OTHER"
    print(f"{m.group(1)}@{ref} -> {status}")
PY

echo
echo "=== Context around lines 110-140 ==="
nl -ba "$FILE" | sed -n '100,150p'

Repository: cipherstash/protectjs-ffi

Length of output: 2538


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/release.yml"

echo "=== lines 110-135 ==="
awk 'NR>=110 && NR<=135 {printf "%d:%s\n", NR, $0}' "$FILE"

echo
echo "=== uses: tag-based (not 40-hex SHA) ==="
python3 - <<'PY'
import re, pathlib
txt=pathlib.Path(".github/workflows/release.yml").read_text()
pat=re.compile(r'uses:\s*([^@]+)@([^\s]+)')
sha=re.compile(r'^[0-9a-fA-F]{40}$')
out=[]
for m in pat.finditer(txt):
    ref=m.group(2)
    if not sha.match(ref):
        out.append((m.group(1), ref))
for owner, ref in out:
    print(f"{owner}@{ref}")
PY

Repository: cipherstash/protectjs-ffi

Length of output: 2937


Pin tag-based GitHub Actions to immutable SHAs in the publish workflow.

  • Line 118: actions/checkout@v4 is a movable tag
  • Line 127: actions/setup-node@v4 is a movable tag
    Also present: actions/create-github-app-token@v1 is tag-based.
🧰 Tools
🪛 zizmor (1.25.2)

[error] 118-118: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 127-127: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 118 - 129, The workflow uses
movable tag refs for third-party actions (actions/checkout@v4,
actions/setup-node@v4, and actions/create-github-app-token@v1); replace each tag
ref with the corresponding immutable commit SHA (e.g.,
actions/checkout@<commit-sha>) by looking up the recommended commit SHA for each
action on its GitHub repo or marketplace and updating the uses entries, then
verify the workflow parses and runs with the pinned SHAs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant