chore(ci): harden workflow permissions and gate releases to owner#211
Conversation
Tighten GITHUB_TOKEN exposure on every workflow: - ci.yml, benchmark.yml, codecov.yml: add top-level permissions: contents: read so PRs run on a read-only token. - reusable-build.yml: drop the unused issues: write grant (no step in this file touches issues) and downgrade to contents: read. - release-npm.yml, release-plz.yml: collapse top-level permissions to empty and grant the minimum each job actually needs (id-token: write for OIDC provenance, contents: write only on jobs that push tags). Add a repository_owner == rstackjs guard to all release jobs as a belt-and-suspenders check on top of workflow_dispatch + environment reviewers, so a fork that ever fires a workflow_dispatch (or a future trigger change) can't proceed.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ab7730b98c
ℹ️ 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".
There was a problem hiding this comment.
Pull request overview
This PR hardens GitHub Actions security posture by enforcing least-privilege GITHUB_TOKEN permissions across workflows and adding a defensive owner-only guard for release workflows, reducing blast radius if a job is compromised.
Changes:
- Add explicit
permissions:blocks to default tocontents: read(or{}where appropriate) instead of relying on repository defaults. - Scope elevated permissions (
contents: write,id-token: write) to only the specific release jobs that need them. - Gate release-related jobs with
if: github.repository_owner == 'rstackjs'to prevent unintended execution outside the canonical repo.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/reusable-build.yml | Drops broad permissions and restricts the reusable build workflow to contents: read. |
| .github/workflows/release-plz.yml | Sets default token permissions to none and grants id-token: write + contents: write only to the release job; adds owner guard. |
| .github/workflows/release-npm.yml | Sets default token permissions to none; grants contents: read to build matrix and contents: write + id-token: write only to release job; adds owner guard. |
| .github/workflows/codecov.yml | Adds top-level contents: read to avoid implicit default permissions. |
| .github/workflows/ci.yml | Adds top-level contents: read to avoid implicit default permissions. |
| .github/workflows/benchmark.yml | Adds top-level contents: read to avoid implicit default permissions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The guard is unnecessary because npm and crates.io trusted publishing already pin the OIDC token's repository claim. A fork that fires these workflow_dispatch workflows would mint a token for its own repo and be rejected by both registries' publisher allowlists, so the guard adds no real protection while adding noise.
Summary
Tighten
GITHUB_TOKENexposure across every workflow.Changes
Minimum-privilege
permissions:blocksci.ymlcontents: readbenchmark.ymlcontents: readcodecov.ymlcontents: readreusable-build.ymlissues: write(unused — no step touches issues)contents: readrelease-npm.ymlid-token: write+issues: write(broadcast to every job)permissions: {};buildmatrix getscontents: read;releasejob getscontents: write+id-token: writerelease-plz.ymlid-token: write+contents: writepermissions: {};release-plzjob gets the two grants it actually needsThe principle is: the default token starts at nothing, and each job earns only the scope its steps actually use. This blocks an exploit running in (say) a build matrix runner from upgrading the cached repo token to a publish credential or comment-on-issues credential.
Test plan
release-plzandrelease-npmdispatch runs still publish successfully (verified next time we cut a release)reusable-build.ymlbuild matrix continues to succeed undercontents: read