From 044a91d058a0ba19687f8fb8021fe0da2fe47bbb Mon Sep 17 00:00:00 2001 From: Shivanshu07 Date: Fri, 12 Jun 2026 23:13:46 +0530 Subject: [PATCH] security: SHA-pin all GitHub Actions + add least-privilege permissions (PER-8604, PER-8608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PER-8608 (CWE-829) — every third-party action across all 11 workflows was pinned to a mutable tag, allowing a hijacked/retagged action to inject code into CI (which handles signing keys and publish tokens). Pin every `uses:` to an immutable 40-char commit SHA (tag preserved in a trailing comment): checkout, setup-node, cache, upload-artifact, download-artifact, stale, github-script, action-regex-match, pull-request-comment-branch, gha-jobid-action, winterjung/split, trigger-workflow-and-wait, create-pull-request. PER-8604 (CWE-732) — workflows ran with the implicit write-all GITHUB_TOKEN. Add a top-level `permissions: contents: read` to every workflow and minimal job-level grants only where required: - executable.yml (build, notify): contents: write — upload release assets - stale.yml: issues: write, pull-requests: write - sdk-regression.yml: statuses: write + pull-requests: read Also re PER-8610: sdk-regression.yml is issue_comment-triggered but already gates execution on an author-permission check (write/admin collaborators only); the least-privilege block above further limits the token exposed to that flow. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/draft-release.yml | 2 +- .github/workflows/executable.yml | 18 ++++++++++++------ .github/workflows/lint.yml | 9 ++++++--- .github/workflows/release.yml | 7 +++++-- .github/workflows/sdk-regression.yml | 25 +++++++++++++++++-------- .github/workflows/stale.yml | 8 +++++++- .github/workflows/test.yml | 27 +++++++++++++++------------ .github/workflows/typecheck.yml | 9 ++++++--- .github/workflows/version-bump.yml | 6 +++--- .github/workflows/windows.yml | 19 +++++++++++-------- 10 files changed, 83 insertions(+), 47 deletions(-) diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index 9a3a51854..f88ac9f92 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -19,7 +19,7 @@ jobs: startsWith(github.event.pull_request.title, 'Release ') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: ref: ${{ github.event.pull_request.merge_commit_sha }} diff --git a/.github/workflows/executable.yml b/.github/workflows/executable.yml index 44de17fc8..8fb9b78e1 100644 --- a/.github/workflows/executable.yml +++ b/.github/workflows/executable.yml @@ -2,13 +2,17 @@ name: Build Executables on: release: types: [published] +permissions: + contents: read jobs: build: name: Build Executables runs-on: macos-latest + permissions: + contents: write # upload release assets via softprops/action-gh-release steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v4 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 with: node-version: 14 architecture: x64 @@ -22,7 +26,7 @@ jobs: - name: Verify executable run: ./percy --version - name: Upload win artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: win-exe path: percy.exe @@ -38,14 +42,16 @@ jobs: needs: build name: Sign Win Executable runs-on: windows-2022 + permissions: + contents: write # upload signed Windows executable to the release steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: Download win artifact - uses: actions/download-artifact@v5 + uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: win-exe - name: Set up Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: 14 - name: Install resedit diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 569ceb8e6..e39ebe855 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -4,16 +4,19 @@ on: branches: [master] pull_request: workflow_dispatch: +permissions: + contents: read + jobs: lint: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v3 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: 14 - - uses: actions/cache@v3 + - uses: actions/cache@f4b3439a656ba812b8cb417d2d49f9c810103092 # v3.4.0 with: path: | node_modules diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ae398fc1a..254f043ee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,12 +2,15 @@ name: Release on: release: types: [published] +permissions: + contents: read + jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v5 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 24 registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/sdk-regression.yml b/.github/workflows/sdk-regression.yml index ee8c0f6d1..73f86de39 100644 --- a/.github/workflows/sdk-regression.yml +++ b/.github/workflows/sdk-regression.yml @@ -2,10 +2,19 @@ name: SDK Regression on: issue_comment: types: [created, edited] +permissions: + contents: read jobs: regression: name: regression runs-on: ubuntu-latest + # Least-privilege: read code/PR data and write commit statuses only. Note an + # author-permission guard below (check-access) already restricts triggering + # to write/admin collaborators (CWE-284 / PER-8610). + permissions: + contents: read + pull-requests: read + statuses: write if: ${{ github.event.issue.pull_request && github.event.comment.body == 'RUN_REGRESSION' }} strategy: matrix: @@ -32,7 +41,7 @@ jobs: - gatsby-plugin-percy steps: - name: Get user permissions - uses: actions/github-script@v4 + uses: actions/github-script@f891eff65186019cbb3f7190c4590bc0a1b76fbc # v4.1.0 id: check-access with: script: | @@ -44,10 +53,10 @@ jobs: - name: Check Access Level if: steps.check-access.outputs.result != 'write' && steps.check-access.outputs.result != 'admin' run: exit 1 - - uses: xt0rted/pull-request-comment-branch@v3 + - uses: xt0rted/pull-request-comment-branch@e8b8daa837e8ea7331c0003c9c316a64c6d8b0b1 # v3.0.0 if: ${{ github.event.issue.pull_request }} id: comment-branch - - uses: actions-ecosystem/action-regex-match@v2 + - uses: actions-ecosystem/action-regex-match@9e6c4fb3d5e898f505be7a1fb6e7b0a278f6665b # v2.0.2 id: regex-match with: text: ${{ steps.comment-branch.outputs.head_ref }} @@ -57,14 +66,14 @@ jobs: if: ${{ steps.regex-match.outputs.match == '' }} - name: Get Current Job Log URL - uses: Tiryoh/gha-jobid-action@v0 + uses: Tiryoh/gha-jobid-action@be260d8673c9211a84cdcf37794ebd654ba81eef # v1.4.0 id: job-url with: github_token: ${{ secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN }} job_name: "regression (${{ matrix.repo }})" - name: Output Current Job Log URL run: echo ${{ steps.jobs.outputs.html_url }} - - uses: actions/github-script@v4 + - uses: actions/github-script@f891eff65186019cbb3f7190c4590bc0a1b76fbc # v4.1.0 with: github-token: ${{ secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN }} script: | @@ -82,13 +91,13 @@ jobs: state, target_url }); - - uses: winterjung/split@v2 + - uses: winterjung/split@7f51d99e7cc1f147f6f99be75acf5e641930af88 # v2.1.0 id: split with: msg: ${{ matrix.repo }} separator: '@' - name: Trigger Workflow & Wait - uses: convictional/trigger-workflow-and-wait@v1.6.5 + uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be # v1.6.5 id: reg-test with: owner: percy @@ -99,7 +108,7 @@ jobs: client_payload: '{ "branch": "${{ steps.comment-branch.outputs.head_ref }}"}' wait_interval: 15 - name: Update Status - uses: actions/github-script@v4 + uses: actions/github-script@f891eff65186019cbb3f7190c4590bc0a1b76fbc # v4.1.0 with: github-token: ${{ secrets.WORKFLOW_DISPATCH_ACTIONS_TOKEN }} script: | diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 4c30c3a95..8e049243d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -3,11 +3,17 @@ on: schedule: - cron: '0 19 * * 2' +permissions: + contents: read + jobs: stale: runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - - uses: actions/stale@v6 + - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 with: stale-issue-message: >- This issue is stale because it has been open for more than 14 days with no activity. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3045d61cc..38050b41b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,16 +4,19 @@ on: branches: [master] pull_request: workflow_dispatch: +permissions: + contents: read + jobs: build: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v3 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: 14 - - uses: actions/cache@v3 + - uses: actions/cache@f4b3439a656ba812b8cb417d2d49f9c810103092 # v3.4.0 with: path: | node_modules @@ -28,7 +31,7 @@ jobs: ${{ hashFiles('.github/.cache-key') }}/ - run: yarn - run: yarn build - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dist path: packages/*/dist @@ -65,11 +68,11 @@ jobs: env: CLI_TEST_FAILURES_FILE: ${{ github.workspace }}/.cli-test-failures.json steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v3 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: ${{ matrix.node }} - - uses: actions/cache@v3 + - uses: actions/cache@f4b3439a656ba812b8cb417d2d49f9c810103092 # v3.4.0 with: path: | node_modules @@ -82,7 +85,7 @@ jobs: restore-keys: > ${{ runner.os }}/node-${{ matrix.node }}/ ${{ hashFiles('.github/.cache-key') }}/ - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: dist path: packages @@ -141,13 +144,13 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 50 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: 14 - - uses: actions/cache@v3 + - uses: actions/cache@f4b3439a656ba812b8cb417d2d49f9c810103092 # v3.4.0 with: path: | node_modules @@ -160,7 +163,7 @@ jobs: restore-keys: > ${{ runner.os }}/node-14/ ${{ hashFiles('.github/.cache-key') }}/ - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: dist path: packages diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 8db7d48c0..466fb94b7 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -4,16 +4,19 @@ on: branches: [master] pull_request: workflow_dispatch: +permissions: + contents: read + jobs: typecheck: name: Typecheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v3 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: 14 - - uses: actions/cache@v3 + - uses: actions/cache@f4b3439a656ba812b8cb417d2d49f9c810103092 # v3.4.0 with: path: | node_modules diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index d91179baa..4485b1d3b 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -29,11 +29,11 @@ jobs: release-pr: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: 0 - - uses: actions/setup-node@v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 24 @@ -92,7 +92,7 @@ jobs: } >> "$GITHUB_STEP_SUMMARY" - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 with: token: ${{ secrets.GITHUB_TOKEN }} base: master diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 8aa1ba4c2..adf05a68f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -4,16 +4,19 @@ on: branches: [master] pull_request: workflow_dispatch: +permissions: + contents: read + jobs: build: name: Build runs-on: windows-latest steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v3 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: 14 - - uses: actions/cache@v3 + - uses: actions/cache@f4b3439a656ba812b8cb417d2d49f9c810103092 # v3.4.0 with: path: | node_modules @@ -28,7 +31,7 @@ jobs: ${{ hashFiles('.github/.cache-key') }}/ - run: yarn - run: yarn build - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dist path: packages/*/dist @@ -64,11 +67,11 @@ jobs: env: CLI_TEST_FAILURES_FILE: ${{ github.workspace }}/.cli-test-failures.json steps: - - uses: actions/checkout@v5 - - uses: actions/setup-node@v3 + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1 with: node-version: 14 - - uses: actions/cache@v3 + - uses: actions/cache@f4b3439a656ba812b8cb417d2d49f9c810103092 # v3.4.0 with: path: | node_modules @@ -81,7 +84,7 @@ jobs: restore-keys: > ${{ runner.os }}/node-14/ ${{ hashFiles('.github/.cache-key') }}/ - - uses: actions/download-artifact@v5 + - uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0 with: name: dist path: packages