#!/usr/bin/env bash # ©AngelaMos | 2026 # exploit-gate.sh set -uo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" VULNERABLE_IMAGE="ruby:4.0.2-slim" PATCHED_IMAGE="ruby:4.0.6-slim" VULNERABLE_TAG="marshalsea-chain:vulnerable" PATCHED_TAG="marshalsea-chain:patched" build_probe_image() { docker build -q \ --build-arg "RUBY_IMAGE=$1" \ -f "${HERE}/target/chain.Dockerfile" \ -t "$2" "${HERE}" >/dev/null } run_probe() { local tag="$1" local label="$2" docker run --rm \ --network none \ --read-only \ --tmpfs /tmp:rw,noexec,nosuid,size=1m \ --user nobody \ -e "MATRIX_IMAGE=${label}" \ -v "${HERE}/lib:/app/lib:ro" \ -v "${HERE}/test/support/exploit_probe.rb:/app/probe.rb:ro" \ -w /app \ "${tag}" ruby -Ilib /app/probe.rb 2>&1 } echo "CVE-2026-41316 exploit gate" echo echo "building probe images" build_probe_image "${VULNERABLE_IMAGE}" "${VULNERABLE_TAG}" || { echo "FAIL could not build the vulnerable probe image" exit 1 } build_probe_image "${PATCHED_IMAGE}" "${PATCHED_TAG}" || { echo "FAIL could not build the patched probe image" exit 1 } echo vulnerable_output="$(run_probe "${VULNERABLE_TAG}" "${VULNERABLE_IMAGE#ruby:}")" vulnerable_status=$? echo "${vulnerable_output}" | sed 's/^/ /' patched_output="$(run_probe "${PATCHED_TAG}" "${PATCHED_IMAGE#ruby:}")" patched_status=$? echo "${patched_output}" | sed 's/^/ /' echo failures=0 expect_line() { local output="$1" key="$2" want="$3" message="$4" if echo "${output}" | grep -qx "${key}=${want}"; then echo " PASS ${message}" else echo " FAIL ${message}" failures=$((failures + 1)) fi } if [[ "${vulnerable_output}" == *"chain=FIRED"* ]]; then echo " PASS the chain executes from Marshal.load alone on the vulnerable image" else echo " FAIL the chain did not execute on the vulnerable image" failures=$((failures + 1)) fi if [[ "${patched_output}" == *"chain=BLOCKED"* ]]; then echo " PASS the patched image blocks the same chain" else echo " FAIL the patched image did not block the chain" failures=$((failures + 1)) fi expect_line "${vulnerable_output}" builder_did_not_execute_its_own_payload true \ "building the payload does not run it in the builder's own process" expect_line "${vulnerable_output}" primitive_is_inert_until_the_application_calls_it true \ "the primitive is inert on load and needs an application call, unlike the chain" expect_line "${vulnerable_output}" chain_carries_no_sink_tag true \ "the chain carries no sink tag, so only the class rules can catch it" if [[ "${vulnerable_status}" -eq 0 && "${patched_status}" -eq 0 ]]; then echo " PASS every observed outcome matched the chain metadata prediction" else echo " FAIL an observed outcome contradicted the chain metadata prediction" failures=$((failures + 1)) fi echo if [[ ${failures} -eq 0 ]]; then echo "GATE PASSED" exit 0 fi echo "GATE FAILED (${failures})" exit 1