#!/bin/bash
set -euo pipefail

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

# Use the durable project cache if it exists.
PCC="$ROOT/project_cache_context.json"
CACHE_DIR=""
if [ -f "$PCC" ] && command -v jq >/dev/null 2>&1; then
  CACHE_DIR=$(jq -r '.project_cache_dir // empty' "$PCC")
  [ -n "$CACHE_DIR" ] && CACHE_DIR="$CACHE_DIR/repo"
fi
[ -z "$CACHE_DIR" ] && CACHE_DIR="$ROOT/artifacts/sppb"
mkdir -p "$CACHE_DIR"

# Sudo helper for docker.
if sudo docker ps >/dev/null 2>&1; then
  SUDO="sudo"
else
  SUDO=""
fi

POC_REPO="$CACHE_DIR/poc"
if [ ! -d "$POC_REPO" ]; then
  git clone --depth 1 https://github.com/papageo75/CVE-2026-48908-PoC.git "$POC_REPO" >/dev/null 2>&1 || true
fi

VULN_ZIP_URL="https://www.joomshaper.com/downloads/release/10365"  # 6.6.1
FIXED_ZIP_URL="https://www.joomshaper.com/downloads/release/10367" # 6.6.2
ADMIN_PASS="J00mlaAdminPass!"

run_role() {
  local role="$1"       # vuln or fixed
  local zip_url="$2"
  local project_name="sppb_${role}"
  local compose_dir="$CACHE_DIR/compose_${role}"
  mkdir -p "$compose_dir"

  echo "===== Starting ${role} environment ====="

  cat > "$compose_dir/docker-compose.yml" <<EOF
version: "3.8"
services:
  init:
    image: busybox:stable
    command:
      - sh
      - -c
      - "wget -q -O /extensions/sppb.zip '${zip_url}' && ls -l /extensions/sppb.zip"
    volumes:
      - sppb_zip:/extensions

  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: joomla
      MYSQL_USER: joomla
      MYSQL_PASSWORD: joomla
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-prootpass"]
      interval: 5s
      timeout: 5s
      retries: 15

  joomla:
    image: joomla:5.2-php8.2-apache
    environment:
      JOOMLA_DB_HOST: db
      JOOMLA_DB_USER: joomla
      JOOMLA_DB_PASSWORD: joomla
      JOOMLA_DB_NAME: joomla
      JOOMLA_SITE_NAME: "SPPBTest-${role}"
      JOOMLA_ADMIN_USER: Admin
      JOOMLA_ADMIN_USERNAME: admin
      JOOMLA_ADMIN_PASSWORD: ${ADMIN_PASS}
      JOOMLA_ADMIN_EMAIL: admin@example.com
    volumes:
      - joomla_data:/var/www/html
      - sppb_zip:/extensions:ro
    depends_on:
      init:
        condition: service_completed_successfully
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "test \\"\$(curl -s -o /dev/null -w '%{http_code}' http://localhost:80/)\\" = \\"200\\""]
      interval: 5s
      timeout: 5s
      retries: 60

volumes:
  db_data:
  joomla_data:
  sppb_zip:
EOF

  ${SUDO} docker-compose -p "$project_name" -f "$compose_dir/docker-compose.yml" down -v >/dev/null 2>&1 || true
  ${SUDO} docker-compose -p "$project_name" -f "$compose_dir/docker-compose.yml" up -d 2>&1 | tee "$LOGS/${role}_compose.log"

  echo "Waiting for Joomla to be healthy..."
  local max_wait=180
  local waited=0
  while [ "$waited" -lt "$max_wait" ]; do
    status=$(${SUDO} docker-compose -p "$project_name" -f "$compose_dir/docker-compose.yml" ps --format json 2>/dev/null | jq -s -r '.[] | select(.Service=="joomla") | .Health // .Status' 2>/dev/null || echo "unknown")
    echo "  $(date -Iseconds) joomla status: $status"
    if echo "$status" | grep -q "healthy"; then
      break
    fi
    sleep 5
    waited=$((waited+5))
  done
  if [ "$waited" -ge "$max_wait" ]; then
    echo "ERROR: Joomla did not become healthy in time"
    ${SUDO} docker-compose -p "$project_name" -f "$compose_dir/docker-compose.yml" logs --tail=50 joomla > "$LOGS/${role}_joomla_health.log" 2>&1 || true
    return 1
  fi

  echo "Raising PHP upload limits for the extension install..."
  ${SUDO} docker-compose -p "$project_name" -f "$compose_dir/docker-compose.yml" exec --user root -T joomla bash -c \
    "printf 'upload_max_filesize = 64M\npost_max_size = 64M\nmax_execution_time = 120\n' > /usr/local/etc/php/conf.d/99-upload.ini && apachectl -k graceful" || true
  sleep 5

  echo "Installing SP Page Builder via Joomla web installer..."
  ${SUDO} docker-compose -p "$project_name" -f "$compose_dir/docker-compose.yml" exec -T joomla bash -s <<INSTALL
set -euo pipefail
ADMIN_PASS='${ADMIN_PASS}'
BASE='http://localhost:80'
COOKIE='/tmp/cookies.txt'

# Login page: fetch token and return
login_page=\$(curl -s -c \$COOKIE -b \$COOKIE -L "\$BASE/administrator/index.php")
token=\$(echo "\$login_page" | grep -oE 'name="[a-f0-9]{32}" value="1"' | head -1 | grep -oE '[a-f0-9]{32}')
ret=\$(echo "\$login_page" | grep -oE 'name="return" value="[^"]*"' | head -1 | grep -oE 'value="[^"]*"' | sed 's/value="//;s/"\$//')
[ -z "\$token" ] && { echo "ERROR: no login token"; exit 1; }

curl -s -c \$COOKIE -b \$COOKIE -L -o /tmp/login_result.html \
  -d "option=com_login&task=login&return=\$ret&\$token=1&username=admin&passwd=\$ADMIN_PASS" \
  "\$BASE/administrator/index.php"
if ! grep -q "Home Dashboard" /tmp/login_result.html; then
  echo "ERROR: admin login failed"
  grep -iE 'error|warning|message' /tmp/login_result.html | head -5 || true
  exit 1
fi

# Install page: fetch token
install_page=\$(curl -s -c \$COOKIE -b \$COOKIE -L "\$BASE/administrator/index.php?option=com_installer&view=install")
inst_token=\$(echo "\$install_page" | grep -oE 'name="[a-f0-9]{32}" value="1"' | head -1 | grep -oE '[a-f0-9]{32}')
[ -z "\$inst_token" ] && { echo "ERROR: no install token"; exit 1; }

curl -s -c \$COOKIE -b \$COOKIE -L -D /tmp/install_headers.txt -o /tmp/install_result.html \
  -F "\$inst_token=1" -F 'option=com_installer' -F 'view=install' -F 'task=install.install' \
  -F 'installtype=upload' -F 'install_package=@/extensions/sppb.zip' \
  "\$BASE/administrator/index.php?option=com_installer&view=install&\$inst_token=1"

# Verify installation
php /var/www/html/cli/joomla.php extension:list | grep -i 'SP Page Builder' > /tmp/extension_list.txt
if ! [ -s /tmp/extension_list.txt ]; then
  echo "ERROR: extension did not install"
  cat /tmp/install_result.html | grep -iE 'error|warning|message' | head -10 || true
  exit 1
fi
cat /tmp/extension_list.txt
INSTALL

  echo "Running PoC against the ${role} environment..."
  set +e
  cat "$POC_REPO/sppb_rce.py" | ${SUDO} docker run --rm --network "${project_name}_default" -i python:3-slim bash -c \
    "pip install -q requests && cat > /poc.py && python3 /poc.py http://joomla" \
    > "$LOGS/${role}_poc.log" 2>&1
  local exit_code=$?
  set -e

  echo "PoC exit code for ${role}: $exit_code"
  echo "PoC output:"
  cat "$LOGS/${role}_poc.log"

  ${SUDO} docker-compose -p "$project_name" -f "$compose_dir/docker-compose.yml" down -v >/dev/null 2>&1 || true

  echo "$exit_code" > "$LOGS/${role}_exitcode.txt"
  return 0
}

vuln_ok=0
fixed_ok=0

if run_role "vuln" "$VULN_ZIP_URL"; then
  if [ "$(cat "$LOGS/vuln_exitcode.txt")" = "0" ]; then
    vuln_ok=1
  fi
else
  echo "ERROR: vulnerable environment failed to start"
fi

if run_role "fixed" "$FIXED_ZIP_URL"; then
  if grep -q "SP Page Builder is patched (6.6.2+)" "$LOGS/fixed_poc.log" 2>/dev/null; then
    fixed_ok=1
  fi
else
  echo "ERROR: fixed environment failed to start"
fi

echo ""
echo "===== RESULTS ====="
echo "vuln_ok=$vuln_ok fixed_ok=$fixed_ok"

# Defaults for the runtime manifest
entrypoint_detail="index.php?option=com_sppagebuilder&task=asset.uploadCustomIcon"
healthcheck_passed=false
target_path_reached=false
service_started=false
claim_outcome="unknown"
repro_result="inconclusive"
observed="none"

if [ "$vuln_ok" -eq 1 ] && [ "$fixed_ok" -eq 1 ]; then
  OUTCOME="confirmed"
  NOTES="Vulnerable 6.6.1 allowed unauthenticated RCE via asset.uploadCustomIcon; fixed 6.6.2 rejected the upload."
  healthcheck_passed=true
  target_path_reached=true
  service_started=true
  claim_outcome="confirmed"
  repro_result="confirmed"
  observed="code_execution"
elif [ "$vuln_ok" -eq 1 ]; then
  OUTCOME="partial"
  NOTES="Vulnerable version reproduced, but fixed-version negative control did not reject the exploit."
  healthcheck_passed=true
  target_path_reached=true
  service_started=true
  claim_outcome="partial"
  repro_result="confirmed"
  observed="code_execution"
else
  OUTCOME="inconclusive"
  NOTES="Unable to reproduce the vulnerability on the deployed environment."
fi

jq -n \
  --arg outcome "$OUTCOME" \
  --arg notes "$NOTES" \
  --arg entrypoint_detail "$entrypoint_detail" \
  --argjson healthcheck "$healthcheck_passed" \
  --argjson target "$target_path_reached" \
  --argjson service "$service_started" \
  '{
    entrypoint_kind: "endpoint",
    entrypoint_detail: $entrypoint_detail,
    service_started: $service,
    healthcheck_passed: $healthcheck,
    target_path_reached: $target,
    runtime_stack: ["joomla", "mysql", "sp-page-builder"],
    proof_artifacts: [
      "logs/vuln_compose.log",
      "logs/vuln_poc.log",
      "logs/fixed_compose.log",
      "logs/fixed_poc.log"
    ],
    notes: $notes
  }' > "$REPRO_DIR/runtime_manifest.json"

echo "Wrote runtime_manifest.json"

if [ "$OUTCOME" = "confirmed" ]; then
  exit 0
else
  exit 1
fi
