Skip to content

feat(ci): fail on critical vulnerabilities #3

feat(ci): fail on critical vulnerabilities

feat(ci): fail on critical vulnerabilities #3

name: AI Security Audit
on:
pull_request:
types: [opened, synchronize, reopened]
permissions:
contents: read
pull-requests: write
jobs:
ai-security-audit:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check ANTHROPIC_API_KEY
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
if [ -z "$ANTHROPIC_API_KEY" ]; then
echo "::error::ANTHROPIC_API_KEY is not configured. Please add it to your repository secrets (Settings > Secrets and variables > Actions > New repository secret). The AI security audit cannot run without this key."
exit 1
fi
- name: Get PR diff
id: diff
env:
GH_TOKEN: ${{ github.token }}
run: |
gh pr diff ${{ github.event.pull_request.number }} > pr_diff.txt
echo "diff_size=$(wc -c < pr_diff.txt | tr -d ' ')" >> "$GITHUB_OUTPUT"
- name: Check diff size
id: check
run: |
if [ "${{ steps.diff.outputs.diff_size }}" -eq 0 ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "No diff found, skipping audit."
elif [ "${{ steps.diff.outputs.diff_size }}" -gt 200000 ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "Diff too large (>200KB), skipping AI audit."
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Install Claude Code
if: steps.check.outputs.skip != 'true'
run: npm install -g @anthropic-ai/claude-code
- name: Run AI security audit
if: steps.check.outputs.skip != 'true'
id: audit
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
PROMPT=$(cat <<'AUDIT_PROMPT'
You are a senior security engineer performing a security audit on a pull request diff.
Analyze the following code diff and provide a structured security audit report.
Focus on these areas:
1. **Critical Vulnerabilities**: SQL injection, command injection, XSS, SSRF, deserialization flaws, path traversal
2. **Authentication & Authorization**: Broken auth, missing access controls, credential exposure
3. **Cryptography Issues**: Weak algorithms, hardcoded keys/secrets, improper random number generation
4. **Data Exposure**: Sensitive data leaks, PII exposure, excessive logging of secrets
5. **Dependency Risks**: Known vulnerable patterns, unsafe deserialization
6. **Blockchain-Specific**: Private key handling, transaction signing flaws, smart contract interaction risks, address validation
7. **Input Validation**: Missing or insufficient validation, type confusion, buffer issues
8. **Configuration & Infrastructure**: Insecure defaults, debug mode in production, permissive CORS
Output format (use GitHub-flavored Markdown):
## AI Security Audit Report
### Summary
[One-paragraph overall assessment with risk level: CRITICAL / HIGH / MEDIUM / LOW / CLEAN]
### Findings
For each finding:
#### [SEVERITY] Finding Title
- **File**: `filename:line_number`
- **Category**: [category from above]
- **Description**: What the issue is
- **Impact**: What could go wrong
- **Recommendation**: How to fix it
If no security issues are found, state that the code appears clean and list what was checked.
### Statistics
- Files analyzed: X
- Issues found: X (critical: X, high: X, medium: X, low: X)
---
*This report was generated by AI security audit. Please verify findings manually.*
Here is the diff to audit:
AUDIT_PROMPT
)
DIFF_CONTENT=$(cat pr_diff.txt)
FULL_PROMPT="${PROMPT}
\`\`\`diff
${DIFF_CONTENT}
\`\`\`"
# Run claude and capture output
AUDIT_RESULT=$(echo "$FULL_PROMPT" | claude -p --output-format text 2>&1) || true
# Save result to file (avoid shell escaping issues)
echo "$AUDIT_RESULT" > audit_result.md
- name: Post audit comment
if: steps.check.outputs.skip != 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
# Build comment body
{
echo "<!-- ai-security-audit -->"
echo ""
cat audit_result.md
} > comment_body.md
PR_NUMBER=${{ github.event.pull_request.number }}
# Delete previous audit comment if exists
EXISTING_COMMENT_ID=$(gh api \
"repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \
--jq '.[] | select(.body | contains("<!-- ai-security-audit -->")) | .id' \
| head -1)
if [ -n "$EXISTING_COMMENT_ID" ]; then
gh api \
--method DELETE \
"repos/${{ github.repository }}/issues/comments/${EXISTING_COMMENT_ID}" \
|| true
fi
# Post new comment
gh pr comment "$PR_NUMBER" --body-file comment_body.md
- name: Fail if critical issues found
if: steps.check.outputs.skip != 'true'
run: |
if grep -qi '\[CRITICAL\]' audit_result.md; then
CRITICAL_COUNT=$(grep -ci '\[CRITICAL\]' audit_result.md)
echo "::error::AI security audit found ${CRITICAL_COUNT} CRITICAL issue(s). Please fix them before merging."
exit 1
fi
- name: Post skip comment
if: steps.check.outputs.skip == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
REASON="No diff found"
if [ "${{ steps.diff.outputs.diff_size }}" -gt 200000 ]; then
REASON="Diff too large (>200KB) for AI audit"
fi
BODY="<!-- ai-security-audit -->
## AI Security Audit Report
**Skipped**: ${REASON}.
Please perform a manual security review."
# Delete previous audit comment if exists
EXISTING_COMMENT_ID=$(gh api \
"repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" \
--jq '.[] | select(.body | contains("<!-- ai-security-audit -->")) | .id' \
| head -1)
if [ -n "$EXISTING_COMMENT_ID" ]; then
gh api \
--method DELETE \
"repos/${{ github.repository }}/issues/comments/${EXISTING_COMMENT_ID}" \
|| true
fi
gh pr comment "$PR_NUMBER" --body "$BODY"