#!/bin/bash
set -euo pipefail

# Exit 0 means a distinct bypass reproduced on WordPress 7.0.2/latest.
# Exit 1 means the bounded variant search ran successfully but found no bypass.
# Exit 2 means infrastructure or a required oracle failed.

ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
VARIANT_DIR="$ROOT/vuln_variant"
LOGS="$ROOT/logs/vuln_variant"
ARTIFACTS="$VARIANT_DIR/artifacts"
HELPER="$VARIANT_DIR/variant_probe.py"
mkdir -p "$LOGS" "$ARTIFACTS"
cd "$ROOT"

RUN_LOG="$LOGS/reproduction_steps.log"
: > "$RUN_LOG"
exec > >(tee -a "$RUN_LOG") 2>&1

WP_IMAGE="wordpress:7.0.1-php8.2-apache@sha256:bfc320ed4f02dd3939186b8020de64203a48a939d6dedcf44cb92cf2368923f5"
MYSQL_IMAGE="mysql:8.0@sha256:7dcddc01f13bab2f15cde676d44d01f61fc9f99fe7785e86196dfc07d358ae2b"
DRIVER_IMAGE="python:3.12-slim@sha256:423ed6ab25b1921a477529254bfeeabf5855151dc2c3141699a1bfc852199fbf"
VULN_COMMIT="18f793b1f16c1b15b0fc37027f4aeaefab0bfe02"
FIXED_COMMIT="855551c4477bd5a0407221c57dae123c4163b434"
LATEST_COMMIT="ace9192af868524bdc49cf4fcb91f4c12c73ee5f"

[ -f "$HELPER" ] || { echo "[INFRA] Missing $HELPER"; exit 2; }
command -v docker >/dev/null || { echo "[INFRA] Docker is unavailable"; exit 2; }
docker info >/dev/null 2>&1 || { echo "[INFRA] Docker daemon is unavailable"; exit 2; }
command -v git >/dev/null || { echo "[INFRA] Git is unavailable"; exit 2; }
command -v jq >/dev/null || { echo "[INFRA] jq is unavailable"; exit 2; }
command -v timeout >/dev/null || { echo "[INFRA] timeout is unavailable"; exit 2; }

CACHE_DIR=""
if [ -f "$ROOT/project_cache_context.json" ]; then
  if [ "$(jq -r '.prepared // false' "$ROOT/project_cache_context.json")" = true ]; then
    CACHE_DIR="$(jq -r '.project_cache_dir // empty' "$ROOT/project_cache_context.json")"
  fi
fi
REPO="${CACHE_DIR:+$CACHE_DIR/repo}"
if [ -z "$REPO" ] || [ ! -d "$REPO/.git" ]; then
  REPO="$ARTIFACTS/wordpress-develop"
  [ -d "$REPO/.git" ] || git clone https://github.com/WordPress/wordpress-develop.git "$REPO"
fi

for commit in "$VULN_COMMIT" "$FIXED_COMMIT" "$LATEST_COMMIT"; do
  git -C "$REPO" cat-file -e "$commit^{commit}" 2>/dev/null || git -C "$REPO" fetch --no-tags origin "$commit"
  [ "$(git -C "$REPO" rev-parse "$commit")" = "$commit" ] || { echo "[INFRA] Source identity mismatch for $commit"; exit 2; }
done

# The latest revision was resolved from origin/trunk on 2026-07-18 and is
# commit-pinned above. Record the current remote head for provenance, but do
# not make a reproducible historical replay depend on a moving branch.
REMOTE_TRUNK="$(git -C "$REPO" ls-remote origin refs/heads/trunk | awk '{print $1}' || true)"

