#!/bin/bash
set -euo pipefail

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

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

echo "=== CVE-2026-10536 libcurl HTTP/2 stream-dependency UAF repro ==="
echo "ROOT=$ROOT"

CACHE_CTX="$ROOT/project_cache_context.json"
if [ -f "$CACHE_CTX" ] && jq -e '.prepared == true' "$CACHE_CTX" >/dev/null 2>&1; then
  PROJECT_CACHE_DIR="$(jq -r '.project_cache_dir' "$CACHE_CTX")"
else
  PROJECT_CACHE_DIR="$ROOT/artifacts/cache"
fi
REPO_DIR="$PROJECT_CACHE_DIR/repo"
VULN_INSTALL="$PROJECT_CACHE_DIR/install-vuln"
FIXED_INSTALL="$PROJECT_CACHE_DIR/install-fixed"

FIXED_COMMIT="bfbff7852f050232edd3e5ca5c6bf2021c340f5a"
VULN_COMMIT="6e3f8dc1f173b47de9a68516ce4b95bf25598c2f"  # curl-8_20_0

sudo apt-get update >/dev/null
sudo apt-get install -y \
  build-essential autoconf automake libtool pkg-config \
  libssl-dev libnghttp2-dev zlib1g-dev git >/dev/null

mkdir -p "$PROJECT_CACHE_DIR"
if [ ! -d "$REPO_DIR/.git" ]; then
  echo "Cloning curl repository..."
  git clone --depth 2000 https://github.com/curl/curl.git "$REPO_DIR"
fi

cd "$REPO_DIR"

fetch_tag() {
  git -C "$REPO_DIR" fetch --depth 2000 origin "refs/tags/curl-8_20_0:refs/tags/curl-8_20_0" >/dev/null 2>&1 || true
}
fetch_tag

build_version() {
  local role="$1"
  local commit="$2"
  local install_prefix="$3"
  local stamp="$install_prefix/.build_stamp"

  if [ -f "$stamp" ] && [ "$(cat "$stamp")" = "$commit" ] && [ -f "$install_prefix/lib/libcurl.a" ]; then
    echo "[$role] Using cached build at $install_prefix"
    return 0
  fi

  echo "[$role] Building commit $commit ..."
  rm -rf "$install_prefix"
  mkdir -p "$install_prefix"
  git -C "$REPO_DIR" checkout -f "$commit"
  make distclean >/dev/null 2>&1 || true
  autoreconf -fi >/dev/null 2>&1
  ./configure \
    --with-nghttp2 --with-openssl --enable-debug \
    --disable-shared --enable-static \
    --disable-ldap --disable-ldaps \
    --without-libpsl --without-libidn2 \
    --without-zstd --without-brotli \
    CFLAGS="-fsanitize=address -fno-omit-frame-pointer -g" \
    LDFLAGS="-fsanitize=address" \
    --prefix="$install_prefix" >/dev/null
  make -j"$(nproc)" >/dev/null
  make install >/dev/null
  echo "$commit" > "$stamp"
}

build_version "vuln" "$VULN_COMMIT" "$VULN_INSTALL"
build_version "fixed" "$FIXED_COMMIT" "$FIXED_INSTALL"

cat > "$REPRO_DIR/reproducer.c" <<'CEOF'
#include <curl/curl.h>
#include <stdio.h>

int main(void) {
  CURL *parent = curl_easy_init();
  CURL *child  = curl_easy_init();

  if(!parent || !child) {
    fprintf(stderr, "init failed\n");
    return 1;
  }

  curl_easy_setopt(parent, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2_0);
  curl_easy_setopt(child,  CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2_0);

  /* Set stream dependency of child on parent. */
  curl_easy_setopt(child, CURLOPT_STREAM_DEPENDS, parent);

  curl_easy_reset(child);
  curl_easy_cleanup(child);

  curl_easy_cleanup(parent);

  return 0;
}
CEOF

compile_and_run() {
  local role="$1"
  local install="$2"
  local out="$REPRO_DIR/reproducer_${role}"
  local log="$LOGS/reproducer_${role}.log"

  rm -f "$log" "$out"
  echo "[$role] Compiling reproducer..." >> "$log"
  if ! gcc -g -fsanitize=address \
       -I"$install/include" \
       "$REPRO_DIR/reproducer.c" \
       -L"$install/lib" -lcurl -lnghttp2 -lssl -lcrypto -lz \
       -o "$out" >>"$log" 2>&1; then
    echo "[$role] Compilation failed" >> "$log"
    echo "255" > "$ARTIFACTS/reproducer_${role}_exit.txt"
    return 255
  fi

  echo "[$role] Running reproducer..." >> "$log"
  set +e
  LD_LIBRARY_PATH="$install/lib" "$out" >>"$log" 2>&1
  local rc=$?
  set -e
  echo "$rc" > "$ARTIFACTS/reproducer_${role}_exit.txt"
  return $rc
}

echo "=== Running reproducer against vulnerable build ==="
VULN_RC=0
compile_and_run "vuln" "$VULN_INSTALL" || VULN_RC=$?

echo "=== Running reproducer against fixed build ==="
FIXED_RC=0
compile_and_run "fixed" "$FIXED_INSTALL" || FIXED_RC=$?

UAF_FOUND=false
if grep -q "ERROR: AddressSanitizer: heap-use-after-free" "$LOGS/reproducer_vuln.log" 2>/dev/null; then
  UAF_FOUND=true
fi

echo "VULN_RC=$VULN_RC FIXED_RC=$FIXED_RC UAF_FOUND=$UAF_FOUND"

if [ "$UAF_FOUND" = true ] && [ "$VULN_RC" -ne 0 ] && [ "$FIXED_RC" -eq 0 ]; then
  RESULT="confirmed"
  echo "=== RESULT: confirmed (UAF on vulnerable, clean on fixed) ==="
  EXIT_CODE=0
else
  RESULT="not_confirmed"
  echo "=== RESULT: not confirmed ==="
  EXIT_CODE=1
fi

jq -n \
  --arg result "$RESULT" \
  --arg vuln_log "logs/reproducer_vuln.log" \
  --arg fixed_log "logs/reproducer_fixed.log" \
  --arg vuln_commit "$VULN_COMMIT" \
  --arg fixed_commit "$FIXED_COMMIT" \
  --arg reproducer "repro/reproducer.c" \
  --argjson vuln_exit "$VULN_RC" \
  --argjson fixed_exit "$FIXED_RC" \
  --argjson uaf_found "$UAF_FOUND" \
  '{
    entrypoint_kind: "function_call",
    entrypoint_detail: "curl_easy_setopt(CURLOPT_STREAM_DEPENDS), curl_easy_reset, curl_easy_cleanup",
    service_started: false,
    healthcheck_passed: true,
    target_path_reached: true,
    runtime_stack: ["libcurl"],
    proof_artifacts: [
      $vuln_log,
      $fixed_log,
      $reproducer
    ],
    notes: "library_api harness: vulnerable build triggers ASan heap-use-after-free in priority_remove_child; fixed build exits cleanly.",
    result: $result,
    vulnerable_commit: $vuln_commit,
    fixed_commit: $fixed_commit,
    vulnerable_exit_code: $vuln_exit,
    fixed_exit_code: $fixed_exit,
    asan_uaf_found: $uaf_found
  }' > "$REPRO_DIR/runtime_manifest.json"

echo "Wrote $REPRO_DIR/runtime_manifest.json"
exit "$EXIT_CODE"
