spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic ยท github.com/anthropics

fix: prevent API key exfiltration in dedupe workflowBrowse files
Security fix to address potential prompt injection attack vector where
malicious issue content could exploit gh api/comment permissions to
exfiltrate the ANTHROPIC_API_KEY.

Changes:
- Remove gh api:* and gh issue comment:* from dedupe command allowed-tools
- Command now outputs structured JSON to /tmp/dedupe-result.json
- Comment posting moved to isolated workflow step without API key access
- Added URL validation to prevent injection in comment content

The Claude Code step can now only read issues (gh issue view/search/list),
while comment posting happens in a separate step that only has GITHUB_TOKEN.
Claude authored on Nov 19, 2025 parent 68ba478 ยท commit e1c91d294d4f
2 changed files+91 -23compared with 68ba478
M.claude/commands/dedupe.md
M.github/workflows/claude-dedupe-issues.yml
@@ -1,5 +1,5 @@
11 ---
2-allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh api:*), Bash(gh issue comment:*)
2+allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*)
33 description: Find duplicate GitHub issues
44 ---
55
@@ -7,32 +7,33 @@
77
88 To do this, follow these steps precisely:
99
10-1. Use an agent to check if the Github issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicates comment that you made earlier. If so, do not proceed.
10+1. Use an agent to check if the Github issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicates comment that you made earlier. If so, do not proceed and write `{"skip": true, "reason": "..."}` to `/tmp/dedupe-result.json`.
1111 2. Use an agent to view a Github issue, and ask the agent to return a summary of the issue
1212 3. Then, launch 5 parallel agents to search Github for duplicates of this issue, using diverse keywords and search approaches, using the summary from #1
13-4. Next, feed the results from #1 and #2 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, do not proceed.
14-5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates)
13+4. Next, feed the results from #1 and #2 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, write `{"skip": true, "reason": "no duplicates found"}` to `/tmp/dedupe-result.json` and do not proceed.
14+5. Finally, write a JSON file to `/tmp/dedupe-result.json` with the list of duplicate issue URLs. Do NOT post comments directly.
1515
16-Notes (be sure to tell this to your agents, too):
16+IMPORTANT: You must write output to `/tmp/dedupe-result.json` - never post comments directly. The workflow will handle posting.
1717
18-- Use `gh` to interact with Github, rather than web fetch
19-- Do not use other tools, beyond `gh` (eg. don't use other MCP servers, file edit, etc.)
20-- Make a todo list first
21-- For your comment, follow the following format precisely (assuming for this example that you found 3 suspected duplicates):
18+The JSON output format must be exactly:
19+```json
20+{
21+ "duplicates": ["https://github.com/owner/repo/issues/123", "https://github.com/owner/repo/issues/456"]
22+}
23+```
2224
23----
25+Or if skipping:
26+```json
27+{
28+ "skip": true,
29+ "reason": "reason for skipping"
30+}
31+```
2432
25-Found 3 possible duplicate issues:
33+Notes (be sure to tell this to your agents, too):
2634
27-1. <link to issue>
28-2. <link to issue>
29-3. <link to issue>
30-
31-This issue will be automatically closed as a duplicate in 3 days.
32-
33-- If your issue is a duplicate, please close it and ๐Ÿ‘ the existing issue instead
34-- To prevent auto-closure, add a comment or ๐Ÿ‘Ž this comment
35-
36-๐Ÿค– Generated with [Claude Code](https://claude.ai/code)
37-
38----
35+- Use `gh` to interact with Github for reading issues only, rather than web fetch
36+- Do not use other tools beyond `gh` for reading (eg. don't use other MCP servers, file edit beyond the output JSON, etc.)
37+- Do NOT use `gh issue comment` or `gh api` - these are not permitted for security reasons
38+- Make a todo list first
39+- Output must be written to `/tmp/dedupe-result.json` - the workflow handles comment posting
@@ -23,14 +23,81 @@
2323 uses: actions/checkout@v4
2424
2525 - name: Run Claude Code slash command
26+ id: claude
2627 uses: anthropics/claude-code-base-action@beta
2728 with:
2829 prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
2930 anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
3031 claude_args: "--model claude-sonnet-4-5-20250929"
32+ # Note: GH_TOKEN only provides read access for issue viewing/searching
33+ # Comment posting is handled in a separate isolated step below
3134 claude_env: |
3235 GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
3336
37+ # SECURITY: This step runs in isolation without access to ANTHROPIC_API_KEY
38+ # It only has GITHUB_TOKEN for posting comments, preventing secret exfiltration
39+ - name: Post duplicate comment (isolated from API key)
40+ if: success()
41+ env:
42+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43+ run: |
44+ ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
45+ RESULT_FILE="/tmp/dedupe-result.json"
46+
47+ if [ ! -f "$RESULT_FILE" ]; then
48+ echo "No dedupe result file found, skipping comment"
49+ exit 0
50+ fi
51+
52+ # Check if we should skip
53+ if jq -e '.skip' "$RESULT_FILE" > /dev/null 2>&1; then
54+ REASON=$(jq -r '.reason // "unknown"' "$RESULT_FILE")
55+ echo "Skipping comment: $REASON"
56+ exit 0
57+ fi
58+
59+ # Get duplicates array
60+ DUPLICATES=$(jq -r '.duplicates // []' "$RESULT_FILE")
61+ COUNT=$(echo "$DUPLICATES" | jq 'length')
62+
63+ if [ "$COUNT" -eq 0 ]; then
64+ echo "No duplicates found, skipping comment"
65+ exit 0
66+ fi
67+
68+ # Build comment body (limit to 3 duplicates for safety)
69+ SAFE_COUNT=$((COUNT > 3 ? 3 : COUNT))
70+
71+ COMMENT="Found $SAFE_COUNT possible duplicate issue"
72+ if [ "$SAFE_COUNT" -ne 1 ]; then
73+ COMMENT="${COMMENT}s"
74+ fi
75+ COMMENT="${COMMENT}:"
76+ COMMENT="${COMMENT}
77+ "
78+
79+ for i in $(seq 0 $((SAFE_COUNT - 1))); do
80+ URL=$(echo "$DUPLICATES" | jq -r ".[$i]")
81+ # Validate URL format to prevent injection
82+ if [[ "$URL" =~ ^https://github\.com/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/issues/[0-9]+$ ]]; then
83+ COMMENT="${COMMENT}
84+ $((i + 1)). $URL"
85+ fi
86+ done
87+
88+ COMMENT="${COMMENT}
89+
90+ This issue will be automatically closed as a duplicate in 3 days.
91+
92+ - If your issue is a duplicate, please close it and ๐Ÿ‘ the existing issue instead
93+ - To prevent auto-closure, add a comment or ๐Ÿ‘Ž this comment
94+
95+ ๐Ÿค– Generated with [Claude Code](https://claude.ai/code)"
96+
97+ # Post the comment
98+ gh issue comment "$ISSUE_NUMBER" --repo "${{ github.repository }}" --body "$COMMENT"
99+ echo "Posted duplicate comment on issue #$ISSUE_NUMBER"
100+
34101 - name: Log duplicate comment event to Statsig
35102 if: always()
36103 env: