Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions .github/prompts/security-review.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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:
Expand Down Expand Up @@ -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.
48 changes: 43 additions & 5 deletions .github/workflows/pr-security-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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()
Expand Down
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading