#!/usr/bin/env bash
#
# onx-modsec-audit-search — /var/log/httpd/modsec_audit.log parse + JSON.
#
# Input: {"limit": 500, "since_hours": 24, "rule_id":"(opt)", "ip":"(opt)"}
# Output: {"events":[...], "total":N}

set -euo pipefail

readonly LOG_FILE="/var/log/httpd/modsec_audit.log"

input="$(cat 2>/dev/null || echo '{}')"
limit="$(echo "$input"  | jq -r '.limit // 500')"
hours="$(echo "$input"  | jq -r '.since_hours // 24')"
filter_rule="$(echo "$input" | jq -r '.rule_id // empty')"
filter_ip="$(echo "$input"   | jq -r '.ip // empty')"

[[ "$limit" =~ ^[0-9]+$ ]] || limit=500
[[ "$hours" =~ ^[0-9]+$ ]] || hours=24

if [[ ! -r "$LOG_FILE" ]]; then
    jq -nc --arg p "$LOG_FILE" '{ok:false,error:"audit log not readable",path:$p,events:[]}' >&2
    exit 2
fi

# Audit log "Concurrent" format genelde JSON-per-line; "Serial" format multi-line
# Bu script Serial format için: --A-- ile başlayan blok = bir event.
since_epoch=$(date -d "$hours hours ago" +%s 2>/dev/null || date -v "-${hours}H" +%s)

events_json="["
first=1
count=0

# AWK ile parse: --A-- ile başlayan blokları topla, --Z-- ile bitir, satırları process et
awk -v since="$since_epoch" -v limit="$limit" -v frule="$filter_rule" -v fip="$filter_ip" '
BEGIN { blk = ""; in_block = 0 }
/^---[A-Za-z0-9-]+---A--$/ { blk = ""; in_block = 1; next }
/^---[A-Za-z0-9-]+---Z--$/ {
    if (in_block && blk != "") print blk "<<<EOF>>>"
    in_block = 0; blk = ""
    next
}
in_block { blk = blk $0 "\n" }
' "$LOG_FILE" 2>/dev/null | while IFS= read -r -d '<' line; do
    # Each "line" is now one event block
    block="${line%<<EOF>>>*}"
    [[ -z "$block" ]] && continue

    # Parse A header: timestamp, unique_id, source IP
    ts_unix=$(echo "$block" | head -1 | grep -oE '\[[^\]]+\]' | head -1 | sed 's/\[//;s/\]//' || true)
    if [[ -n "$ts_unix" ]]; then
        ts_epoch=$(date -d "$ts_unix" +%s 2>/dev/null || echo 0)
        [[ $ts_epoch -lt $since_epoch ]] && continue
    fi

    event_id=$(echo "$block" | head -1 | awk '{print $2}' || echo "")
    src_ip=$(echo "$block" | grep -oE 'X-Forwarded-For: [0-9.]+' | head -1 | awk '{print $2}' || true)
    [[ -z "$src_ip" ]] && src_ip=$(echo "$block" | head -1 | awk '{print $4}' || echo "0.0.0.0")

    # Parse B section: request line + host header
    method=$(echo "$block" | awk '/--B--/,/^$/' | head -1 | awk '{print $1}' || echo "GET")
    uri=$(echo "$block"    | awk '/--B--/,/^$/' | head -1 | awk '{print $2}' || echo "/")
    domain=$(echo "$block" | awk '/^Host:/ {print $2; exit}' | tr -d '\r' || echo "")

    # Parse H section: ModSec messages — rule_id and tag and severity
    rule_id=$(echo "$block" | grep -oE '\[id "[0-9]+"\]' | head -1 | grep -oE '[0-9]+' || echo "")
    rule_tag=$(echo "$block" | grep -oE '\[tag "[^"]+"\]' | head -1 | sed 's/\[tag "//;s/"\]//' || echo "")
    severity=$(echo "$block" | grep -oE 'severity "[^"]+"' | head -1 | sed 's/severity "//;s/"//' | tr '[:lower:]' '[:upper:]' || echo "")
    msg=$(echo "$block"  | grep -oE '\[msg "[^"]+"\]'  | head -1 | sed 's/\[msg "//;s/"\]//' || echo "")

    # Action determined by response code
    response_code=$(echo "$block" | grep -oE '^HTTP/[0-9.]+ [0-9]+' | head -1 | awk '{print $2}' || echo "")
    action_taken="passed"
    [[ "$response_code" == "403" || "$response_code" == "406" ]] && action_taken="blocked"

    # Filters
    [[ -n "$filter_rule" && "$rule_id" != "$filter_rule" ]] && continue
    [[ -n "$filter_ip"   && "$src_ip"  != "$filter_ip"   ]] && continue
    [[ -z "$rule_id" ]] && continue

    # JSON build
    [[ $first -eq 0 ]] && events_json+=","
    first=0
    events_json+="$(jq -nc \
        --arg event_id "$event_id" \
        --arg source_ip "$src_ip" \
        --arg domain "$domain" \
        --arg uri "$uri" \
        --arg method "$method" \
        --arg rule_id "$rule_id" \
        --arg rule_tag "$rule_tag" \
        --arg severity "$severity" \
        --arg reason "$msg" \
        --arg action_taken "$action_taken" \
        --argjson response_code "${response_code:-null}" \
        --arg event_at "$(date -d "$ts_unix" -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "$ts_unix")" \
        '{event_id:$event_id, source_ip:$source_ip, domain_name:$domain, uri:$uri, method:$method, rule_id:$rule_id, rule_tag:$rule_tag, severity:$severity, reason:$reason, action_taken:$action_taken, response_code:$response_code, event_at:$event_at}')"

    count=$((count + 1))
    [[ $count -ge $limit ]] && break
done

events_json+="]"

jq -nc --argjson events "$events_json" --argjson total "$count" \
    '{ok:true, events:$events, total:$total}'
