From ebddc5e376252f054009bec599032a90166ee720 Mon Sep 17 00:00:00 2001 From: kingchenc Date: Mon, 1 Jun 2026 02:01:31 +0200 Subject: [PATCH] ci: set least-privilege top-level token permissions (P21.1b) (#103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auto-injected GITHUB_TOKEN defaulted to write-all in ci.yml, bench.yml and release.yml (no top-level permissions block), and codeql.yml declared its scopes only at job level. Add a top-level `permissions: contents: read` to all four so the token starts read-only and only the jobs that genuinely write through it raise the scope: - release.yml: github-release keeps contents: write; node-/wasm-publish and attestations keep their id-token / attestations: write blocks. The cargo/python/node publish jobs push to crates.io/PyPI/npm via their own registry secrets, not the GITHUB_TOKEN, so read-only is correct for them. - codeql.yml: analyze keeps security-events: write (job level). - ci.yml / bench.yml: no job writes back to the repo (coverage uploads via CODECOV_TOKEN; bench only uploads an artifact), so no job override is needed. sync-about.yml already had a top-level block but at contents: write; demote the top level to read and move contents: write down to the single `sync` job (the PR-head counter push is the only GITHUB_TOKEN write). The cross-repo About/docs/webpage/org writes are unaffected — they run through the fine-grained ABOUT_SYNC_TOKEN, which the permissions key does not govern. Raises OpenSSF Scorecard Token-Permissions from 0 toward 10. --- .github/workflows/bench.yml | 7 +++++++ .github/workflows/ci.yml | 7 +++++++ .github/workflows/codeql.yml | 7 +++++++ .github/workflows/release.yml | 11 +++++++++++ .github/workflows/sync-about.yml | 18 ++++++++++++------ 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index ac43043c..5f179499 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -22,6 +22,13 @@ on: required: false default: "10" +# Least-privilege default for the auto-injected GITHUB_TOKEN. The single job +# only builds and uploads an artifact (upload-artifact uses the artifact +# storage API, not the contents scope), so it never needs repo write (OpenSSF +# Scorecard: Token-Permissions). +permissions: + contents: read + env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91ce89dd..7ef29e5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,13 @@ on: pull_request: branches: [main] +# Least-privilege default for the auto-injected GITHUB_TOKEN. None of the CI +# jobs write back to the repo — coverage uploads via CODECOV_TOKEN, everything +# else is build/test/lint — so a read-only token is sufficient (OpenSSF +# Scorecard: Token-Permissions). +permissions: + contents: read + env: CARGO_TERM_COLOR: always RUSTFLAGS: "-D warnings" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 215d58e6..4bb6a168 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,6 +12,13 @@ on: schedule: - cron: '31 3 * * 0' # Sundays 03:31 UTC +# Least-privilege default for the auto-injected GITHUB_TOKEN. The analyze job +# raises exactly the scopes CodeQL needs (security-events: write to upload +# results) in its own job-level block below; this top-level read-only default +# covers any future job (OpenSSF Scorecard: Token-Permissions). +permissions: + contents: read + jobs: analyze: name: Analyze (${{ matrix.language }}) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7fcd1460..35b63490 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,17 @@ on: tags: ["v*"] workflow_dispatch: +# Least-privilege default for the auto-injected GITHUB_TOKEN. The publish jobs +# (cargo/python/node) push to external registries via their own secrets +# (CARGO_REGISTRY_TOKEN / PYPI_API_TOKEN / NPM_TOKEN), not the GITHUB_TOKEN, so +# they need no repo write. The jobs that genuinely write through the +# GITHUB_TOKEN — github-release (contents: write), node-/wasm-publish and +# attestations (id-token / attestations: write) — declare those rights in their +# own job-level permissions blocks, which override this default (OpenSSF +# Scorecard: Token-Permissions). +permissions: + contents: read + env: CARGO_TERM_COLOR: always diff --git a/.github/workflows/sync-about.yml b/.github/workflows/sync-about.yml index cf1a0c61..f9c65de7 100644 --- a/.github/workflows/sync-about.yml +++ b/.github/workflows/sync-about.yml @@ -55,18 +55,24 @@ on: types: [opened, synchronize, reopened] workflow_dispatch: -# `contents: write` is needed so the workflow can push the counter -# fix-up commit to the PR head branch via the auto-provided -# GITHUB_TOKEN. The wider About / Wiki writes still go through the -# fine-grained PAT (ABOUT_SYNC_TOKEN) because they need -# `Administration: write` (gh repo edit) which GITHUB_TOKEN lacks. +# Least-privilege default for the auto-injected GITHUB_TOKEN. The `contents: +# write` the workflow needs — to push the counter fix-up commit to the PR head +# branch — is raised at the job level below, not here, so the top-level default +# stays read-only (OpenSSF Scorecard: Token-Permissions). The wider About / +# docs / webpage / org writes still go through the fine-grained PAT +# (ABOUT_SYNC_TOKEN), which the `permissions:` key does not govern at all. permissions: - contents: write + contents: read pull-requests: read jobs: sync: runs-on: ubuntu-latest + # The only GITHUB_TOKEN write in this workflow: pushing the counter fix-up + # commit onto a same-repo PR head branch (git push origin HEAD:). + permissions: + contents: write + pull-requests: read steps: # On PRs from forks the head ref lives in another repo; pushing # back to it from this workflow is blocked by GitHub. We still