Skip to content

Commit 734b5e3

Browse files
committed
fix: harden claude-agent workflow against script injection and untrusted triggers
Issue title/body were interpolated directly into a double-quoted bash string via ${{ }}, letting a crafted title/body (e.g. containing a double quote) break out and run arbitrary shell in a job with contents/pull-requests/issues write access and both ANTHROPIC_API_KEY and GITHUB_TOKEN available. Pass them through env: and reference as shell variables instead. Also add an author_association check (OWNER/MEMBER/COLLABORATOR) so the workflow, which triggers on any issue/comment containing "@claude", can no longer be invoked by arbitrary outside users. Reported by an external security researcher via email. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WTkGkngvts5uHP9CjedJTg
1 parent aaa8dae commit 734b5e3

1 file changed

Lines changed: 21 additions & 4 deletions

File tree

‎.github/workflows/claude-agent.yml‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,12 @@ permissions:
1313

1414
jobs:
1515
claude:
16-
# Only run if the issue/comment contains "@claude" and it's NOT a PR
17-
if: (contains(github.event.issue.body, '@claude') || contains(github.event.comment.body, '@claude')) && github.event.issue.pull_request == null
16+
# Only run if the issue/comment contains "@claude", it's NOT a PR, and the
17+
# author is trusted (prevents arbitrary outside users from triggering the agent)
18+
if: >-
19+
(contains(github.event.issue.body, '@claude') || contains(github.event.comment.body, '@claude')) &&
20+
github.event.issue.pull_request == null &&
21+
contains(fromJSON('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association || github.event.issue.author_association)
1822
runs-on: ubuntu-latest
1923
steps:
2024
- name: Checkout code
@@ -32,6 +36,9 @@ jobs:
3236
env:
3337
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
3438
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39+
ISSUE_NUMBER: ${{ github.event.issue.number }}
40+
ISSUE_TITLE: ${{ github.event.issue.title }}
41+
ISSUE_BODY: ${{ github.event.issue.body }}
3542
run: |
3643
# 1. Prepare the Rules Variable
3744
RULES=""
@@ -43,8 +50,18 @@ jobs:
4350
fi
4451
4552
# 2. Construct the Prompt
46-
# We place RULES at the very top so they act as system instructions
47-
PROMPT="PROJECT RULES (STRICTLY FOLLOW):\n$RULES\n\n----------------\n\nTASK:\nAct as a senior developer. Fix the issue described in GitHub Issue #${{ github.event.issue.number }}: ${{ github.event.issue.title }}.\n\nCONTEXT:\n${{ github.event.issue.body }}"
53+
# Issue title/body come in via env vars (not interpolated into the script
54+
# text) so untrusted content can't break out of the quoted string here.
55+
PROMPT="PROJECT RULES (STRICTLY FOLLOW):
56+
$RULES
57+
58+
----------------
59+
60+
TASK:
61+
Act as a senior developer. Fix the issue described in GitHub Issue #$ISSUE_NUMBER: $ISSUE_TITLE.
62+
63+
CONTEXT:
64+
$ISSUE_BODY"
4865
4966
# 3. Run Agent
5067
# We use the constructed $PROMPT variable inside the -p flag

0 commit comments

Comments
 (0)