{
  echo "repository=https://github.com/WordPress/wordpress-develop.git"
  echo "vulnerable_commit=$VULN_COMMIT"
  echo "fixed_commit=$FIXED_COMMIT"
  echo "latest_snapshot_commit=$LATEST_COMMIT"
  echo "remote_trunk_observed=${REMOTE_TRUNK:-unavailable}"
  git -C "$REPO" log -1 --format='fixed_object=%H fixed_date=%cI fixed_subject=%s' "$FIXED_COMMIT"
  git -C "$REPO" log -1 --format='latest_object=%H latest_date=%cI latest_subject=%s' "$LATEST_COMMIT"
} | tee "$LOGS/source_identity.log" "$LOGS/fixed_version.txt" "$LOGS/latest_version.txt"

git -C "$REPO" show "$FIXED_COMMIT:SECURITY.md" > "$LOGS/security_policy.md"
git -C "$REPO" diff "$VULN_COMMIT..$FIXED_COMMIT" -- \
  src/wp-includes/class-wp-query.php \
  src/wp-includes/rest-api.php \
  src/wp-includes/rest-api/class-wp-rest-server.php > "$LOGS/fix_diff.txt"
{
  fixed_batch="$(git -C "$REPO" grep -h -E '\$matches\[\][[:space:]]*=.*\$single_request' "$FIXED_COMMIT" -- src/wp-includes/rest-api/class-wp-rest-server.php 2>/dev/null | wc -l)"
  latest_batch="$(git -C "$REPO" grep -h -E '\$matches\[\][[:space:]]*=.*\$single_request' "$LATEST_COMMIT" -- src/wp-includes/rest-api/class-wp-rest-server.php 2>/dev/null | wc -l)"
  fixed_nested="$(git -C "$REPO" grep -h -F 'is_dispatching()' "$FIXED_COMMIT" -- src/wp-includes/rest-api.php src/wp-includes/rest-api/class-wp-rest-server.php 2>/dev/null | wc -l)"
  latest_nested="$(git -C "$REPO" grep -h -F 'is_dispatching()' "$LATEST_COMMIT" -- src/wp-includes/rest-api.php src/wp-includes/rest-api/class-wp-rest-server.php 2>/dev/null | wc -l)"
  fixed_author="$(git -C "$REPO" grep -h -F "wp_parse_id_list( \$query_vars['author__not_in'] )" "$FIXED_COMMIT" -- src/wp-includes/class-wp-query.php 2>/dev/null | wc -l)"
  latest_author="$(git -C "$REPO" grep -h -F "wp_parse_id_list( \$query_vars['author__not_in'] )" "$LATEST_COMMIT" -- src/wp-includes/class-wp-query.php 2>/dev/null | wc -l)"
  echo "fixed_contains_batch_alignment=$fixed_batch"
  echo "latest_contains_batch_alignment=$latest_batch"
  echo "fixed_contains_nested_dispatch_guard=$fixed_nested"
  echo "latest_contains_nested_dispatch_guard=$latest_nested"
  echo "fixed_contains_author_id_normalization=$fixed_author"
  echo "latest_contains_author_id_normalization=$latest_author"
  echo "serve_request_call_sites_latest:"
  git -C "$REPO" grep -n -E -- '->serve_request\(' "$LATEST_COMMIT" -- 'src/**/*.php' || true
} | tee "$LOGS/static_coverage.log"

grep -q '^fixed_contains_batch_alignment=1$' "$LOGS/static_coverage.log" || { echo "[INFRA] Fixed batch guard absent"; exit 2; }
grep -q '^latest_contains_batch_alignment=1$' "$LOGS/static_coverage.log" || { echo "[INFRA] Latest batch guard absent"; exit 2; }
grep -Eq '^fixed_contains_nested_dispatch_guard=[1-9]' "$LOGS/static_coverage.log" || { echo "[INFRA] Fixed nested-dispatch guard absent"; exit 2; }
grep -Eq '^latest_contains_nested_dispatch_guard=[1-9]' "$LOGS/static_coverage.log" || { echo "[INFRA] Latest nested-dispatch guard absent"; exit 2; }
grep -q '^fixed_contains_author_id_normalization=1$' "$LOGS/static_coverage.log" || { echo "[INFRA] Fixed author normalization absent"; exit 2; }
grep -q '^latest_contains_author_id_normalization=1$' "$LOGS/static_coverage.log" || { echo "[INFRA] Latest author normalization absent"; exit 2; }

