Skip to content
Merged
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
45 changes: 27 additions & 18 deletions .github/workflows/security-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
permissions:
contents: read
pull-requests: write
issues: write
id-token: write

jobs:
Expand All @@ -31,40 +32,48 @@ jobs:
Read that file first, then follow its instructions exactly.
Review only the changes introduced by this PR.
Post your findings as a structured review comment.
claude_args: |
--max-turns 50
--model claude-opus-4-6
claude_args: --max-turns 50 --model claude-opus-4-7

- name: Post fallback comment on failure
if: steps.claude-review.outcome == 'failure'
- name: Post status comment if Claude did not comment
if: always()
uses: actions/github-script@v7
with:
script: |
// Check if there's already a sticky comment from Claude
const { data: comments } = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
});
const outcome = '${{ steps.claude-review.outcome }}';
// Claude posts its own sticky comment when successful; only intervene if it didn't.
const hasClaudeComment = comments.some(c =>
c.body && c.body.includes('Security Review')
c.body && c.body.includes('Security Review') && c.user.type === 'Bot'
);
if (!hasClaudeComment) {
const isFailure = outcome === 'failure' || outcome === 'cancelled';
await github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: [
'## Security Review',
'',
'⚠️ **Automated security review did not complete.**',
'',
'Claude hit the max-turns limit or encountered an error before posting findings.',
'A manual review of S0 (project-scoped data access), S1 (authorization policies),',
'and S2 (audit trail coverage) is recommended for this PR.',
'',
'See the [workflow run](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId + ') for details.',
].join('\n'),
body: isFailure
? [
'## Security Review',
'',
'⚠️ **Automated security review did not complete.**',
'',
'Claude hit the max-turns limit or encountered an error before posting findings.',
'A manual review of S0 (project-scoped data access), S1 (authorization policies),',
'and S2 (audit trail coverage) is recommended for this PR.',
'',
'See the [workflow run](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId + ') for details.',
].join('\n')
: [
'## Security Review',
'',
'⚠️ The review completed but no findings comment was posted.',
'',
'See the [workflow run](https://github.com/' + context.repo.owner + '/' + context.repo.repo + '/actions/runs/' + context.runId + ') for the raw Claude output.',
].join('\n'),
});
}

Expand Down
Loading