diff --git a/.github/workflows/ai-security-audit.yml b/.github/workflows/ai-security-audit.yml new file mode 100644 index 00000000..d86a6225 --- /dev/null +++ b/.github/workflows/ai-security-audit.yml @@ -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 "" + 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("")) | .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 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("")) | .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"