Skip to content

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

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

chore: harden release workflow for supply-chain security#1178
thetutlage wants to merge 5 commits into
22.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
    • Pinned CI actions to fixed versions across lint, typecheck, and database test jobs to improve workflow stability.
    • Enabled weekly automated monitoring of GitHub Actions dependencies.
    • Hardened release workflow with tightened permissions, concurrency controls, adjusted install/audit steps, and reduced token exposure.

thetutlage and others added 4 commits May 28, 2026 12:26
- 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cd455738-a26b-4bfd-89f6-79a4cd57e9d9

📥 Commits

Reviewing files that changed from the base of the PR and between 793b9ea and 094597e.

📒 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

Adds Dependabot for GitHub Actions, pins checkout/setup-node to exact commit SHAs in checks and release workflows, tightens workflow and job permissions with a release concurrency lock, and changes release install/audit steps to use npm audit signatures and only GITHUB_TOKEN.

Changes

GitHub Actions Security and Reproducibility Improvements

Layer / File(s) Summary
Dependabot GitHub Actions monitoring configuration
.github/dependabot.yml
Adds Dependabot v2 config to monitor github-actions in the repository root on a weekly schedule.
Checks workflow action pinning and permissions
.github/workflows/checks.yml
Adds workflow-level contents: read and replaces actions/checkout@v4 and actions/setup-node@v4 with pinned commit SHAs across lint, typecheck, test-postgres, test-mysql, test-sqlite, and test-mssql jobs.
Release workflow permissions and concurrency
.github/workflows/release.yml
Sets workflow-level contents: read, adds a concurrency lock for the release group, and defines explicit job-level permissions for reusable checks calls and the release job.
Release job action pinning and npm audit hardening
.github/workflows/release.yml
Pins actions/checkout and actions/setup-node to exact v4 commit SHAs (adds npm registry-url), removes the prior npm auth token init step, runs npm install --ignore-scripts, adds npm audit signatures, and runs npm run release -- --ci with only GITHUB_TOKEN.

🎯 3 (Moderate) | ⏱️ ~20 minutes

🐰 Our workflows now sip carrots, not chaos,
With Dependabot watching each action's trail,
Pins in place so builds don’t wander or pause,
Audit signatures make the release tale hale,
Gently hopping forward — CI secure and hale. 🥕✨

🚥 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 accurately describes the main objective of the changeset: hardening the release workflow for supply-chain security through pinned actions, OIDC-based publishing, audit signatures, and permission tightening.
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 (2)
.github/workflows/checks.yml (1)

14-14: 💤 Low value

Consider adding persist-credentials: false for additional defense-in-depth.

While the workflow-level contents: read permission already limits what can be done with the token, explicitly setting persist-credentials: false on each actions/checkout step prevents the GITHUB_TOKEN from being persisted to disk in the .git/config file, providing an additional layer of protection against potential credential exfiltration through artifacts or logs.

🔒 Optional hardening for checkout steps

Apply this pattern to all six checkout actions in the workflow:

-      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
+        with:
+          persist-credentials: false

Also applies to: 24-24, 48-48, 78-78, 95-95, 119-119

🤖 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 at line 14, The checkout steps use
actions/checkout (e.g., the step with "uses:
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5") but do not
explicitly disable credential persistence; update each actions/checkout step
(all six occurrences referenced in the review) to include the input
persist-credentials: false so the GITHUB_TOKEN is not written to .git/config and
cannot be persisted to artifacts/logs.
.github/workflows/release.yml (1)

25-27: 💤 Low value

Consider adding persist-credentials: false for release workflow.

Similar to the checks workflow, explicitly setting persist-credentials: false provides additional defense-in-depth by preventing the GITHUB_TOKEN from being persisted to disk, even though the job already has explicit contents: write permissions scoped appropriately.

🔒 Optional hardening for checkout step
       - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
         with:
           fetch-depth: 0
+          persist-credentials: false

Note: If the release process requires pushing commits or tags using the checkout action's persisted credentials, then persist-credentials: false may need to remain unset or be set to true. Verify whether the git operations in the "git config" and "npm run release" steps rely on the persisted token or use GITHUB_TOKEN directly.

🤖 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 25 - 27, Update the
actions/checkout step in the release workflow to explicitly set
persist-credentials: false (add the persist-credentials: false key under the
existing with: block for the checkout action) to avoid persisting GITHUB_TOKEN
to the checked-out repo; confirm whether any downstream steps (e.g., the git
config or the npm run release commands) rely on persisted checkout credentials
and, if they do, either leave persist-credentials unset or change those steps to
use GITHUB_TOKEN directly before enforcing the false setting.
🤖 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:
- Line 14: The checkout steps use actions/checkout (e.g., the step with "uses:
actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5") but do not
explicitly disable credential persistence; update each actions/checkout step
(all six occurrences referenced in the review) to include the input
persist-credentials: false so the GITHUB_TOKEN is not written to .git/config and
cannot be persisted to artifacts/logs.

In @.github/workflows/release.yml:
- Around line 25-27: Update the actions/checkout step in the release workflow to
explicitly set persist-credentials: false (add the persist-credentials: false
key under the existing with: block for the checkout action) to avoid persisting
GITHUB_TOKEN to the checked-out repo; confirm whether any downstream steps
(e.g., the git config or the npm run release commands) rely on persisted
checkout credentials and, if they do, either leave persist-credentials unset or
change those steps to use GITHUB_TOKEN directly before enforcing the false
setting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 48c94e09-1e4d-4b08-978c-7d092e0312f5

📥 Commits

Reviewing files that changed from the base of the PR and between 1a88178 and 793b9ea.

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

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