for image in "$WP_IMAGE" "$MYSQL_IMAGE" "$DRIVER_IMAGE"; do
  docker image inspect "$image" >/dev/null 2>&1 || docker pull "$image"
done

TOKEN="${PRUVA_VARIANT_TOKEN:-$(date +%s)-$$}"
SAFE="$(printf '%s' "$TOKEN" | tr -cd 'a-zA-Z0-9' | cut -c1-14)"
[ -n "$SAFE" ] || SAFE="$$"
PREFIX="pruva-vv-63030-$SAFE"
NET="$PREFIX-net"
DB="$PREFIX-db"
VULN_WP="$PREFIX-vuln"
FIXED_WP="$PREFIX-fixed"
DRIVER="$PREFIX-driver"
DB_PASS="VariantDb-${SAFE}!"
ROOT_PASS="VariantRoot-${SAFE}!"
CLEANED=false

cleanup() {
  local rc=$?
  if [ "$CLEANED" != true ]; then
    docker rm -fv "$DRIVER" "$VULN_WP" "$FIXED_WP" "$DB" >/dev/null 2>&1 || true
    docker network rm "$NET" >/dev/null 2>&1 || true
    CLEANED=true
  fi
  exit "$rc"
}
trap cleanup EXIT INT TERM

docker rm -fv "$DRIVER" "$VULN_WP" "$FIXED_WP" "$DB" >/dev/null 2>&1 || true
docker network rm "$NET" >/dev/null 2>&1 || true
docker network create --internal "$NET" >/dev/null

docker run -d --name "$DB" --network "$NET" \
  -e MYSQL_ROOT_PASSWORD="$ROOT_PASS" -e MYSQL_DATABASE=wordpress \
  -e MYSQL_USER=wordpress -e MYSQL_PASSWORD="$DB_PASS" \
  "$MYSQL_IMAGE" --local-infile=0 >/dev/null
for _ in $(seq 1 120); do
  docker exec "$DB" mysql -h localhost -uroot -p"$ROOT_PASS" -NBe 'SELECT 1' >/dev/null 2>&1 && break
  sleep 1
done
docker exec "$DB" mysql -h localhost -uroot -p"$ROOT_PASS" -NBe 'SELECT 1' >/dev/null 2>&1 || { echo "[INFRA] MySQL authenticated readiness failed"; exit 2; }

docker exec "$DB" mysql -uroot -p"$ROOT_PASS" -e \
  "CREATE DATABASE wordpress_fixed;" 2>/dev/null
docker exec "$DB" mysql -uroot -p"$ROOT_PASS" -e \
  "GRANT ALL PRIVILEGES ON wordpress_fixed.* TO 'wordpress'@'%'; FLUSH PRIVILEGES;" 2>/dev/null

start_wp() {
  local name="$1" database="$2"
  docker run -d --name "$name" --network "$NET" \
    -e WORDPRESS_DB_HOST="$DB:3306" -e WORDPRESS_DB_USER=wordpress \
    -e WORDPRESS_DB_PASSWORD="$DB_PASS" -e WORDPRESS_DB_NAME="$database" \
    "$WP_IMAGE" >/dev/null
  for _ in $(seq 1 120); do
    code="$(docker exec "$name" curl -sS --max-time 3 -o /dev/null -w '%{http_code}' http://127.0.0.1/ 2>/dev/null || true)"
    [ "$code" = 200 ] || [ "$code" = 302 ] || { sleep 1; continue; }
    return 0
  done
  echo "[INFRA] WordPress readiness failed for $name"
  exit 2
}

start_wp "$VULN_WP" wordpress
start_wp "$FIXED_WP" wordpress_fixed

