From 8a9a0a4a186a13a274110ce4c1e6b6280c7ea167 Mon Sep 17 00:00:00 2001 From: Brandon Werner Date: Thu, 25 Jun 2026 11:43:51 -0700 Subject: [PATCH] fix: pin Claude PR review action to immutable SHA Pin the third-party Claude action to the current v1 commit (428971d, the commit v1 currently resolves to) instead of the floating @v1 tag, and remove the unused id-token: write permission from the workflow. Add regression tests so the workflow cannot drift back to a floating action tag or id-token write access. Ports a fix originally written on the pre-rename entraclaw fork (never upstreamed) and refreshes the pinned SHA to current v1. --- .github/workflows/claude-pr-review.yml | 3 +-- tests/test_workflows_security.py | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/test_workflows_security.py diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml index 28590db..08c6c79 100644 --- a/.github/workflows/claude-pr-review.yml +++ b/.github/workflows/claude-pr-review.yml @@ -13,7 +13,6 @@ jobs: contents: write pull-requests: write issues: write - id-token: write steps: - name: Check out PR head commit uses: actions/checkout@v4 @@ -22,7 +21,7 @@ jobs: fetch-depth: 0 - name: Run Claude on the review feedback - uses: anthropics/claude-code-action@v1 + uses: anthropics/claude-code-action@428971d2ecd6e3a7cb0ee0da2a3a8b33fdb3678d # v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} prompt: | diff --git a/tests/test_workflows_security.py b/tests/test_workflows_security.py new file mode 100644 index 0000000..3bec515 --- /dev/null +++ b/tests/test_workflows_security.py @@ -0,0 +1,22 @@ +"""Security regression tests for GitHub Actions workflows.""" + +from __future__ import annotations + +import re +from pathlib import Path + +WORKFLOW = Path(".github/workflows/claude-pr-review.yml") + + +def test_claude_pr_review_uses_immutable_action_pin() -> None: + workflow = WORKFLOW.read_text() + + match = re.search(r"uses:\s*anthropics/claude-code-action@([0-9a-f]{40})\b", workflow) + + assert match is not None + + +def test_claude_pr_review_does_not_request_oidc_token() -> None: + workflow = WORKFLOW.read_text() + + assert not re.search(r"^\s*id-token:\s*write\s*$", workflow, re.MULTILINE)