Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Claude Code Review

on:
pull_request:
pull_request_target:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n .github/workflows/claude-code-review.yml

Repository: zenstackhq/zenstack

Length of output: 3984


🌐 Web query:

GitHub Actions pull_request_target security risks forked pull requests secrets write permissions

💡 Result:

Why pull_request_target is risky with forked PRs

  • It runs with the base repository trust/permissions, not the fork’s. That means the workflow can get repository secrets (and can also access “trusted” secret stores in cases where pull_request would be blocked). (docs.github.com)
  • If your workflow checks out and executes PR code (e.g., actions/checkout at github.event.pull_request.head.sha or otherwise runs code from the fork), you can end up running attacker-controlled code with secrets available, enabling secret exfiltration (“pwn request”) and follow-on compromise. (securitylab.github.com)
  • The built-in GITHUB_TOKEN may have write capabilities unless you explicitly restrict it, so a compromised run can potentially push commits/tags, open/modify PRs, create releases, edit issues, etc. (depending on granted permissions). (synacktiv.com)

The common “fork PR + secrets + write” failure mode

  1. Attacker opens a PR from a fork.
  2. A pull_request_target workflow triggers (base repo context).
  3. The workflow fetches/runs the PR’s code (tests/build scripts, custom actions from the PR, etc.).
  4. The PR code prints/exfiltrates secrets or uses a write-capable GITHUB_TOKEN to persist changes (e.g., push to the repo, poison artifacts). (securitylab.github.com)

Key mitigations (safe patterns)

  • Prefer pull_request for CI on code from forks (GitHub’s default safety model: no secrets + read-only token). (github.blog)
  • If you must use pull_request_target (e.g., to comment/label):
    • Do not check out / run PR code in that workflow. (docs.github.com)
    • Set explicit minimal permissions: for GITHUB_TOKEN (avoid broad write). (github.blog)
    • Gate any privileged behavior behind maintainer approval/labels and keep the privileged job operating only on trusted refs. (securitylab.github.com)

Recent platform change to be aware of

  • GitHub announced changes (Nov 7, 2025) to reduce edge cases by ensuring the default branch is the workflow source for pull_request_target-related evaluation, but GitHub still emphasizes that using pull_request_target with fork PRs remains inherently higher risk because it executes based on user-supplied PRs and can access secrets. (github.blog)

Citations:


pull_request_target with untrusted PR checkout creates secret exfiltration risk

Line 4 enables pull_request_target, which runs this workflow with base-repository secrets and permissions. However, the actions/checkout@v4 call (lines 30–32) does not specify a ref, causing it to check out the PR's head code (attacker-controlled). Combined with the available CLAUDE_CODE_OAUTH_TOKEN secret and pull-requests: write permission, this creates a direct path for secret exfiltration: a malicious PR could extract the token or abuse the write scope during the action's execution.

Either switch to pull_request (revokes secrets, read-only token) or apply all three mitigations if pull_request_target is mandatory:

  1. Checkout base SHA only with persist-credentials: false to prevent untrusted code execution with secrets in scope.
  2. Pin the third-party action by commit SHA instead of the @beta tag.
  3. Restrict GITHUB_TOKEN permissions further if possible.
Hardening diff
             - name: Checkout repository
               uses: actions/checkout@v4
               with:
+                  ref: ${{ github.event.pull_request.base.sha }}
                   fetch-depth: 1
+                  persist-credentials: false

             - name: Run Claude Code Review
               id: claude-review
-              uses: anthropics/claude-code-action@beta
+              uses: anthropics/claude-code-action@<commit-sha>
               with:
                   claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/claude-code-review.yml at line 4, The workflow uses
pull_request_target which, combined with actions/checkout without a ref and the
CLAUDE_CODE_OAUTH_TOKEN plus pull-requests: write permission, allows untrusted
PR code to run with repo secrets; either change the event from
pull_request_target to pull_request, or if pull_request_target is required:
modify the checkout step in the workflow to check out the base SHA only (set the
ref to the base commit and add persist-credentials: false), pin any third-party
action usages (replace mutable tags like `@beta` with commit SHAs), and tighten
GITHUB_TOKEN permissions (remove or reduce pull-requests: write) to eliminate
the secret exfiltration path.

types: [opened, synchronize]
# Optional: Only run on specific file changes
# paths:
Expand Down
Loading