docker exec "$FIXED_WP" mkdir -p /tmp/wp702
git -C "$REPO" archive --format=tar "$FIXED_COMMIT:src" \
  | docker exec -i "$FIXED_WP" tar -xf - -C /tmp/wp702 \
  || { echo "[INFRA] Failed to stream fixed source overlay"; exit 2; }
docker exec "$FIXED_WP" sh -c \
  'cp -a /tmp/wp702/. /var/www/html/ && chown -R www-data:www-data /var/www/html'

docker run -d --name "$DRIVER" --network "$NET" "$DRIVER_IMAGE" sleep infinity >/dev/null
docker cp "$HELPER" "$DRIVER:/variant_probe.py"

install_target() {
  local host="$1"
  docker exec -i "$DRIVER" python3 - "$host" <<'PY'
import secrets, sys, urllib.parse, urllib.request
host=sys.argv[1]
password='Owner!'+secrets.token_urlsafe(48)
data=urllib.parse.urlencode({
 'weblog_title':'Pruva variant lab','user_name':'owner',
 'admin_password':password,'admin_password2':password,'pw_weak':'1',
 'admin_email':'owner@invalid.test','blog_public':'0','Submit':'Install WordPress','language':''
}).encode()
req=urllib.request.Request(f'http://{host}/wp-admin/install.php?step=2',data=data,headers={'Content-Type':'application/x-www-form-urlencoded'})
page=urllib.request.urlopen(req,timeout=30).read().decode(errors='replace')
if 'WordPress has been installed' not in page: raise SystemExit('installation failed')
print('installed_with_unrecoverable_random_owner_password=true')
PY
}
install_target "$VULN_WP" | tee "$LOGS/vulnerable_install.log"
install_target "$FIXED_WP" | tee "$LOGS/fixed_install.log"

capture_state() {
  local label="$1" wp="$2" database="$3" out="$4"
  {
    echo "label=$label"
    echo "wordpress_version=$(docker exec "$wp" php -r 'include "/var/www/html/wp-includes/version.php"; echo $wp_version;')"
    echo "wordpress_image=$(docker inspect "$wp" --format '{{.Config.Image}}')"
    echo "wordpress_mounts=$(docker inspect "$wp" --format '{{json .Mounts}}')"
    docker exec "$DB" mysql -uwordpress -p"$DB_PASS" "$database" -NBe \
      "SELECT CONCAT('user_count=',COUNT(*)) FROM wp_users; SELECT CONCAT('post_count=',COUNT(*)) FROM wp_posts; SELECT CONCAT('oembed_count=',COUNT(*)) FROM wp_posts WHERE post_type='oembed_cache'; SELECT CONCAT('local_infile=',@@local_infile);" 2>/dev/null
  } | tee "$out"
}
capture_state vulnerable "$VULN_WP" wordpress "$LOGS/vulnerable_before.log"
capture_state fixed "$FIXED_WP" wordpress_fixed "$LOGS/fixed_before.log"
grep -q '^wordpress_version=7.0.1$' "$LOGS/vulnerable_before.log" || { echo "[INFRA] Vulnerable version mismatch"; exit 2; }
grep -q '^wordpress_version=7.0.2-src$' "$LOGS/fixed_before.log" || { echo "[INFRA] Fixed version mismatch"; exit 2; }
for f in "$LOGS/vulnerable_before.log" "$LOGS/fixed_before.log"; do
  grep -q '^user_count=1$' "$f" || { echo "[INFRA] Initial user-count mismatch"; exit 2; }
  grep -q '^oembed_count=0$' "$f" || { echo "[INFRA] Initial oEmbed-count mismatch"; exit 2; }
  grep -q '^local_infile=0$' "$f" || { echo "[INFRA] local_infile is not disabled"; exit 2; }
done

