Skip to content
Open
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
186 changes: 186 additions & 0 deletions .github/workflows/ai-security-audit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
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"