#!/bin/bash
# codex-security-review.sh - Run an AI-powered security vulnerability review using Codex CLI
# Usage: codex-security-review.sh [OPTIONS]
#
# Runs codex exec in read-only sandbox mode with a security-focused review prompt.
# Outputs findings as JSON and optionally posts them to a PR.

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"

# Defaults
PR_NUMBER=""
BASE_BRANCH="main"
COMMIT_SHA=""
OUTPUT_FILE=""
POST_TO_PR=false
UNCOMMITTED=false
REVIEW_MODE=""

show_help() {
    cat <<'EOF'
Usage: codex-security-review.sh [OPTIONS]

Run an AI-powered security vulnerability review using OpenAI Codex CLI.

Options:
  -n, --pr <number>       PR number (auto-enables posting findings to PR)
  -b, --base <branch>     Base branch to diff against (default: main)
  -c, --commit <sha>      Review a specific commit
  -o, --output <path>     Write JSON results to file
  --post-to-pr            Post findings as PR comment (requires -n)
  --uncommitted           Review uncommitted changes (staged + unstaged + untracked)
  -h, --help              Show this help

Examples:
  # Security review uncommitted changes
  codex-security-review.sh --uncommitted

  # Security review a PR and post findings
  codex-security-review.sh -n 42

  # Security review against main, save JSON
  codex-security-review.sh -b main -o security.json

  # Security review a specific commit
  codex-security-review.sh -c abc123f
EOF
    exit 0
}

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -n|--pr)
            PR_NUMBER="$2"
            POST_TO_PR=true
            REVIEW_MODE="pr"
            shift 2
            ;;
        -b|--base)
            BASE_BRANCH="$2"
            REVIEW_MODE="base"
            shift 2
            ;;
        -c|--commit)
            COMMIT_SHA="$2"
            REVIEW_MODE="commit"
            shift 2
            ;;
        -o|--output)
            OUTPUT_FILE="$2"
            shift 2
            ;;
        --post-to-pr)
            POST_TO_PR=true
            shift
            ;;
        --uncommitted)
            UNCOMMITTED=true
            REVIEW_MODE="uncommitted"
            shift
            ;;
        -h|--help)
            show_help
            ;;
        *)
            echo "Unknown option: $1" >&2
            echo "Run with --help for usage" >&2
            exit 1
            ;;
    esac
done

# Validate
if [[ -z "$REVIEW_MODE" ]]; then
    echo "Error: Specify a review mode: --uncommitted, --base <branch>, --commit <sha>, or --pr <number>" >&2
    exit 1
fi

if [[ "$POST_TO_PR" == true && -z "$PR_NUMBER" ]]; then
    echo "Error: --post-to-pr requires -n <pr_number>" >&2
    exit 1
fi

check_codex
check_jq

# Verify we're in a git repo
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
    echo "Error: Not inside a git repository" >&2
    exit 1
fi

# Get the diff context
echo "Gathering diff context..." >&2
case "$REVIEW_MODE" in
    uncommitted) DIFF_CONTEXT=$(build_diff_context "uncommitted" "") ;;
    base)        DIFF_CONTEXT=$(build_diff_context "base" "$BASE_BRANCH") ;;
    commit)      DIFF_CONTEXT=$(build_diff_context "commit" "$COMMIT_SHA") ;;
    pr)          DIFF_CONTEXT=$(build_diff_context "pr" "$PR_NUMBER") ;;
esac

if [[ -z "$DIFF_CONTEXT" ]]; then
    echo "No changes found to review." >&2
    exit 0
fi

