From dbd15630f1c6fcfb758cee93a553cc5cb3707fa0 Mon Sep 17 00:00:00 2001 From: Ian Maia Date: Wed, 1 Jul 2026 18:37:06 +0200 Subject: [PATCH 1/6] Secure Claude workflows --- .github/workflows/claude-review.yml | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index 9d54ff323..cc49d664d 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -12,15 +12,35 @@ jobs: permissions: contents: read pull-requests: write - id-token: write steps: + # issue_comment payloads identify PR comments, but do not include the PR + # head repo. Query the PR before allowing Claude to run on fork PRs. + - name: Check PR origin + id: pr-origin + env: + GH_TOKEN: ${{ github.token }} + ISSUE_NUMBER: ${{ github.event.issue.number }} + REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + head_repo="$(gh api "repos/${REPOSITORY}/pulls/${ISSUE_NUMBER}" --jq '.head.repo.full_name' 2>/dev/null || true)" + + if [ "$head_repo" = "$REPOSITORY" ]; then + echo "is_internal=true" >> "$GITHUB_OUTPUT" + else + echo "is_internal=false" >> "$GITHUB_OUTPUT" + echo "Skipping Claude for external PR from ${head_repo:-unknown}." + fi + - name: Checkout repository - uses: actions/checkout@v5 + if: steps.pr-origin.outputs.is_internal == 'true' + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 1 - name: PR Review with Progress Tracking - uses: anthropics/claude-code-action@v1 + if: steps.pr-origin.outputs.is_internal == 'true' + uses: anthropics/claude-code-action@6a838f847a10bc4b88da6ae796ff68e74cf9f4cc # v1.0.158 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} track_progress: true @@ -56,7 +76,6 @@ jobs: - Check API documentation accuracy Provide detailed feedback using inline comments for specific issues. - Use top-level comments for general observations or praise. claude_args: | - --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)" + --allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr diff:*),Bash(gh pr view:*)" From e63a52929b5ae0be2f9e8d95e326b71a6db79fdb Mon Sep 17 00:00:00 2001 From: Ian Maia Date: Wed, 1 Jul 2026 21:14:06 +0200 Subject: [PATCH 2/6] Address Claude workflow review feedback --- .github/workflows/claude-review.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index cc49d664d..bd3dda542 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -23,7 +23,11 @@ jobs: REPOSITORY: ${{ github.repository }} run: | set -euo pipefail - head_repo="$(gh api "repos/${REPOSITORY}/pulls/${ISSUE_NUMBER}" --jq '.head.repo.full_name' 2>/dev/null || true)" + if ! head_repo="$(gh api "repos/${REPOSITORY}/pulls/${ISSUE_NUMBER}" --jq '.head.repo.full_name')"; then + echo "Unable to determine PR head repository for #${ISSUE_NUMBER}; skipping Claude." + echo "is_internal=false" >> "$GITHUB_OUTPUT" + exit 0 + fi if [ "$head_repo" = "$REPOSITORY" ]; then echo "is_internal=true" >> "$GITHUB_OUTPUT" @@ -37,6 +41,7 @@ jobs: uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 1 + persist-credentials: false - name: PR Review with Progress Tracking if: steps.pr-origin.outputs.is_internal == 'true' From cd3c6c3666c26c6bbd5160609338d4c0f2cb9e6c Mon Sep 17 00:00:00 2001 From: Ian Maia Date: Thu, 2 Jul 2026 11:26:52 +0200 Subject: [PATCH 3/6] Restore Claude OIDC permission --- .github/workflows/claude-review.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index bd3dda542..c71ebea78 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -12,6 +12,7 @@ jobs: permissions: contents: read pull-requests: write + id-token: write steps: # issue_comment payloads identify PR comments, but do not include the PR # head repo. Query the PR before allowing Claude to run on fork PRs. From 0994a53e923190420a7e87c30dd6661cc703937e Mon Sep 17 00:00:00 2001 From: Ian Maia Date: Fri, 3 Jul 2026 13:54:06 +0200 Subject: [PATCH 4/6] Clarify Claude OIDC permission --- .github/workflows/claude-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index c71ebea78..a71b0faf6 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -12,7 +12,7 @@ jobs: permissions: contents: read pull-requests: write - id-token: write + id-token: write # Required by claude-code-action for GitHub App token exchange. steps: # issue_comment payloads identify PR comments, but do not include the PR # head repo. Query the PR before allowing Claude to run on fork PRs. From f76db70adf2e7fcad68f5f375970763a911bdeff Mon Sep 17 00:00:00 2001 From: "Ian G. Maia" Date: Fri, 3 Jul 2026 14:00:49 +0200 Subject: [PATCH 5/6] Update comment Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/claude-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index a71b0faf6..c963f1cab 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -15,7 +15,7 @@ jobs: id-token: write # Required by claude-code-action for GitHub App token exchange. steps: # issue_comment payloads identify PR comments, but do not include the PR - # head repo. Query the PR before allowing Claude to run on fork PRs. + # head repo. Query the PR and only run Claude when the head repo matches this repository (skip forks/external PRs). - name: Check PR origin id: pr-origin env: From efe603008ab6e997df065f4bdb4b71759845bf5b Mon Sep 17 00:00:00 2001 From: Ian Maia Date: Fri, 3 Jul 2026 14:03:25 +0200 Subject: [PATCH 6/6] Gate Claude review trigger permissions --- .github/workflows/claude-review.yml | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml index c963f1cab..1bf7d9213 100644 --- a/.github/workflows/claude-review.yml +++ b/.github/workflows/claude-review.yml @@ -5,14 +5,20 @@ on: types: [created] jobs: - review-with-tracking: + check-pr-origin: if: | - github.event.issue.pull_request && contains(github.event.comment.body, '@claude') + github.event.issue.pull_request && + contains(github.event.comment.body, '@claude') && + ( + github.event.comment.author_association == 'OWNER' || + github.event.comment.author_association == 'MEMBER' || + github.event.comment.author_association == 'COLLABORATOR' + ) runs-on: ubuntu-latest permissions: - contents: read - pull-requests: write - id-token: write # Required by claude-code-action for GitHub App token exchange. + pull-requests: read + outputs: + is_internal: ${{ steps.pr-origin.outputs.is_internal }} steps: # issue_comment payloads identify PR comments, but do not include the PR # head repo. Query the PR and only run Claude when the head repo matches this repository (skip forks/external PRs). @@ -37,15 +43,23 @@ jobs: echo "Skipping Claude for external PR from ${head_repo:-unknown}." fi + + review-with-tracking: + needs: check-pr-origin + if: needs.check-pr-origin.outputs.is_internal == 'true' + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + id-token: write # Required by claude-code-action for GitHub App token exchange. + steps: - name: Checkout repository - if: steps.pr-origin.outputs.is_internal == 'true' uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 with: fetch-depth: 1 persist-credentials: false - name: PR Review with Progress Tracking - if: steps.pr-origin.outputs.is_internal == 'true' uses: anthropics/claude-code-action@6a838f847a10bc4b88da6ae796ff68e74cf9f4cc # v1.0.158 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}