From 1401cb4f998dcc86a3726f9e86ffa3aec67a7d40 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sat, 15 Mar 2025 11:58:36 -0700 Subject: [PATCH] Configure permissions of `GITHUB_TOKEN` in workflows `GITHUB_TOKEN` is an access token that is automatically generated and made accessible for use in GitHub Actions workflow runs. The global default permissions of this token for workflow runs in a trusted context (i.e., not triggered by a `pull_request` event from a fork) are set in the GitHub enterprise/organization/repository's administrative settings, giving it either read-only or write permissions in all scopes. In the case of a read-only default configuration, any workflow operations that require write permissions would fail with an error like: > 403: Resource not accessible by integration In the case of a write default configuration, workflows have unnecessary permissions, which violates the security principle of least privilege. For this reason, GitHub Actions now allows fine grained control at a per-workflow or per-workflow job scope of the permissions provided to the token. This is done using the `permissions` workflow key, which is used here to configure the workflows for only the permissions require by each individual job. Configuration Granularity ------------------------- I chose to always configure permissions at the job level even though in some cases the same permissions configuration could be used for all jobs in a workflow. Even if functionally equivalent, I think it is semantically more appropriate to always set the permissions at the job scope since the intention is to make the most granular possible permissions configuration. Hopefully this approach will increase the likelihood that appropriate permissions configurations will be made in any additional jobs that are added to the workflows in the future. Security Implications --------------------- The automatic permissions downgrade from write to read for workflow runs in an untrusted context (e.g., triggered by a `pull_request` event from a fork) is unaffected by this change. API Request Implications ------------------------ Even when all permissions are withheld (`permissions: {}`), the token still provides the authenticated API request rate limiting allowance (authenticating API requests to avoid rate limiting is a one of the uses of the token in these workflows). Excess Permissions ------------------ Read permissions are required in the "contents" scope in order to checkout private repositories. Even though those permissions are not required when the workflows are installed in this public repositories, these workflows are "templates", intended to be applicable in public and private repositories both. So a small excess in permissions was chosen instead of the alternative of having to maintain separate variants of each workflow for use in public or private repos. For the sake of maintainability, it is best to avoid any unnecessary differences between the files in this repository and the contents of the upstream "templates". --- .github/workflows/check-general-formatting-task.yml | 3 +++ .github/workflows/check-go-dependencies-task.yml | 5 +++++ .github/workflows/check-go-task.yml | 11 +++++++++++ .github/workflows/check-license.yml | 3 +++ .github/workflows/check-markdown-task.yml | 5 +++++ .github/workflows/check-npm-task.yml | 8 +++++--- .github/workflows/check-prettier-formatting-task.yml | 2 ++ .github/workflows/check-taskfiles.yml | 3 +++ .github/workflows/check-workflows-task.yml | 2 ++ .github/workflows/check-yaml-task.yml | 3 +++ .github/workflows/release-go-crosscompile-task.yml | 6 ++++++ .github/workflows/spell-check-task.yml | 2 ++ .github/workflows/sync-labels-npm.yml | 6 ++++++ 13 files changed, 56 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check-general-formatting-task.yml b/.github/workflows/check-general-formatting-task.yml index 9a2d420..185ed32 100644 --- a/.github/workflows/check-general-formatting-task.yml +++ b/.github/workflows/check-general-formatting-task.yml @@ -15,6 +15,7 @@ on: jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -40,6 +41,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Set environment variables diff --git a/.github/workflows/check-go-dependencies-task.yml b/.github/workflows/check-go-dependencies-task.yml index 0088c4e..34a8e01 100644 --- a/.github/workflows/check-go-dependencies-task.yml +++ b/.github/workflows/check-go-dependencies-task.yml @@ -37,6 +37,7 @@ on: jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -62,6 +63,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository @@ -119,6 +122,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository diff --git a/.github/workflows/check-go-task.yml b/.github/workflows/check-go-task.yml index 4a03eaf..91c5363 100644 --- a/.github/workflows/check-go-task.yml +++ b/.github/workflows/check-go-task.yml @@ -31,6 +31,7 @@ on: jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -57,6 +58,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false @@ -90,6 +93,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false @@ -126,6 +131,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false @@ -162,6 +169,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false @@ -198,6 +207,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false diff --git a/.github/workflows/check-license.yml b/.github/workflows/check-license.yml index 39f738d..a33e8e2 100644 --- a/.github/workflows/check-license.yml +++ b/.github/workflows/check-license.yml @@ -35,6 +35,7 @@ on: jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -60,6 +61,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository diff --git a/.github/workflows/check-markdown-task.yml b/.github/workflows/check-markdown-task.yml index c8f3ee3..9493920 100644 --- a/.github/workflows/check-markdown-task.yml +++ b/.github/workflows/check-markdown-task.yml @@ -39,6 +39,7 @@ on: jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -64,6 +65,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository @@ -90,6 +93,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository diff --git a/.github/workflows/check-npm-task.yml b/.github/workflows/check-npm-task.yml index ee41352..ca41257 100644 --- a/.github/workflows/check-npm-task.yml +++ b/.github/workflows/check-npm-task.yml @@ -24,12 +24,10 @@ on: workflow_dispatch: repository_dispatch: -permissions: - contents: read - jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -56,6 +54,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false @@ -90,6 +90,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false diff --git a/.github/workflows/check-prettier-formatting-task.yml b/.github/workflows/check-prettier-formatting-task.yml index 2abad99..f74f831 100644 --- a/.github/workflows/check-prettier-formatting-task.yml +++ b/.github/workflows/check-prettier-formatting-task.yml @@ -233,6 +233,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository diff --git a/.github/workflows/check-taskfiles.yml b/.github/workflows/check-taskfiles.yml index 752ca4d..70b9b59 100644 --- a/.github/workflows/check-taskfiles.yml +++ b/.github/workflows/check-taskfiles.yml @@ -29,6 +29,7 @@ on: jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -55,6 +56,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false diff --git a/.github/workflows/check-workflows-task.yml b/.github/workflows/check-workflows-task.yml index 32d75ac..714380a 100644 --- a/.github/workflows/check-workflows-task.yml +++ b/.github/workflows/check-workflows-task.yml @@ -26,6 +26,8 @@ on: jobs: validate: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository diff --git a/.github/workflows/check-yaml-task.yml b/.github/workflows/check-yaml-task.yml index 5194fbf..9bd15ad 100644 --- a/.github/workflows/check-yaml-task.yml +++ b/.github/workflows/check-yaml-task.yml @@ -49,6 +49,7 @@ on: jobs: run-determination: runs-on: ubuntu-latest + permissions: {} outputs: result: ${{ steps.determination.outputs.result }} steps: @@ -75,6 +76,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read strategy: fail-fast: false diff --git a/.github/workflows/release-go-crosscompile-task.yml b/.github/workflows/release-go-crosscompile-task.yml index 78dd996..c355b5c 100644 --- a/.github/workflows/release-go-crosscompile-task.yml +++ b/.github/workflows/release-go-crosscompile-task.yml @@ -20,6 +20,8 @@ on: jobs: create-release-artifacts: runs-on: ubuntu-latest + permissions: + contents: read strategy: matrix: @@ -87,6 +89,8 @@ jobs: outputs: checksum-darwin_amd64: ${{ steps.re-package.outputs.checksum-darwin_amd64 }} checksum-darwin_arm64: ${{ steps.re-package.outputs.checksum-darwin_arm64 }} + permissions: + contents: read env: GON_CONFIG_PATH: gon.config.hcl @@ -198,6 +202,8 @@ jobs: create-release: runs-on: ubuntu-latest needs: notarize-macos + permissions: + contents: write steps: - name: Download artifact diff --git a/.github/workflows/spell-check-task.yml b/.github/workflows/spell-check-task.yml index 7e08739..e00c308 100644 --- a/.github/workflows/spell-check-task.yml +++ b/.github/workflows/spell-check-task.yml @@ -45,6 +45,8 @@ jobs: needs: run-determination if: needs.run-determination.outputs.result == 'true' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository diff --git a/.github/workflows/sync-labels-npm.yml b/.github/workflows/sync-labels-npm.yml index 88c6956..8369ea4 100644 --- a/.github/workflows/sync-labels-npm.yml +++ b/.github/workflows/sync-labels-npm.yml @@ -30,6 +30,8 @@ on: jobs: check: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout repository @@ -65,6 +67,7 @@ jobs: download: needs: check runs-on: ubuntu-latest + permissions: {} strategy: matrix: @@ -92,6 +95,9 @@ jobs: sync: needs: download runs-on: ubuntu-latest + permissions: + contents: read + issues: write steps: - name: Set environment variables