# Build the security review prompt
REVIEW_PROMPT=$(cat <<'PROMPT'
You are an expert application security engineer performing a security-focused code review.
Your goal is to identify vulnerabilities, security anti-patterns, and data exposure risks.

## Security Review Scope

### OWASP Top 10 (2021)
- A01: Broken Access Control — missing authorization checks, IDOR, privilege escalation
- A02: Cryptographic Failures — weak algorithms, plaintext secrets, missing encryption
- A03: Injection — SQL, NoSQL, OS command, LDAP, XPath injection
- A04: Insecure Design — missing threat modeling, unsafe business logic
- A05: Security Misconfiguration — debug mode, default credentials, unnecessary features
- A06: Vulnerable Components — known CVEs in dependencies
- A07: Authentication Failures — weak auth, missing MFA, session issues
- A08: Data Integrity Failures — deserialization, unsigned updates
- A09: Logging Failures — sensitive data in logs, missing audit trails
- A10: SSRF — unvalidated URLs, internal service access

### Additional Checks
- Hardcoded secrets, API keys, tokens, passwords
- Insecure direct object references
- Missing input validation at trust boundaries
- Cross-Site Scripting (XSS) — reflected, stored, DOM-based
- Cross-Site Request Forgery (CSRF) protection
- Insecure file handling (path traversal, unrestricted upload)
- Race conditions and TOCTOU vulnerabilities
- Information disclosure (stack traces, verbose errors)
- Supply chain risks (typosquatting, dependency confusion)

## Severity Guide
- **critical**: Exploitable vulnerability with immediate impact (RCE, auth bypass, data breach)
- **high**: Significant vulnerability requiring prompt fix (injection, XSS, secrets exposure)
- **medium**: Vulnerability with limited exploitability or impact (missing headers, weak config)
- **low**: Minor security concern or hardening opportunity (informational, defense-in-depth)

## Rules
- Include CWE IDs when applicable
- Include OWASP category when applicable
- Provide specific remediation steps for every finding
- Only report findings you are confident about
- Do NOT flag non-security code quality issues
- If no security issues found, say so clearly

PROMPT
)

# Set up temp files for output and diff
TEMP_OUTPUT=$(mktemp /tmp/codex-security-XXXXXX.json)
TEMP_DIFF=$(mktemp /tmp/codex-diff-XXXXXX.txt)
trap 'rm -f "$TEMP_OUTPUT" "$TEMP_DIFF"' EXIT

SCHEMA_FILE="$SCRIPT_DIR/schemas/security-review-schema.json"

# Write diff to temp file
echo "$DIFF_CONTEXT" > "$TEMP_DIFF"

echo "Running Codex security review..." >&2
echo "  Diff size: $(wc -l < "$TEMP_DIFF") lines" >&2

# Build full prompt with diff reference
FULL_PROMPT="${REVIEW_PROMPT}

Here are the code changes to security review:

\`\`\`diff
$(cat "$TEMP_DIFF")
\`\`\`"

# Run codex exec with prompt from stdin to avoid arg length limits
echo "$FULL_PROMPT" | codex exec \
    --sandbox read-only \
    --output-schema "$SCHEMA_FILE" \
    -o "$TEMP_OUTPUT" \
    - 2>&1 | while IFS= read -r line; do
    echo "  [codex] $line" >&2
done

# Check output was produced
if [[ ! -s "$TEMP_OUTPUT" ]]; then
    echo "Error: Codex produced no output" >&2
    exit 1
fi

# Validate JSON
if ! jq empty "$TEMP_OUTPUT" 2>/dev/null; then
    echo "Error: Codex output is not valid JSON" >&2
    cat "$TEMP_OUTPUT" >&2
    exit 1
fi

# Save output if requested
if [[ -n "$OUTPUT_FILE" ]]; then
    cp "$TEMP_OUTPUT" "$OUTPUT_FILE"
    echo "Results saved to: $OUTPUT_FILE" >&2
fi

# Post to PR if requested
if [[ "$POST_TO_PR" == true && -n "$PR_NUMBER" ]]; then
    echo "Posting findings to PR #$PR_NUMBER..." >&2
    post_to_pr "$PR_NUMBER" "$TEMP_OUTPUT" "security"
    echo "Posted security review to PR #$PR_NUMBER" >&2
fi

# Always print results to stdout
print_results "$TEMP_OUTPUT" "security"
