#!/bin/bash
set -euo pipefail

ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
CODING="$ROOT/coding"
mkdir -p "$LOGS"

exec > >(tee -a "$LOGS/verify_fix.log")
exec 2>&1

log() { echo "[VERIFY] $(date -Iseconds) $*"; }

# ---------------------------------------------------------------------------
# Project cache / repository setup
# ---------------------------------------------------------------------------
CACHE_CTX="$ROOT/project_cache_context.json"
PROJECT_CACHE_DIR="$ROOT/artifacts/civetweb"
if [ -f "$CACHE_CTX" ]; then
    PROJECT_CACHE_DIR="$(jq -r '.project_cache_dir // empty' "$CACHE_CTX" || true)"
    if [ -z "$PROJECT_CACHE_DIR" ]; then
        PROJECT_CACHE_DIR="$ROOT/artifacts/civetweb"
    fi
fi
REPO="$PROJECT_CACHE_DIR/repo"
PATCH="$CODING/proposed_fix.diff"
VULN_COMMIT="3309a6ca"

log "Project cache dir: $PROJECT_CACHE_DIR"
log "Repo path: $REPO"
log "Patch: $PATCH"
log "Vulnerable commit: $VULN_COMMIT"

if [ ! -f "$PATCH" ]; then
    log "ERROR: patch file not found at $PATCH"
    exit 1
fi

if [ ! -d "$REPO/.git" ]; then
    log "ERROR: repository not found at $REPO"
    exit 1
fi

BUILD_DIR="$PROJECT_CACHE_DIR/verify-build-coding"
TEST_DIR="$PROJECT_CACHE_DIR/verify-test-coding"
DOCROOT="$TEST_DIR/docroot"

# Clean and recreate build/test directories
rm -rf "$BUILD_DIR" "$TEST_DIR"
mkdir -p "$BUILD_DIR" "$DOCROOT"

# Export source from vulnerable commit
cd "$BUILD_DIR"
log "Exporting source from commit $VULN_COMMIT..."
git -C "$REPO" archive --format=tar "$VULN_COMMIT" | tar -x

# Apply the proposed fix
log "Applying patch..."
patch -p1 < "$PATCH"
log "Patch applied cleanly."

# Build the patched binary
log "Building patched binary..."
make clean 2>/dev/null || true
make -j"$(nproc)"
if [ ! -x "$BUILD_DIR/civetweb" ]; then
    log "ERROR: patched binary not built"
    exit 1
fi
log "Patched binary built: $BUILD_DIR/civetweb"

# Create digest password file: user:realm:md5(user:realm:password)
PASSWD="$TEST_DIR/passwd.txt"
HASH=$(printf '%s' "admin:mydomain.com:password" | md5sum | awk '{print $1}')
printf 'admin:mydomain.com:%s\n' "$HASH" > "$PASSWD"

PORT=8099

# Start patched server
"$BUILD_DIR/civetweb" -listening_ports "$PORT" \
    -document_root "$DOCROOT" \
    -put_delete_auth_file "$PASSWD" \
    -authentication_domain mydomain.com \
    > "$TEST_DIR/server.log" 2>&1 &
PID=$!
log "Server PID $PID on port $PORT"

# Wait for health check
healthy=0
for i in $(seq 1 30); do
    if curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:$PORT" 2>/dev/null | grep -q '^200$'; then
        healthy=1
        break
    fi
    sleep 0.2
done

if [ "$healthy" -eq 0 ]; then
    log "ERROR: server failed to become healthy"
    log "Server log tail:"
    tail -n 20 "$TEST_DIR/server.log" || true
    kill "$PID" 2>/dev/null || true
    wait "$PID" 2>/dev/null || true
    exit 1
fi
log "Server healthy."

# Upload the exploit payload via PUT
curl -s --max-time 5 --digest -u admin:password -X PUT \
    --data-binary '<!--#exec "id; uname -a" -->' \
    "http://127.0.0.1:$PORT/pwn.shtml" \
    -o /dev/null -D "$TEST_DIR/put_headers.txt" \
    2>"$TEST_DIR/put_stderr.txt" || true
PUT_STATUS=$(grep -E '^HTTP' "$TEST_DIR/put_headers.txt" 2>/dev/null | tail -1 | awk '{print $2}' || true)
log "PUT status: $PUT_STATUS"

# Retrieve the uploaded file via GET
curl -s --max-time 5 --digest -u admin:password \
    "http://127.0.0.1:$PORT/pwn.shtml" \
    -o "$TEST_DIR/get_body.txt" \
    -D "$TEST_DIR/get_headers.txt" \
    2>"$TEST_DIR/get_stderr.txt" || true
GET_STATUS=$(grep -E '^HTTP' "$TEST_DIR/get_headers.txt" 2>/dev/null | tail -1 | awk '{print $2}' || true)
log "GET status: $GET_STATUS"
log "GET body:"
tail -c 500 "$TEST_DIR/get_body.txt" || true

# Stop server
kill "$PID" 2>/dev/null || true
wait "$PID" 2>/dev/null || true

# Verify the exploit no longer executes commands
if [ ! -f "$TEST_DIR/get_body.txt" ]; then
    log "ERROR: no GET response body captured"
    exit 1
fi

if grep -qE "uid=|Linux" "$TEST_DIR/get_body.txt"; then
    log "FAIL: command output still present in GET response — fix did not work"
    exit 1
fi

if [ "$GET_STATUS" != "200" ]; then
    log "ERROR: GET request did not return 200 (status: $GET_STATUS)"
    exit 1
fi

log "PASS: patched server accepted the PUT upload but did not execute the SSI #exec command."
exit 0
