#!/usr/bin/env bash
#
# onx-modsec-test-request — Bir HTTP isteği CRS kurallarına karşı simüle et.
#
# Strateji: localhost'a curl ile istek at, ModSecurity audit log'tan match'leri çek.
# Alternatif: modsecurity-cli (varsa) — daha temiz output.
#
# Input: {"method":"GET","uri":"/admin?id=1' OR 1=1","headers":"...","body":"..."}
# Output: {"matched_rules":[...], "anomaly_score":N, "action":"allow|block", "would_block":bool}

set -euo pipefail

input="$(cat)"
method=$(echo "$input" | jq -r '.method // "GET"')
uri=$(echo "$input"    | jq -r '.uri // "/"')
headers=$(echo "$input"| jq -r '.headers // ""')
body=$(echo "$input"   | jq -r '.body // ""')

readonly TEST_HOST="127.0.0.1"
readonly TEST_PORT="80"
readonly LOG_FILE="/var/log/httpd/modsec_audit.log"

# Generate unique marker for this test request
marker="ONOX-TEST-$(date +%s%N)-$RANDOM"

# Build curl args
declare -a curl_args=(
    -s -o /dev/null -w "%{http_code}"
    --max-time 10
    -H "User-Agent: $marker"
    -H "X-ONOX-Test: 1"
    -X "$method"
)

# Add custom headers
if [[ -n "$headers" ]]; then
    while IFS= read -r line; do
        [[ -z "$line" ]] && continue
        curl_args+=(-H "$line")
    done <<<"$headers"
fi

# Add body for POST/PUT/PATCH
if [[ -n "$body" ]] && [[ "$method" == "POST" || "$method" == "PUT" || "$method" == "PATCH" ]]; then
    curl_args+=(--data-binary "$body")
fi

# Record audit log size before
log_size_before=0
[[ -r "$LOG_FILE" ]] && log_size_before=$(stat -c%s "$LOG_FILE" 2>/dev/null || echo 0)

# Send request to localhost (loopback Apache)
response_code=$(curl "${curl_args[@]}" "http://${TEST_HOST}:${TEST_PORT}${uri}" 2>/dev/null || echo "000")

# Wait briefly for log to flush
sleep 0.5

# Read new log entries
matched_rules='[]'
anomaly_score=0
if [[ -r "$LOG_FILE" ]] && [[ $log_size_before -gt 0 ]]; then
    new_entries=$(tail -c +"$((log_size_before + 1))" "$LOG_FILE" 2>/dev/null || echo "")

    # Filter entries that match our marker
    test_block=$(echo "$new_entries" | awk -v marker="$marker" '
        /---.*---A--/ { capture = 0; block = $0 "\n"; next }
        /---.*---Z--/ { if (capture) print block; capture = 0; next }
        { block = block $0 "\n"; if ($0 ~ marker) capture = 1 }
    ')

    if [[ -n "$test_block" ]]; then
        # Extract matched rule IDs + tags + severity
        matched_rules=$(echo "$test_block" \
            | grep -oE '\[id "[0-9]+"\][^\[]*\[msg "[^"]*"\][^\[]*\[severity "[A-Z]+"\]' \
            | while IFS= read -r match; do
                rid=$(echo "$match" | grep -oE 'id "[0-9]+"' | grep -oE '[0-9]+')
                msg=$(echo "$match" | grep -oE 'msg "[^"]+"' | sed 's/msg "//;s/"$//')
                sev=$(echo "$match" | grep -oE 'severity "[A-Z]+"' | sed 's/severity "//;s/"$//')

                # Score from severity
                case "$sev" in
                    CRITICAL) score=5 ;;
                    ERROR)    score=4 ;;
                    WARNING)  score=3 ;;
                    NOTICE)   score=2 ;;
                    *)        score=1 ;;
                esac

                jq -nc --arg rid "$rid" --arg msg "$msg" --arg sev "$sev" --argjson score "$score" \
                    '{rule_id:$rid, category:$msg, severity:$sev, score:$score}'
            done | jq -s '.')

        # Total anomaly score
        anomaly_score=$(echo "$matched_rules" | jq '[.[].score] | add // 0')
    fi
fi

# Determine action
action="allow"
would_block=false
if [[ "$response_code" == "403" || "$response_code" == "406" ]]; then
    action="block"
    would_block=true
elif [[ "$anomaly_score" -ge 5 ]]; then
    action="block"
    would_block=true
fi

jq -nc \
    --argjson matched_rules "$matched_rules" \
    --argjson anomaly_score "$anomaly_score" \
    --arg action "$action" \
    --argjson would_block "$would_block" \
    --arg response_code "$response_code" \
    '{ok:true, matched_rules:$matched_rules, anomaly_score:$anomaly_score, action:$action, would_block:$would_block, http_response_code:$response_code}'
