From da13991060ecf881f0d07e45d4825bfd3237e7da Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Mon, 2 Nov 2026 17:46:24 +0100 Subject: [PATCH 1/5] ci: keep the package-manager cache out of the release workflow setup-node no longer restores the shared pnpm cache in the release and snapshot jobs, which hold id-token: write for npm trusted publishing, and the snapshot job no longer persists the checkout token. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/npm-release-trigger.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/npm-release-trigger.yml b/.github/workflows/npm-release-trigger.yml index 9a5afbdef..6bbb24ef5 100644 --- a/.github/workflows/npm-release-trigger.yml +++ b/.github/workflows/npm-release-trigger.yml @@ -41,7 +41,7 @@ jobs: - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: .nvmrc - cache: pnpm + package-manager-cache: false # no shared cache in the release path registry-url: 'https://registry.npmjs.org' - name: Install npm with staged-publishing support run: npm install --global npm@11.15.0 @@ -100,11 +100,12 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false - uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: .nvmrc - cache: pnpm + package-manager-cache: false # no shared cache in the release path registry-url: 'https://registry.npmjs.org' - name: Install npm with staged-publishing support run: npm install --global npm@11.15.0 From a2fa488fb6a28a06e83d7183aae686a3cf25a1fe Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Mon, 2 Nov 2026 17:46:24 +0100 Subject: [PATCH 2/5] ci: pin actions, scope permissions and set job timeouts Pin every action and reusable workflow to a full commit SHA with its version in a comment, default workflows to no permissions and grant each job what it uses, stop persisting the checkout token, and give each job a timeout above its slowest recent run. The pnpm-format label workflow passes only the secret it needs instead of inheriting all of them, and removes the label through env variables instead of interpolating the event into the shell script. Co-Authored-By: Claude Opus 5.5 --- .../actions/install-dependencies/action.yml | 6 ++--- .github/workflows/changeset-check-trigger.yml | 9 ++++--- .github/workflows/claude.yml | 6 ++++- .github/workflows/on_pr_pnpm-format-label.yml | 22 ++++++++++------ .github/workflows/pnpm-format.yml | 13 ++++++++-- .github/workflows/tests.yml | 25 ++++++++++++++++--- 6 files changed, 60 insertions(+), 21 deletions(-) diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 1eeeaf935..4d885806a 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -5,12 +5,12 @@ runs: steps: - name: Setup Node - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version-file: .nvmrc - name: Setup PNPM - uses: pnpm/action-setup@v3 + uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 with: version: 10 run_install: false @@ -22,7 +22,7 @@ runs: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - name: Setup pnpm cache - uses: actions/cache@v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: path: | ${{ steps.pnpm-cache.outputs.STORE_PATH }} diff --git a/.github/workflows/changeset-check-trigger.yml b/.github/workflows/changeset-check-trigger.yml index 03230ded0..92075370f 100644 --- a/.github/workflows/changeset-check-trigger.yml +++ b/.github/workflows/changeset-check-trigger.yml @@ -4,10 +4,11 @@ on: pull_request: branches: [master] -permissions: - contents: read - pull-requests: write +permissions: {} jobs: check: - uses: 0xPolygon/pipelines/.github/workflows/apps-changeset-check.yml@main + permissions: + contents: read + pull-requests: write + uses: 0xPolygon/pipelines/.github/workflows/apps-changeset-check.yml@3c69da7f7438cb371c56851dab2cf00c5d7dbce6 # main diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 6e12002ac..6a56564ab 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -10,6 +10,8 @@ on: pull_request_review: types: [submitted] +permissions: {} + jobs: claude: if: | @@ -18,6 +20,7 @@ jobs: (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) runs-on: ubuntu-latest + timeout-minutes: 60 permissions: contents: read pull-requests: read @@ -26,9 +29,10 @@ jobs: actions: read # Required for Claude to read CI results on PRs steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: fetch-depth: 1 + persist-credentials: false - name: Run Claude Code id: claude diff --git a/.github/workflows/on_pr_pnpm-format-label.yml b/.github/workflows/on_pr_pnpm-format-label.yml index 84fb27cb3..f677c6c06 100644 --- a/.github/workflows/on_pr_pnpm-format-label.yml +++ b/.github/workflows/on_pr_pnpm-format-label.yml @@ -4,20 +4,28 @@ on: pull_request: types: [labeled] +permissions: {} + jobs: proto: if: ${{ github.event.label.name == 'pnpm format' }} uses: ./.github/workflows/pnpm-format.yml - secrets: inherit + permissions: + contents: read + secrets: + GH_TOKEN_GIT_COMMIT: ${{ secrets.GH_TOKEN_GIT_COMMIT }} rm: if: ${{ github.event.label.name == 'pnpm format' }} runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + pull-requests: write steps: - name: Remove the label - run: | - LABEL=$(echo "${{ github.event.label.name }}" | sed 's/ /%20/g') - curl -X DELETE \ - -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ - -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels/$LABEL + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + LABEL: ${{ github.event.label.name }} + run: gh api -X DELETE "repos/$REPO/issues/$PR_NUMBER/labels/${LABEL// /%20}" diff --git a/.github/workflows/pnpm-format.yml b/.github/workflows/pnpm-format.yml index 1be36e1a6..f5163b2dc 100644 --- a/.github/workflows/pnpm-format.yml +++ b/.github/workflows/pnpm-format.yml @@ -2,23 +2,32 @@ name: pnpm format on: workflow_call: + secrets: + GH_TOKEN_GIT_COMMIT: + required: true + +permissions: {} jobs: run: runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 with: ref: ${{ github.head_ref }} fetch-depth: 20 + persist-credentials: false # the commit-back action clones with its own token - uses: ./.github/actions/install-dependencies - run: pnpm format - name: Commit back - uses: 0xsequence/actions/git-commit@v0.0.4 + uses: 0xsequence/actions/git-commit@d6560e817fb0e198d271e8c72f4a243d02188a8a # v0.0.4 env: API_TOKEN_GITHUB: ${{ secrets.GH_TOKEN_GIT_COMMIT }} with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1be53cad4..c7d250138 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,20 +2,32 @@ on: [push] name: tests +permissions: {} + jobs: install: name: Install dependencies runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false - uses: ./.github/actions/install-dependencies build: name: Run build runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read needs: [install] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false - uses: ./.github/actions/install-dependencies - run: pnpm clean - run: pnpm build @@ -25,12 +37,17 @@ jobs: tests: name: Run all tests runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read needs: [build] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false - uses: ./.github/actions/install-dependencies - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 + uses: foundry-rs/foundry-toolchain@dba66c27052e63476d886f22216e119ba7e615bf # v1.9.2 with: version: v1.5.0 - name: Start Anvil in background From 16b27bf89422e660afeb9b770002cacbf6666344 Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Mon, 2 Nov 2026 17:46:24 +0100 Subject: [PATCH 3/5] ci: add dependency review, zizmor and Dependabot for actions Dependency review fails PRs that add a dependency with a high-severity advisory or a licence outside the allowlist. zizmor lints workflow changes on PRs and weekly. Dependabot keeps the pinned action SHAs current, with a 7-day cooldown before a new release is proposed. Co-Authored-By: Claude Opus 5.5 --- .github/dependabot.yml | 12 ++++++++++ .github/workflows/dependency-review.yml | 24 ++++++++++++++++++++ .github/workflows/zizmor.yml | 29 +++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..b054c0c42 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + cooldown: + default-days: 7 + groups: + actions: + patterns: ["*"] diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000..bc66951e2 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,24 @@ +name: Dependency review + +on: + pull_request: + +permissions: {} + +jobs: + dependency-review: + name: Dependency review + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + pull-requests: write # comment-summary-in-pr + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + fail-on-severity: high + allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD, MPL-2.0, Unlicense, CC0-1.0 + comment-summary-in-pr: on-failure diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000..fc0606d4b --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,29 @@ +name: Workflow lint + +on: + pull_request: + paths: [".github/**"] + schedule: + - cron: "41 6 * * 1" + workflow_dispatch: + +permissions: {} + +jobs: + zizmor: + name: zizmor + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + actions: read + security-events: write # SARIF upload to code scanning + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + persist-credentials: false + - uses: zizmorcore/zizmor-action@85b2b9fb8e3325e7a23d7d0a9d7ba5ae0f999e3e # v0.7.1 + with: + min-severity: medium + min-confidence: medium + advanced-security: true From 5e39612cc76c7375799d376104a8b4624fb8cfac Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Tue, 3 Nov 2026 13:44:31 +0100 Subject: [PATCH 4/5] ci: accept the permissive licences already in the dependency tree Dependency review only passed because no dependency changed. Add the permissive licences GitHub reports for packages already in the tree (BlueOak-1.0.0, CC-BY-4.0, Python-2.0, BSD-2-Clause-Views, Zlib, ...) and exempt by name the packages already present under copyleft or unparsed licence data, so bumping them doesn't fail the check while a new dependency with such a licence still does. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/dependency-review.yml | 40 +++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index bc66951e2..2d42473b5 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -14,11 +14,47 @@ jobs: contents: read pull-requests: write # comment-summary-in-pr steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 with: fail-on-severity: high - allow-licenses: MIT, Apache-2.0, BSD-2-Clause, BSD-3-Clause, ISC, 0BSD, MPL-2.0, Unlicense, CC0-1.0 + allow-licenses: >- + MIT, + Apache-2.0, + BSD-2-Clause, + BSD-3-Clause, + ISC, + 0BSD, + MPL-2.0, + Unlicense, + CC0-1.0, + BlueOak-1.0.0, + BSD-2-Clause-Views, + CC-BY-3.0, + CC-BY-4.0, + MIT-0, + MITNFA, + PostgreSQL, + Python-2.0, + Unicode-3.0, + Zlib, + LicenseRef-scancode-google-patent-license-golang + # Already in the lockfile under copyleft or unparsed licence data; new + # dependencies with those licences still fail. + allow-dependencies-licenses: >- + pkg:npm/@img/sharp-libvips-darwin-arm64, + pkg:npm/@img/sharp-libvips-darwin-x64, + pkg:npm/@img/sharp-libvips-linux-arm, + pkg:npm/@img/sharp-libvips-linux-arm64, + pkg:npm/@img/sharp-libvips-linux-ppc64, + pkg:npm/@img/sharp-libvips-linux-riscv64, + pkg:npm/@img/sharp-libvips-linux-s390x, + pkg:npm/@img/sharp-libvips-linux-x64, + pkg:npm/@img/sharp-libvips-linuxmusl-arm64, + pkg:npm/@img/sharp-libvips-linuxmusl-x64, + pkg:npm/@img/sharp-win32-arm64, + pkg:npm/@img/sharp-win32-ia32, + pkg:npm/@img/sharp-win32-x64 comment-summary-in-pr: on-failure From 3d844d4c6a4ce1af57dab4a127d9269df8a2f724 Mon Sep 17 00:00:00 2001 From: Taylan Pince Date: Tue, 3 Nov 2026 13:44:31 +0100 Subject: [PATCH 5/5] ci: move Node 20 actions to their current releases checkout, setup-node, cache and pnpm/action-setup run on the deprecated Node 20 runtime at the versions pinned so far; pin the releases the release workflow already uses. pnpm/action-setup now takes the version from packageManager. Dependabot also scans .github/actions, which directory: / leaves out. zizmor no longer requests contents and actions read, which only private repositories need. Co-Authored-By: Claude Opus 5.5 --- .github/actions/install-dependencies/action.yml | 7 +++---- .github/dependabot.yml | 2 +- .github/workflows/claude.yml | 2 +- .github/workflows/pnpm-format.yml | 2 +- .github/workflows/tests.yml | 6 +++--- .github/workflows/zizmor.yml | 4 +--- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 4d885806a..a7ba57eac 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -5,14 +5,13 @@ runs: steps: - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: .nvmrc - name: Setup PNPM - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: - version: 10 run_install: false - name: Get pnpm store directory @@ -22,7 +21,7 @@ runs: echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - name: Setup pnpm cache - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5.1.0 with: path: | ${{ steps.pnpm-cache.outputs.STORE_PATH }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b054c0c42..9b63c6f5b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,7 +1,7 @@ version: 2 updates: - package-ecosystem: github-actions - directory: / + directories: ["/", "/.github/actions/*"] schedule: interval: weekly day: monday diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 6a56564ab..e6dc0f7a3 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -29,7 +29,7 @@ jobs: actions: read # Required for Claude to read CI results on PRs steps: - name: Checkout repository - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 persist-credentials: false diff --git a/.github/workflows/pnpm-format.yml b/.github/workflows/pnpm-format.yml index f5163b2dc..98966fde7 100644 --- a/.github/workflows/pnpm-format.yml +++ b/.github/workflows/pnpm-format.yml @@ -16,7 +16,7 @@ jobs: contents: read steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.head_ref }} fetch-depth: 20 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c7d250138..47ed8f2e9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -12,7 +12,7 @@ jobs: permissions: contents: read steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: ./.github/actions/install-dependencies @@ -25,7 +25,7 @@ jobs: contents: read needs: [install] steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: ./.github/actions/install-dependencies @@ -42,7 +42,7 @@ jobs: contents: read needs: [build] steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: ./.github/actions/install-dependencies diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index fc0606d4b..638ec6f91 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -15,11 +15,9 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 permissions: - contents: read - actions: read security-events: write # SARIF upload to code scanning steps: - - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - uses: zizmorcore/zizmor-action@85b2b9fb8e3325e7a23d7d0a9d7ba5ae0f999e3e # v0.7.1