From e3e41cb57a069264c4e7735e68a895de73577be8 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 11:27:07 -0400 Subject: [PATCH 01/22] Add Dependabot cooldown for supply chain attack mitigation Adds a 7-day cooldown before Dependabot proposes updates to GitHub Actions dependencies. This gives the community time to discover and report any supply chain compromise in a newly released version before it lands in a PR. Co-Authored-By: Claude Sonnet 4.6 --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f6faee6938..c20187016c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,3 +8,5 @@ updates: github-actions: patterns: - "*" + cooldown: + default-days: 7 From 99799241c09a61480c30dc7c262661ed66e082f5 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 11:34:43 -0400 Subject: [PATCH 02/22] Suppress zizmor false positive for pull_request_target trigger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zizmor flags pull_request_target as a dangerous trigger because it runs with write permissions and access to secrets, making it risky when combined with code checked out from a fork. This workflow never checks out any code — it only makes GitHub API calls using the built-in token — so the trigger is safe here. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/pull-requests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 99bb1af860..040250fb55 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -4,7 +4,7 @@ name: Pull Requests # https://github.com/laravel/.github/blob/main/.github/workflows/pull-requests.yml on: - pull_request_target: + pull_request_target: # zizmor: ignore[dangerous-triggers] types: - opened From 0d790b031e2bbbfc304f09e22a3559dcb273c721 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 11:40:15 -0400 Subject: [PATCH 03/22] Scope workflow token permissions to minimum required MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitHub Actions defaults to overly broad token permissions when no permissions block is defined. Each job now declares only what it needs: - code-style-lint: contents:read — only checks out and reads code - pr-title: none — only uses event context variables, no token ops - release: contents:write — creates releases and uploads assets - stale: issues:write + pull-requests:write — comments and closes stale items - tests (php/js): contents:read — checks out and runs tests, no writes - tests (slack): actions:read — reads workflow conclusion to decide whether to notify Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/code-style-lint.yml | 2 ++ .github/workflows/pr-title.yml | 1 + .github/workflows/release.yml | 2 ++ .github/workflows/stale.yml | 3 +++ .github/workflows/tests.yml | 6 ++++++ 5 files changed, 14 insertions(+) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index 9e90858df0..4e19e052ab 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -6,6 +6,8 @@ on: jobs: lint-code-styling: runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Checkout code diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index a4ecda7248..48a9abcb04 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -7,6 +7,7 @@ on: jobs: pr-title: runs-on: ubuntu-latest + permissions: {} steps: - name: Validate PR title matches target branch env: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bd472c2074..47b672b7f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,8 @@ on: jobs: build: runs-on: ubuntu-latest + permissions: + contents: write steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 151e58c369..b251d28e09 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,6 +7,9 @@ on: jobs: stale: runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write steps: - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fca6256b8f..02e022709f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,8 @@ jobs: php-tests: runs-on: ${{ matrix.os }} if: "!contains(github.event.head_commit.message, '[ci skip]')" + permissions: + contents: read strategy: matrix: @@ -96,6 +98,8 @@ jobs: js-tests: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '[ci skip]')" + permissions: + contents: read name: JavaScript tests @@ -146,6 +150,8 @@ jobs: name: Slack Notification runs-on: ubuntu-latest needs: [php-tests, js-tests] + permissions: + actions: read if: always() steps: - uses: technote-space/workflow-conclusion-action@45ce8e0eb155657ab8ccf346ade734257fd196a5 # v3 From 50bd9e43cd69d09665be473b77c8b02d322199c0 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 11:43:19 -0400 Subject: [PATCH 04/22] Disable auto-caching in release workflow to prevent cache poisoning actions/setup-node enables dependency caching automatically when package.json includes a packageManager or devEngines.packageManager field. A poisoned cache could silently contaminate published release artifacts. Explicitly disabling it ensures release builds always use a clean install. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 47b672b7f2..bef88042b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,6 +18,7 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 20.19.0 + package-manager-cache: false - name: Install dependencies run: npm ci From cf0b1556eb1c05770d4e93851de14d984b03a928 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 11:47:50 -0400 Subject: [PATCH 05/22] Prevent credential persistence in checkout steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, actions/checkout writes the GitHub token into the local Git config so subsequent steps can push or pull. Any step after checkout — including third-party actions — can read that token. Setting persist-credentials: false removes it immediately after checkout, limiting the token's exposure to only the checkout step itself. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/code-style-lint.yml | 2 ++ .github/workflows/release.yml | 2 ++ .github/workflows/tests.yml | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index 4e19e052ab..16e1b57362 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -12,6 +12,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Get changed files id: changed-files diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bef88042b3..5c03c0e1ac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Use Node.js 20.19.0 uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 02e022709f..55481dc705 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -33,6 +33,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Get changed files id: changed-files @@ -106,6 +108,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Get changed files id: changed-files From f0897fff26a2f468eee2388c0414e3e8fbd0ccff Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 11:55:06 -0400 Subject: [PATCH 06/22] Add zizmor GitHub Actions security analysis workflow Runs zizmor on every push to master/*.x and on all PRs. Results are uploaded as SARIF and will appear in the Security tab. The workflow is intentionally added before all findings are resolved so we can see them surface in the UI. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/zizmor.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/zizmor.yml diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000000..ea82567807 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,27 @@ +name: GitHub Actions Security Analysis + +on: + push: + branches: + - master + - '*.x' + pull_request: + branches: + - '**' + +permissions: {} + +jobs: + zizmor: + name: zizmor + runs-on: ubuntu-latest + permissions: + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 From f1655fdf85098272ce5867681b3f441547e45ed8 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 12:25:08 -0400 Subject: [PATCH 07/22] Switch zizmor to annotations and restrict to workflow file changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the SARIF upload approach with inline PR annotations, which surface findings directly in the diff where they're most actionable. The job will now fail on findings, enabling it to be used as a required status check to block merges. Also adds path filtering so the workflow only runs when .github/**.yml files are changed — zizmor only understands Actions syntax so there's no value in running it on unrelated changes. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/zizmor.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index ea82567807..56da3ec974 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -5,9 +5,11 @@ on: branches: - master - '*.x' + paths: + - '.github/**.yml' pull_request: - branches: - - '**' + paths: + - '.github/**.yml' permissions: {} @@ -15,8 +17,7 @@ jobs: zizmor: name: zizmor runs-on: ubuntu-latest - permissions: - security-events: write + permissions: {} steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -25,3 +26,6 @@ jobs: - name: Run zizmor uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 + with: + advanced-security: false + annotations: true From 284a75440b3e8b32164db6db9352eaecc116694e Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 12:43:43 -0400 Subject: [PATCH 08/22] Replace archived Slack action with slackapi/slack-github-action v3.0.3 Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/tests.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 55481dc705..562866371c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -160,12 +160,15 @@ jobs: steps: - uses: technote-space/workflow-conclusion-action@45ce8e0eb155657ab8ccf346ade734257fd196a5 # v3 - name: Send Slack notification - uses: 8398a7/action-slack@77eaa4f1c608a7d68b38af4e3f739dcd8cba273e # v3 + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 if: env.WORKFLOW_CONCLUSION == 'failure' && github.event_name == 'schedule' with: - status: failure - fields: repo,message,commit,author,action,eventName,ref,workflow - author_name: ${{ github.actor }} - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | + text: ":x: *${{ github.repository }}* tests failed" + blocks: + - type: section + text: + type: mrkdwn + text: ":x: *${{ github.repository }}* tests failed\n*Ref:* ${{ github.ref }}\n*Author:* ${{ github.actor }}\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.workflow }}>" From 0cac676de10fd24771eff92a4bef8c64c20d21e0 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 12:44:33 -0400 Subject: [PATCH 09/22] Add temporary Slack notification to verify action on PR Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/tests.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 562866371c..83808accac 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -172,3 +172,16 @@ jobs: text: type: mrkdwn text: ":x: *${{ github.repository }}* tests failed\n*Ref:* ${{ github.ref }}\n*Author:* ${{ github.actor }}\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.workflow }}>" + - name: Send test Slack notification (temporary) + uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 + if: github.event_name == 'pull_request' + with: + webhook: ${{ secrets.SLACK_WEBHOOK }} + webhook-type: incoming-webhook + payload: | + text: ":white_check_mark: *statamic/cms* test notification" + blocks: + - type: section + text: + type: mrkdwn + text: ":white_check_mark: *statamic/cms* test notification\n*Ref:* refs/heads/zizmor\n*Author:* jasonvarga\n*Workflow:* " From 5e50cead1a48bb7c8a0ba06903a9afe17d382528 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 12:52:29 -0400 Subject: [PATCH 10/22] Replace archived release actions with softprops/action-gh-release MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the archived actions/create-release and actions/upload-release-asset (×4) with a single softprops/action-gh-release step that handles both release creation and asset uploads. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/release.yml | 51 +++++------------------------------ 1 file changed, 7 insertions(+), 44 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5c03c0e1ac..2f90dd5d92 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,55 +35,18 @@ jobs: version: ${{ github.ref }} - name: Create release - id: create_release - uses: actions/create-release@0cb9c9b65d5d1901c1f53e5e66eaf4afd303e70e # v1 + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ steps.changelog.outputs.version }} - release_name: ${{ steps.changelog.outputs.version }} + name: ${{ steps.changelog.outputs.version }} body: ${{ steps.changelog.outputs.text }} - prerelease: false - - - name: Upload dist zip to release - uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./resources/dist.tar.gz - asset_name: dist.tar.gz - asset_content_type: application/tar+gz - - - name: Upload dist-dev zip to release - uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./resources/dist-dev.tar.gz - asset_name: dist-dev.tar.gz - asset_content_type: application/tar+gz - - - name: Upload dist-frontend zip to release - uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./resources/dist-frontend.tar.gz - asset_name: dist-frontend.tar.gz - asset_content_type: application/tar+gz - - - name: Upload dist-package zip to release - uses: actions/upload-release-asset@e8f9f06c4b078e705bd2ea027f0926603fc9b4d5 # v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: ./resources/dist-package.tar.gz - asset_name: dist-package.tar.gz - asset_content_type: application/tar+gz + files: | + ./resources/dist.tar.gz + ./resources/dist-dev.tar.gz + ./resources/dist-frontend.tar.gz + ./resources/dist-package.tar.gz - name: Deploy Storybook to Forge continue-on-error: true From cd4e60b2dcbffc1121a6beba981d2943937f9c30 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 12:57:49 -0400 Subject: [PATCH 11/22] Replace softprops/action-gh-release with native gh CLI Uses the pre-installed gh CLI instead of a third-party action, avoiding a supply chain dependency for functionality the runner already provides. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/release.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f90dd5d92..966369da59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,17 +35,17 @@ jobs: version: ${{ github.ref }} - name: Create release - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.changelog.outputs.version }} - name: ${{ steps.changelog.outputs.version }} - body: ${{ steps.changelog.outputs.text }} - files: | - ./resources/dist.tar.gz - ./resources/dist-dev.tar.gz - ./resources/dist-frontend.tar.gz + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + RELEASE_VERSION: ${{ steps.changelog.outputs.version }} + RELEASE_NOTES: ${{ steps.changelog.outputs.text }} + run: | + gh release create "$RELEASE_VERSION" \ + --title "$RELEASE_VERSION" \ + --notes "$RELEASE_NOTES" \ + ./resources/dist.tar.gz \ + ./resources/dist-dev.tar.gz \ + ./resources/dist-frontend.tar.gz \ ./resources/dist-package.tar.gz - name: Deploy Storybook to Forge From bafdac89c65a7856a246c388cd9f1a2795c657c0 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 13:03:50 -0400 Subject: [PATCH 12/22] Fix zizmor ref-confusion: correct version comment for laravel-pint-action Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/code-style-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index 16e1b57362..32c34e5fd7 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -24,7 +24,7 @@ jobs: - name: Check PHP code style issues if: steps.changed-files.outputs.any_modified == 'true' - uses: aglipanci/laravel-pint-action@36de00d5f5a8a4e12d443e01671daa12a18f4c79 # v2.6 + uses: aglipanci/laravel-pint-action@36de00d5f5a8a4e12d443e01671daa12a18f4c79 # 2.6 with: testMode: true verboseMode: true From bd0ddb75c25a45e7fbe5f73b66a5c45f21bd1c2a Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 13:05:14 -0400 Subject: [PATCH 13/22] Remove temporary Slack notification step Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/tests.yml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 83808accac..562866371c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -172,16 +172,3 @@ jobs: text: type: mrkdwn text: ":x: *${{ github.repository }}* tests failed\n*Ref:* ${{ github.ref }}\n*Author:* ${{ github.actor }}\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.workflow }}>" - - name: Send test Slack notification (temporary) - uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3 - if: github.event_name == 'pull_request' - with: - webhook: ${{ secrets.SLACK_WEBHOOK }} - webhook-type: incoming-webhook - payload: | - text: ":white_check_mark: *statamic/cms* test notification" - blocks: - - type: section - text: - type: mrkdwn - text: ":white_check_mark: *statamic/cms* test notification\n*Ref:* refs/heads/zizmor\n*Author:* jasonvarga\n*Workflow:* " From 9c248a8a262c05e00ef95e65e0d2e3fdcda001ed Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 14:51:17 -0400 Subject: [PATCH 14/22] bump From acf822e4a033c12eff21f15821e671fa57c6a1b1 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 15:31:02 -0400 Subject: [PATCH 15/22] Enable pedantic mode in zizmor workflow Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/zizmor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 56da3ec974..16be397398 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -29,3 +29,4 @@ jobs: with: advanced-security: false annotations: true + persona: pedantic From d239c1e25b9754f552fdc66adc52fd82f82ee033 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 15:44:14 -0400 Subject: [PATCH 16/22] Add concurrency limits to workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevents redundant runs queuing up when multiple commits are pushed to the same branch in quick succession. PR-triggered workflows use cancel-in-progress: true so a new push immediately cancels the prior run. The tests workflow uses a conditional so only PR runs are cancelled — scheduled nightly runs are left to complete. Release and stale workflows are suppressed instead since they trigger on tags/schedule where concurrent runs are impossible in practice. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/code-style-lint.yml | 4 ++++ .github/workflows/pr-title.yml | 4 ++++ .github/workflows/pull-requests.yml | 4 ++++ .github/workflows/release.yml | 2 +- .github/workflows/stale.yml | 2 +- .github/workflows/tests.yml | 4 ++++ .github/workflows/zizmor.yml | 4 ++++ 7 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index 32c34e5fd7..d239be2039 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -3,6 +3,10 @@ name: Lint code style issues on: pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: lint-code-styling: runs-on: ubuntu-latest diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 48a9abcb04..917e3947fb 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -4,6 +4,10 @@ on: pull_request: types: [opened, edited, synchronize, reopened] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: pr-title: runs-on: ubuntu-latest diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 040250fb55..6128b1a79a 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -11,6 +11,10 @@ on: permissions: pull-requests: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: uneditable: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 966369da59..686e40c436 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,6 @@ name: Create Release -on: +on: # zizmor: ignore[concurrency-limits] push: tags: - 'v*' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index b251d28e09..3afeda5dea 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,5 +1,5 @@ name: "Close stale issues" -on: +on: # zizmor: ignore[concurrency-limits] workflow_dispatch: schedule: - cron: "30 1 * * *" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 562866371c..ada0d41de5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,6 +9,10 @@ on: schedule: - cron: '0 0 * * *' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: php-tests: runs-on: ${{ matrix.os }} diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 16be397398..7088be7ef9 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -13,6 +13,10 @@ on: permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: zizmor: name: zizmor From 433dd9c7de9bcb04a4679fa57c1a2309f6a5e187 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 15:49:10 -0400 Subject: [PATCH 17/22] Tighten workflow permissions Adds a workflow-level `permissions: {}` block to all workflows that were missing one, ensuring no job can accidentally inherit broad default permissions. Moves the pull-requests.yml permission down to the job level where it belongs. Adds explanatory comments to every non-obvious permission so it's clear why each one is needed. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/code-style-lint.yml | 2 ++ .github/workflows/pr-title.yml | 2 ++ .github/workflows/pull-requests.yml | 5 +++-- .github/workflows/release.yml | 4 +++- .github/workflows/stale.yml | 6 ++++-- .github/workflows/tests.yml | 4 +++- 6 files changed, 17 insertions(+), 6 deletions(-) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index d239be2039..baf64ce242 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -3,6 +3,8 @@ name: Lint code style issues on: pull_request: +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 917e3947fb..5bafd4867a 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -4,6 +4,8 @@ on: pull_request: types: [opened, edited, synchronize, reopened] +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 6128b1a79a..bd41cb0641 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -8,8 +8,7 @@ on: types: - opened -permissions: - pull-requests: write +permissions: {} concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -18,6 +17,8 @@ concurrency: jobs: uneditable: runs-on: ubuntu-latest + permissions: + pull-requests: write # post comment and close PRs that don't allow maintainer edits steps: - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 686e40c436..c83174a4b7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,11 +5,13 @@ on: # zizmor: ignore[concurrency-limits] tags: - 'v*' +permissions: {} + jobs: build: runs-on: ubuntu-latest permissions: - contents: write + contents: write # create GitHub release and upload assets steps: - name: Checkout code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 3afeda5dea..6f3134a1a3 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -4,12 +4,14 @@ on: # zizmor: ignore[concurrency-limits] schedule: - cron: "30 1 * * *" +permissions: {} + jobs: stale: runs-on: ubuntu-latest permissions: - issues: write - pull-requests: write + issues: write # mark issues stale and close them + pull-requests: write # mark pull requests stale and close them steps: - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ada0d41de5..1096a92ab5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,6 +9,8 @@ on: schedule: - cron: '0 0 * * *' +permissions: {} + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} @@ -159,7 +161,7 @@ jobs: runs-on: ubuntu-latest needs: [php-tests, js-tests] permissions: - actions: read + actions: read # required by workflow-conclusion-action to determine overall workflow status if: always() steps: - uses: technote-space/workflow-conclusion-action@45ce8e0eb155657ab8ccf346ade734257fd196a5 # v3 From ea5a9ebc935f5ea0d3588146363a5acb7036d494 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 16:02:35 -0400 Subject: [PATCH 18/22] Pass Forge webhook secret via env variable Avoids direct template expansion of the secret into the shell script, which could allow shell metacharacters in the secret value to be interpreted as commands. Passing it as an env variable means bash treats it as data, not syntax. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c83174a4b7..53c6645001 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,5 +52,7 @@ jobs: - name: Deploy Storybook to Forge continue-on-error: true + env: + FORGE_STORYBOOK_WEBHOOK: ${{ secrets.FORGE_STORYBOOK_WEBHOOK }} run: | - curl -X POST "${{ secrets.FORGE_STORYBOOK_WEBHOOK }}" + curl -X POST "$FORGE_STORYBOOK_WEBHOOK" From d18740578e1fa1e2f74eca69d8b03a8ca2787890 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 16:06:02 -0400 Subject: [PATCH 19/22] Add name fields to anonymous job definitions Gives each job an explicit human-readable display name in the GitHub Actions UI rather than falling back to the job key. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/code-style-lint.yml | 1 + .github/workflows/pr-title.yml | 1 + .github/workflows/pull-requests.yml | 1 + .github/workflows/release.yml | 1 + .github/workflows/stale.yml | 1 + 5 files changed, 5 insertions(+) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index baf64ce242..614b2c6fae 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -11,6 +11,7 @@ concurrency: jobs: lint-code-styling: + name: Lint code style runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 5bafd4867a..25a4ec064f 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -12,6 +12,7 @@ concurrency: jobs: pr-title: + name: Validate PR title runs-on: ubuntu-latest permissions: {} steps: diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index bd41cb0641..8e08f498d3 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -16,6 +16,7 @@ concurrency: jobs: uneditable: + name: Check maintainer edit access runs-on: ubuntu-latest permissions: pull-requests: write # post comment and close PRs that don't allow maintainer edits diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53c6645001..e44553372e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,6 +9,7 @@ permissions: {} jobs: build: + name: Build and release runs-on: ubuntu-latest permissions: contents: write # create GitHub release and upload assets diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6f3134a1a3..c7f012d54b 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,6 +8,7 @@ permissions: {} jobs: stale: + name: Close stale issues runs-on: ubuntu-latest permissions: issues: write # mark issues stale and close them From e20cdde69abfa3b705976a94c82f38bc086798a6 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 16:07:42 -0400 Subject: [PATCH 20/22] Revert "Add name fields to anonymous job definitions" This reverts commit d18740578e1fa1e2f74eca69d8b03a8ca2787890. --- .github/workflows/code-style-lint.yml | 1 - .github/workflows/pr-title.yml | 1 - .github/workflows/pull-requests.yml | 1 - .github/workflows/release.yml | 1 - .github/workflows/stale.yml | 1 - 5 files changed, 5 deletions(-) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index 614b2c6fae..baf64ce242 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -11,7 +11,6 @@ concurrency: jobs: lint-code-styling: - name: Lint code style runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 25a4ec064f..5bafd4867a 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -12,7 +12,6 @@ concurrency: jobs: pr-title: - name: Validate PR title runs-on: ubuntu-latest permissions: {} steps: diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 8e08f498d3..bd41cb0641 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -16,7 +16,6 @@ concurrency: jobs: uneditable: - name: Check maintainer edit access runs-on: ubuntu-latest permissions: pull-requests: write # post comment and close PRs that don't allow maintainer edits diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e44553372e..53c6645001 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,7 +9,6 @@ permissions: {} jobs: build: - name: Build and release runs-on: ubuntu-latest permissions: contents: write # create GitHub release and upload assets diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index c7f012d54b..6f3134a1a3 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -8,7 +8,6 @@ permissions: {} jobs: stale: - name: Close stale issues runs-on: ubuntu-latest permissions: issues: write # mark issues stale and close them From 7f278de3ea884770fd2aae0088e5afdb883ef72a Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 16:08:02 -0400 Subject: [PATCH 21/22] Suppress anonymous-definition findings on job keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Job keys are already human-readable and match the required status check names in branch protection rules — adding name: fields would break those rules without adding value. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/code-style-lint.yml | 2 +- .github/workflows/pr-title.yml | 2 +- .github/workflows/pull-requests.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/stale.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/code-style-lint.yml b/.github/workflows/code-style-lint.yml index baf64ce242..14cdec97eb 100644 --- a/.github/workflows/code-style-lint.yml +++ b/.github/workflows/code-style-lint.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true jobs: - lint-code-styling: + lint-code-styling: # zizmor: ignore[anonymous-definition] runs-on: ubuntu-latest permissions: contents: read diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 5bafd4867a..371f8a26b1 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -11,7 +11,7 @@ concurrency: cancel-in-progress: true jobs: - pr-title: + pr-title: # zizmor: ignore[anonymous-definition] runs-on: ubuntu-latest permissions: {} steps: diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index bd41cb0641..bbec5a4d86 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -15,7 +15,7 @@ concurrency: cancel-in-progress: true jobs: - uneditable: + uneditable: # zizmor: ignore[anonymous-definition] runs-on: ubuntu-latest permissions: pull-requests: write # post comment and close PRs that don't allow maintainer edits diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 53c6645001..85847f9b71 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: # zizmor: ignore[concurrency-limits] permissions: {} jobs: - build: + build: # zizmor: ignore[anonymous-definition] runs-on: ubuntu-latest permissions: contents: write # create GitHub release and upload assets diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6f3134a1a3..ae6fcda0c0 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -7,7 +7,7 @@ on: # zizmor: ignore[concurrency-limits] permissions: {} jobs: - stale: + stale: # zizmor: ignore[anonymous-definition] runs-on: ubuntu-latest permissions: issues: write # mark issues stale and close them From 56042a095d4a12c576520a86792f62170a38f6d1 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 18 May 2026 16:39:24 -0400 Subject: [PATCH 22/22] Scope concurrency group to PR number in pull-requests workflow The other workflows that added a concurrency block use the pull_request trigger, where github.ref is the synthetic refs/pull//merge ref -- already unique per PR, so each PR lands in its own group. pull-requests.yml uses pull_request_target, which runs in the context of the base branch, so github.ref resolves to refs/heads/ instead. Every PR opened against the same base shares one group, and cancel-in-progress would cancel earlier PRs' runs. Keying the group on github.event.pull_request.number restores per-PR isolation for this workflow. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/pull-requests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index bbec5a4d86..b1919640cb 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -11,7 +11,7 @@ on: permissions: {} concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} cancel-in-progress: true jobs: