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
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.
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | --- |
| 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:*) | |
| 3 | 3 | description: Find duplicate GitHub issues |
| 4 | 4 | --- |
| 5 | 5 | |
| @@ -7,32 +7,33 @@ | ||
| 7 | 7 | |
| 8 | 8 | To do this, follow these steps precisely: |
| 9 | 9 | |
| 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`. | |
| 11 | 11 | 2. Use an agent to view a Github issue, and ask the agent to return a summary of the issue |
| 12 | 12 | 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. | |
| 15 | 15 | |
| 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. | |
| 17 | 17 | |
| 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 | +``` | |
| 22 | 24 | |
| 23 | ---- | |
| 25 | +Or if skipping: | |
| 26 | +```json | |
| 27 | +{ | |
| 28 | + "skip": true, | |
| 29 | + "reason": "reason for skipping" | |
| 30 | +} | |
| 31 | +``` | |
| 24 | 32 | |
| 25 | -Found 3 possible duplicate issues: | |
| 33 | +Notes (be sure to tell this to your agents, too): | |
| 26 | 34 | |
| 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 @@ | ||
| 23 | 23 | uses: actions/checkout@v4 |
| 24 | 24 | |
| 25 | 25 | - name: Run Claude Code slash command |
| 26 | + id: claude | |
| 26 | 27 | uses: anthropics/claude-code-base-action@beta |
| 27 | 28 | with: |
| 28 | 29 | prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}" |
| 29 | 30 | anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} |
| 30 | 31 | 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 | |
| 31 | 34 | claude_env: | |
| 32 | 35 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |
| 33 | 36 | |
| 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 | + | |
| 34 | 101 | - name: Log duplicate comment event to Statsig |
| 35 | 102 | if: always() |
| 36 | 103 | env: |