From 0eec03dd28d69cf7b50b10e827ba4df909df1e8c Mon Sep 17 00:00:00 2001 From: jrfnl Date: Mon, 6 Jul 2026 18:04:40 +0200 Subject: [PATCH] GH Actions: set permissions for each workflow/job > Users frequently over-scope their workflow and job permissions, or set broad workflow-level permissions without realizing that all jobs inherit those permissions. > > Furthermore, users often don't realize that the _default_ `GITHUB_TOKEN` permissions can be very broad, meaning that workflows that don't configure any permissions at all can _still_ provide excessive credentials to their individual jobs. > > **Remediation** > In general, permissions should be declared as minimally as possible, and as close to their usage site as possible. > > In practice, this means that workflows should almost always set `permissions: {}` at the workflow level to disable all permissions by default, and then set specific job-level permissions as needed. Includes minor tweaks for consistency in the comments and the order of the GHA instructions. Refs: * https://docs.zizmor.sh/audits/#excessive-permissions --- .github/workflows/merge-conflict-check.yml | 6 ++++++ .github/workflows/qa.yml | 6 ++++++ .github/workflows/reusable-actionlint.yml | 6 ++++++ .github/workflows/reusable-merge-conflict-check.yml | 11 +++++------ README.md | 13 +++++++++++-- 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/.github/workflows/merge-conflict-check.yml b/.github/workflows/merge-conflict-check.yml index 0236e8e..1947444 100644 --- a/.github/workflows/merge-conflict-check.yml +++ b/.github/workflows/merge-conflict-check.yml @@ -12,8 +12,14 @@ on: - synchronize - reopened +# Permissions should be configured at the job level. +permissions: {} + jobs: check-prs: + permissions: + pull-requests: write # To add and remove labels and comments on a PR. + if: github.repository_owner == 'Yoast' name: Check PRs for merge conflicts diff --git a/.github/workflows/qa.yml b/.github/workflows/qa.yml index ee9e230..9884ecc 100644 --- a/.github/workflows/qa.yml +++ b/.github/workflows/qa.yml @@ -7,8 +7,14 @@ on: # Allow manually triggering the workflow. workflow_dispatch: +# Permissions should be configured at the job level. +permissions: {} + jobs: actionlint: + permissions: + contents: read # To clone the repo. + name: 'Lint GH Action workflows' uses: ./.github/workflows/reusable-actionlint.yml with: diff --git a/.github/workflows/reusable-actionlint.yml b/.github/workflows/reusable-actionlint.yml index ce712ce..f0f3eeb 100644 --- a/.github/workflows/reusable-actionlint.yml +++ b/.github/workflows/reusable-actionlint.yml @@ -19,8 +19,14 @@ on: required: false default: '' +# Permissions should be configured at the job level. +permissions: {} + jobs: actionlint: + permissions: + contents: read # To clone the repo. + name: 'Actionlint' runs-on: ubuntu-latest diff --git a/.github/workflows/reusable-merge-conflict-check.yml b/.github/workflows/reusable-merge-conflict-check.yml index 1215713..e03c58e 100644 --- a/.github/workflows/reusable-merge-conflict-check.yml +++ b/.github/workflows/reusable-merge-conflict-check.yml @@ -37,23 +37,22 @@ on: required: false default: true -# Default to no permissions at all; the job below opts in to only what it needs. +# Permissions should be configured at the job level. permissions: {} jobs: check-prs: - name: Merge conflict check + permissions: + issues: write # Needed to create the dirty label. + pull-requests: write # To add and remove labels and comments on a PR. + name: Merge conflict check runs-on: ubuntu-latest # Safety net: the merge conflict check retries for at most ~10 minutes # (retryAfter 120s * retryMax 5), so anything beyond this is a runaway job. timeout-minutes: 20 - permissions: - issues: write # Needed to create the dirty label and to add/remove labels on PRs. - pull-requests: write # Needed to comment and to mark those comments as resolved. - steps: - name: "Create label if it doesn't exist" uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 diff --git a/README.md b/README.md index 5a0d893..7d24950 100644 --- a/README.md +++ b/README.md @@ -18,13 +18,17 @@ Aside from the community health files, this repository also offers a number of r ### Available re-usable workflows The following re-usable workflows are available: -* [`reusable-actionlint.yml`][reusable-actionlint] which runs a [static analysis check][actionlint] on GitHub Actions workflow files only. +* [`reusable-actionlint.yml`][reusable-actionlint] which runs a [static analysis check][actionlint] on GitHub Actions workflow files only. **Inputs**: - `shellcheck`: Optional. Whether to enable shellcheck. Defaults to 'true'. - `pyflakes`: Optional. Whether to enable pyflakes. Defaults to 'true'. - `args`: Optional. Command line arguments to pass to the actionlint command. Defaults to no arguments. -* [`reusable-merge-conflict-check.yml`][reusable-mergeconflict] to check whether open PRs are in a merge conflict state. + **Permissions**: + The `reusable-actionlint` workflow needs the following GH Action permissions - these should be set at "job" level: + - `contents: read # To clone the repo.` + +* [`reusable-merge-conflict-check.yml`][reusable-mergeconflict] to check whether open PRs are in a merge conflict state. **Inputs**: - `dirtyLabel`: Optional. Name of the label which indicates that the branch is dirty. Defaults to 'merge conflict'. - `removeOnDirtyLabel`: Optional. Name of the label which should be removed. Defaults to none. @@ -38,6 +42,11 @@ The following re-usable workflows are available: Note that changing `commentOnDirty` will leave already posted comments unmatched, so those will stay visible instead of being collapsed. + **Permissions**: + The `reusable-merge-conflict-check` workflow needs the following GH Action permissions - these should be set at "job" level: + - `issues: write # Needed to create the dirty label.` (only needed if the `dirtyLabel` may not exist on a repo) + - `pull-requests: write # To add and remove labels and comments on a PR.` + ## A .github repository with versioning ?