run_probe() {
  local label="$1" host="$2" mode="$3" out="$4"
  timeout 45 docker exec "$DRIVER" python3 /variant_probe.py "http://$host" "$mode" | tee "$out"
  jq -e '.http_status == 207' "$out" >/dev/null || { echo "[INFRA] $label/$mode did not reach REST batch endpoint"; exit 2; }
}

# Candidate 1 is a data-shape variant: move the scalar author_exclude from the
# shifted request's URL query into its JSON body. Candidate 2 is the original
# query location used as a positive/negative primitive control. Candidate 3
# independently checks malformed-entry alignment without invoking SQL.
run_probe vulnerable "$VULN_WP" body_scalar "$LOGS/vulnerable_body_scalar.json"
run_probe fixed "$FIXED_WP" body_scalar "$LOGS/fixed_body_scalar.json"
run_probe vulnerable "$VULN_WP" query_scalar "$LOGS/vulnerable_query_scalar.json"
run_probe fixed "$FIXED_WP" query_scalar "$LOGS/fixed_query_scalar.json"
run_probe vulnerable "$VULN_WP" batch_alignment "$LOGS/vulnerable_batch_alignment.json"
run_probe fixed "$FIXED_WP" batch_alignment "$LOGS/fixed_batch_alignment.json"

# The unchanged query-scalar primitive must be alive on 7.0.1 and blocked on
# 7.0.2, otherwise this was not a valid side-by-side runtime test.
jq -e '.marker_reflected == true' "$LOGS/vulnerable_query_scalar.json" >/dev/null || { echo "[INFRA] Vulnerable control primitive was not observed"; exit 2; }
jq -e '.marker_reflected == false' "$LOGS/fixed_query_scalar.json" >/dev/null || { echo "[BYPASS] Original query-scalar primitive unexpectedly reached fixed target"; exit 0; }

BODY_VULN="$(jq -r '.marker_reflected' "$LOGS/vulnerable_body_scalar.json")"
BODY_FIXED="$(jq -r '.marker_reflected' "$LOGS/fixed_body_scalar.json")"
QUERY_FIXED="$(jq -r '.marker_reflected' "$LOGS/fixed_query_scalar.json")"

capture_state vulnerable "$VULN_WP" wordpress "$LOGS/vulnerable_after.log"
capture_state fixed "$FIXED_WP" wordpress_fixed "$LOGS/fixed_after.log"
for suffix in vulnerable fixed; do
  before="$LOGS/${suffix}_before.log"
  after="$LOGS/${suffix}_after.log"
  [ "$(grep '^user_count=' "$before")" = "$(grep '^user_count=' "$after")" ] || { echo "[INFRA] Probe changed $suffix user count"; exit 2; }
  [ "$(grep '^post_count=' "$before")" = "$(grep '^post_count=' "$after")" ] || { echo "[INFRA] Probe persistently changed $suffix post count"; exit 2; }
done

{
  echo "candidate_1=body_scalar_alternate_data_path vulnerable_marker=$BODY_VULN fixed_marker=$BODY_FIXED"
  echo "candidate_2=query_scalar_original_sink_control vulnerable_marker=true fixed_marker=$QUERY_FIXED"
  echo "candidate_3=batch_alignment_alternate_error_position inspected_runtime_and_source=true"
  echo "latest_revision=$LATEST_COMMIT guards_retained=true"
  if [ "$BODY_FIXED" = true ] || [ "$QUERY_FIXED" = true ]; then
    echo "verdict=confirmed_bypass"
  else
    echo "verdict=no_distinct_variant_or_bypass"
  fi
} | tee "$LOGS/candidate_matrix.log"

if [ "$BODY_FIXED" = true ] || [ "$QUERY_FIXED" = true ]; then
  echo "[+] BYPASS CONFIRMED on WordPress 7.0.2"
  exit 0
fi

echo "[-] NO DISTINCT VARIANT/BYPASS: 7.0.2 blocks both scalar locations; trunk retains all three guards"
exit 1
