fix: prevent API key exfiltration in dedupe workflow

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.
This commit is contained in:
Claude
2025-11-19 02:33:20 +00:00
parent 68ba47859a
commit e1c91d294d
2 changed files with 92 additions and 24 deletions

View File

@@ -1,5 +1,5 @@
---
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh api:*), Bash(gh issue comment:*)
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*)
description: Find duplicate GitHub issues
---
@@ -7,32 +7,33 @@ Find up to 3 likely duplicate issues for a given GitHub issue.
To do this, follow these steps precisely:
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.
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`.
2. Use an agent to view a Github issue, and ask the agent to return a summary of the issue
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
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.
5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates)
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.
5. Finally, write a JSON file to `/tmp/dedupe-result.json` with the list of duplicate issue URLs. Do NOT post comments directly.
IMPORTANT: You must write output to `/tmp/dedupe-result.json` - never post comments directly. The workflow will handle posting.
The JSON output format must be exactly:
```json
{
"duplicates": ["https://github.com/owner/repo/issues/123", "https://github.com/owner/repo/issues/456"]
}
```
Or if skipping:
```json
{
"skip": true,
"reason": "reason for skipping"
}
```
Notes (be sure to tell this to your agents, too):
- Use `gh` to interact with Github, rather than web fetch
- Do not use other tools, beyond `gh` (eg. don't use other MCP servers, file edit, etc.)
- Use `gh` to interact with Github for reading issues only, rather than web fetch
- Do not use other tools beyond `gh` for reading (eg. don't use other MCP servers, file edit beyond the output JSON, etc.)
- Do NOT use `gh issue comment` or `gh api` - these are not permitted for security reasons
- Make a todo list first
- For your comment, follow the following format precisely (assuming for this example that you found 3 suspected duplicates):
---
Found 3 possible duplicate issues:
1. <link to issue>
2. <link to issue>
3. <link to issue>
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
🤖 Generated with [Claude Code](https://claude.ai/code)
---
- Output must be written to `/tmp/dedupe-result.json` - the workflow handles comment posting

View File

@@ -23,14 +23,81 @@ jobs:
uses: actions/checkout@v4
- name: Run Claude Code slash command
id: claude
uses: anthropics/claude-code-base-action@beta
with:
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--model claude-sonnet-4-5-20250929"
# Note: GH_TOKEN only provides read access for issue viewing/searching
# Comment posting is handled in a separate isolated step below
claude_env: |
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# SECURITY: This step runs in isolation without access to ANTHROPIC_API_KEY
# It only has GITHUB_TOKEN for posting comments, preventing secret exfiltration
- name: Post duplicate comment (isolated from API key)
if: success()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
RESULT_FILE="/tmp/dedupe-result.json"
if [ ! -f "$RESULT_FILE" ]; then
echo "No dedupe result file found, skipping comment"
exit 0
fi
# Check if we should skip
if jq -e '.skip' "$RESULT_FILE" > /dev/null 2>&1; then
REASON=$(jq -r '.reason // "unknown"' "$RESULT_FILE")
echo "Skipping comment: $REASON"
exit 0
fi
# Get duplicates array
DUPLICATES=$(jq -r '.duplicates // []' "$RESULT_FILE")
COUNT=$(echo "$DUPLICATES" | jq 'length')
if [ "$COUNT" -eq 0 ]; then
echo "No duplicates found, skipping comment"
exit 0
fi
# Build comment body (limit to 3 duplicates for safety)
SAFE_COUNT=$((COUNT > 3 ? 3 : COUNT))
COMMENT="Found $SAFE_COUNT possible duplicate issue"
if [ "$SAFE_COUNT" -ne 1 ]; then
COMMENT="${COMMENT}s"
fi
COMMENT="${COMMENT}:"
COMMENT="${COMMENT}
"
for i in $(seq 0 $((SAFE_COUNT - 1))); do
URL=$(echo "$DUPLICATES" | jq -r ".[$i]")
# Validate URL format to prevent injection
if [[ "$URL" =~ ^https://github\.com/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/issues/[0-9]+$ ]]; then
COMMENT="${COMMENT}
$((i + 1)). $URL"
fi
done
COMMENT="${COMMENT}
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
🤖 Generated with [Claude Code](https://claude.ai/code)"
# Post the comment
gh issue comment "$ISSUE_NUMBER" --repo "${{ github.repository }}" --body "$COMMENT"
echo "Posted duplicate comment on issue #$ISSUE_NUMBER"
- name: Log duplicate comment event to Statsig
if: always()
env: