diff --git a/.github/prompts/security-review.md b/.github/prompts/security-review.md index 397e15dd8..ca0718328 100644 --- a/.github/prompts/security-review.md +++ b/.github/prompts/security-review.md @@ -210,11 +210,12 @@ WORKFLOW (executed in 3 steps): POSTING RESULTS — read this section CAREFULLY before posting anything: -You have access to two MCP tools provided by the `anthropics/claude-code-action` GitHub Action. These are the ONLY -correct way to publish findings — do NOT call the GitHub REST API (`gh`, `octokit`, `curl`) and do NOT attempt to use -`POST /pulls/{n}/reviews` directly. Direct REST calls are not available as tools in this environment. +You have access to one MCP tool provided by the `anthropics/claude-code-action` GitHub Action. This is the ONLY correct +way to publish findings — do NOT call the GitHub REST API (`gh`, `octokit`, `curl`) and do NOT attempt to use +`POST /pulls/{n}/reviews` directly. Direct REST calls are not available as tools in this environment. Do NOT attempt to +post a top-level summary comment yourself; the workflow posts the summary after you exit. -**Tool A — `mcp__github_inline_comment__create_inline_comment`.** Posts an inline review comment on a specific file and +**Tool — `mcp__github_inline_comment__create_inline_comment`.** Posts an inline review comment on a specific file and line. Each call buffers one comment; the action's post-step posts them all to the PR after this session ends. Call it once per finding. @@ -228,9 +229,6 @@ Parameters (all you need for almost every case): - `side` (`"LEFT"` | `"RIGHT"`, default `"RIGHT"`): leave at default unless commenting on a deleted line. - Do NOT pass `confirmed`. Letting it default lets the action's classifier filter test/probe-style comments. -**Tool B — `mcp__github_comment__update_claude_comment`.** Updates a single sticky top-level comment on the PR. Only -parameter is `body` (string). Use this for the no-findings summary or the re-review status summary (see D below). - A. **Re-review awareness.** Before posting, READ the existing PR comments included in the diff/git context above. If your own bot identity has already commented on this PR (look for review comments authored by the bot whose token is running this workflow), treat this run as a re-review: @@ -282,17 +280,13 @@ When suggesting a code fix, optionally include a GitHub suggestion block at the DO NOT post a single summary comment that lists all findings inline. Each finding gets its own `create_inline_comment` call. -D. **Summary / no-findings case.** After all `create_inline_comment` calls (or if there are none), call -`mcp__github_comment__update_claude_comment` ONCE with a short summary: - -- If you posted N inline findings, the body should be: `Security review complete. Posted N inline finding(s).` plus a - one-line status of any prior findings (resolved / still outstanding / no longer applicable) if this is a re-review. -- If you posted zero inline findings, the body should be: `Security review complete. No new high-confidence findings.` - plus the same prior-findings status line if applicable. - -DO NOT post the full per-finding markdown into this summary comment — the inline comments carry the detail. +D. **No top-level summary comment from you.** Do NOT call any other tool to post a summary or status comment — the +workflow posts a top-level summary comment automatically after this run finishes, based on how many findings you +buffered. If you have no findings, simply exit without calling any tool; the workflow will post the "no findings" +summary. -E. **Tool ordering.** Call `create_inline_comment` for every finding first, then call `update_claude_comment` LAST. Do -not interleave. +E. **Final assistant message.** Your final assistant message should be a one-line plain-text status only, e.g. +`Posted N inline finding(s).` or `No high-confidence findings.` — do NOT include the per-finding markdown in this +message. The inline comments carry the detail. START ANALYSIS now. diff --git a/.github/workflows/pr-security-review.yml b/.github/workflows/pr-security-review.yml index 8ca70b55d..6d2184f84 100644 --- a/.github/workflows/pr-security-review.yml +++ b/.github/workflows/pr-security-review.yml @@ -2,7 +2,7 @@ name: Claude Security Review on: pull_request_target: - types: [opened, reopened] + types: [opened, reopened, synchronize] pull_request_review: types: [submitted] workflow_dispatch: @@ -213,12 +213,50 @@ jobs: use_bedrock: 'true' prompt: ${{ steps.prompt.outputs.prompt }} show_full_output: 'true' - # Allow-listing these MCP tool names is what tells the action to register - # the corresponding MCP servers (github_inline_comment + github_comment). - # See anthropics/claude-code-action src/mcp/install-mcp-server.ts. + # Allow-listing this MCP tool name is what tells the action to register the + # github_inline_comment MCP server. See anthropics/claude-code-action + # src/mcp/install-mcp-server.ts. claude_args: >- --model us.anthropic.claude-opus-4-7 --max-turns 30 --allowedTools - mcp__github_inline_comment__create_inline_comment,mcp__github_comment__update_claude_comment + mcp__github_inline_comment__create_inline_comment + + - name: Count buffered findings + id: findings + if: always() + run: | + set -euo pipefail + BUFFER=/tmp/inline-comments-buffer.jsonl + if [ -s "$BUFFER" ]; then + COUNT=$(wc -l < "$BUFFER" | tr -d ' ') + else + COUNT=0 + fi + echo "count=$COUNT" >> "$GITHUB_OUTPUT" + echo "Buffered findings: $COUNT" + + - name: Post security review summary comment + if: always() + uses: actions/github-script@v9 + env: + PR_NUMBER: ${{ steps.pr.outputs.number }} + FINDING_COUNT: ${{ steps.findings.outputs.count }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + const prNumber = parseInt(process.env.PR_NUMBER, 10); + const count = parseInt(process.env.FINDING_COUNT || '0', 10); + const runUrl = process.env.RUN_URL; + const body = + count > 0 + ? `**Claude Security Review:** posted ${count} inline finding${count === 1 ? '' : 's'} on this PR. ([run](${runUrl}))` + : `**Claude Security Review:** no high-confidence findings. ([run](${runUrl}))`; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body, + }); - name: Remove claude-security-reviewing label if: always() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3994b91fc..2c58358e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -55,7 +55,9 @@ PRs as soon as a maintainer submits an **approving review**. PRs from non-collab approval is the gate, so a maintainer must manually review the diff before the automated reviewer runs. To re-run the review on a later commit, submit another approving review (resolves to a fresh workflow run), or trigger -the `Claude Security Review` workflow manually from the Actions tab with the PR number. +the `Claude Security Review` workflow manually from the Actions tab with the PR number. Note that manual dispatch can +verify the analysis and prompt plumbing but cannot post inline comments — the action's inline-comment MCP server only +attaches on PR-context events (`pull_request_target`, `pull_request_review`). ## Code of Conduct