Skip to content

chore: harden release workflow for supply-chain security#262

Open
thetutlage wants to merge 5 commits into
10.xfrom
chore/harden-release-workflow
Open

chore: harden release workflow for supply-chain security#262
thetutlage wants to merge 5 commits into
10.xfrom
chore/harden-release-workflow

Conversation

@thetutlage
Copy link
Copy Markdown
Member

@thetutlage thetutlage commented May 28, 2026

Summary

  • Switches release publishing to npm Trusted Publishing (OIDC) — removes long-lived NPM_TOKEN from the workflow, so transitive postinstall scripts can no longer exfiltrate it from ~/.npmrc.
  • Pins actions/checkout, actions/setup-node, and the adonisjs/.github / adonisjs/core reusable workflows to commit SHAs to defend against tag/branch reflog tampering.
  • Tightens default workflow permissions: to contents: read, elevates per-job (contents: write + id-token: write only on the release job).
  • Switches the release-time install to npm install --ignore-scripts so transitive postinstall scripts cannot run during the release job.
  • Adds an npm audit signatures step before publishing to verify registry signatures.
  • Adds a Dependabot config for the github-actions ecosystem so the pinned SHAs are kept current automatically.
  • Adds a concurrency block to prevent overlapping release runs.

Prerequisites

  • npm Trusted Publishing must already be configured for this package on npmjs.com (workflow file: release.yml, environment: none). Confirmed configured before this PR.
  • The repo secret NPM_TOKEN can be deleted after the first successful tokenless release.

Test plan

  • Trigger the release workflow manually with a patch bump.
  • Confirm publish succeeds without NPM_TOKEN being available.
  • Confirm provenance attestation appears on npmjs.com for the new version.
  • If npm install --ignore-scripts breaks the build for this repo (e.g. a transitive dep relies on postinstall to fetch a native binary), drop the flag in a follow-up.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Enabled weekly Dependabot checks for GitHub Actions version updates.
    • Pinned GitHub Actions to fixed revisions and tightened workflow permissions across checks and release flows.
    • Streamlined release workflow authentication and npm install/audit steps to improve security and publishing permissions.
    • Began tracking the package lock file in version control (package-lock.json).

thetutlage and others added 4 commits May 28, 2026 12:22
- Use npm Trusted Publishing (OIDC) instead of NPM_TOKEN
- Pin third-party actions and reusable workflows to commit SHAs
- Drop default permissions to read-only, elevate per-job
- Add --ignore-scripts to release-time install
- Add npm audit signatures step
- Add Dependabot for github-actions ecosystem
- Add concurrency guard

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses CodeQL "Workflow does not contain permissions" finding by
declaring contents: read at workflow scope so GITHUB_TOKEN is scoped down
on push/pull_request/workflow_call runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Requires approval from the Core Team before npm publish runs, via the
GitHub Environment created in each repo. Pair with an npm Trusted
Publisher config that pins the environment to fully close the gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The hardened release workflow uses npm install --ignore-scripts and
npm audit signatures, both of which require a lockfile to be effective:

- npm audit signatures verifies registry signatures of the exact resolved
  versions in the lockfile; without one, transitive resolution at release
  time would re-resolve from the registry and silently accept any new
  version of a dep, which is the supply-chain hole we are trying to close.
- --ignore-scripts is meaningful only when paired with reproducible
  resolution, otherwise an attacker who controls a future version of a
  transitive dep can still influence build output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 79c8dfb8-db3c-409f-b59e-f7527257ce88

📥 Commits

Reviewing files that changed from the base of the PR and between 932ff61 and 9e614c7.

📒 Files selected for processing (1)
  • .github/workflows/release.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/release.yml

📝 Walkthrough

Walkthrough

Pins GitHub Actions and reusable workflows to specific commit SHAs, tightens workflow and job permissions, adds Dependabot for GitHub Actions (weekly), updates the release job (registry config, audit, --ignore-scripts), and removes package-lock.json from .gitignore so it is tracked.

Changes

CI/CD Security and Reproducibility Hardening

Layer / File(s) Summary
Dependabot automated dependency updates
.github/dependabot.yml
New configuration enables Dependabot to check for GitHub Actions updates weekly at the repository root.
Checks workflow security hardening
.github/workflows/checks.yml
Adds workflow-level permissions: contents: read and pins reusable workflows and steps (including actions/checkout and actions/setup-node) to specific v4 commit SHAs across lint, typecheck, and Postgres/MySQL/MSSQL/SQLite test jobs.
Release workflow security and authentication
.github/workflows/release.yml
Sets top-level read-only permissions and job-level publish permissions, adds concurrency and environment: npm-publish, pins actions to SHAs, configures npm registry via setup-node, removes manual npm token setup, uses npm install --ignore-scripts, and adds npm audit signatures before npm run release -- --ci.
Package lock file tracking
.gitignore
Removes package-lock.json from .gitignore so the lockfile is committed and tracked.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Actions pinned, the branches hop secure,
Dependabot peers weekly, steady and pure.
Locks now tracked, builds follow the plan,
NPM knows its registry; the release takes a stand.
A tiny rabbit cheers the CI clan.

🚥 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 PR title 'chore: harden release workflow for supply-chain security' directly and accurately reflects the main objectives of the changeset: hardening the release workflow through security improvements including OIDC-based publishing, action pinning, permission tightening, and audit steps.
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 chore/harden-release-workflow

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.

🧹 Nitpick comments (1)
.github/workflows/checks.yml (1)

33-34: ⚡ Quick win

Consider adding persist-credentials: false to checkout steps.

Since this workflow only runs tests and doesn't push to git, disabling credential persistence prevents the GITHUB_TOKEN from lingering in the workspace where it could potentially be accessed. This aligns with the PR's supply-chain hardening goals and addresses the zizmor static analysis findings.

🛡️ Example fix for one checkout step (apply to all four)
-      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false

Also applies to: 63-64, 89-90, 108-109

🤖 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/checks.yml around lines 33 - 34, Add persist-credentials:
false to every actions/checkout step to avoid leaving GITHUB_TOKEN in the
workspace; specifically update each step that uses "uses: actions/checkout@..."
(the four occurrences in the file) to include a step-level key
persist-credentials: false directly under the step, keeping other keys (e.g.,
uses, with) unchanged so the checkout still functions but credentials are not
persisted.
🤖 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.

Nitpick comments:
In @.github/workflows/checks.yml:
- Around line 33-34: Add persist-credentials: false to every actions/checkout
step to avoid leaving GITHUB_TOKEN in the workspace; specifically update each
step that uses "uses: actions/checkout@..." (the four occurrences in the file)
to include a step-level key persist-credentials: false directly under the step,
keeping other keys (e.g., uses, with) unchanged so the checkout still functions
but credentials are not persisted.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e6361bf-4e1f-4871-ba6a-7450bfdcac8c

📥 Commits

Reviewing files that changed from the base of the PR and between ea8933f and 932ff61.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • .github/dependabot.yml
  • .github/workflows/checks.yml
  • .github/workflows/release.yml
  • .gitignore
💤 Files with no reviewable changes (1)
  • .gitignore

Re-adds 'secrets: inherit' to the checks job in release.yml so the
reusable checks workflow continues to receive repository secrets when
invoked by workflow_call from release. Without this, integration tests
in checks that depend on secrets (e.g. RESEND_API_KEY in mail) fail
during release runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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