#!/usr/bin/env bash set -uo pipefail # CASAN Plan-16 SEC-03 (H-03) — rollback-manager: no `bash -c`, structured argv only. # # The rollback `execute` path used to `bash -c "$rollback_command"` where the # command was read from the unsigned transaction log — arbitrary code execution # for anyone who can append a line. This proves: # * a genuine checkpoint still restores the file (regression), # * a forged free-form record with an RCE payload is REFUSED (not executed), # * a forged structured record whose backup points outside the controlled # backup dir is REFUSED (cannot copy an arbitrary source file). # # Deterministic; no model/app/network. Hermetic tx log via a temp workspace. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" RM="$PROJECT_ROOT/.specify/scripts/bash/rollback-manager.sh" TX_LOG="$PROJECT_ROOT/.specify/logs/level5/rollback-transactions.jsonl" WORK="$(mktemp -d)" # rollback-manager uses a fixed tx-log path; back it up and restore on exit. [[ -f "$TX_LOG" ]] && cp -p "$TX_LOG" "$WORK/tx.bak" restore_tx() { if [[ -f "$WORK/tx.bak" ]]; then cp -p "$WORK/tx.bak" "$TX_LOG"; else rm -f "$TX_LOG"; fi; } trap 'restore_tx; rm -f "$WORK/PWNED"; rm -rf "$WORK"' EXIT PASS=0; FAIL=0 pass() { echo "PASS: $1"; PASS=$((PASS + 1)); } fail() { echo "FAIL: $1"; FAIL=$((FAIL + 1)); } rc_of() { set +e; "$@" >/dev/null 2>&1; echo $?; set -e 2>/dev/null || true; } echo "===== Plan-16 SEC-03: rollback-manager no bash -c (RCE) =====" # 1) Regression: genuine checkpoint -> execute restores exact content. TARGET="$WORK/plan.txt"; echo "ORIGINAL" > "$TARGET" TX="$(bash "$RM" checkpoint "$TARGET" | sed -n 's/.*transaction_id=\([^ ]*\).*/\1/p')" echo "OVERWRITTEN" > "$TARGET" bash "$RM" execute "$TX" >/dev/null 2>&1 [[ "$(cat "$TARGET")" == "ORIGINAL" ]] \ && pass "genuine checkpoint restores exact pre-overwrite content" \ || fail "checkpoint did not restore (got: $(cat "$TARGET"))" # 2) Fail-able: forged free-form record carrying an RCE payload must be REFUSED. rm -f "$WORK/PWNED" python3 - "$TX_LOG" "$WORK/PWNED" <<'PY' import json, sys log, marker = sys.argv[1], sys.argv[2] open(log, "a", encoding="utf-8").write(json.dumps({ "transaction_id": "evil-rce", "action": "checkpoint", "rollback_command": f"touch {marker}"}) + "\n") PY RC="$(rc_of bash "$RM" execute evil-rce)" if [[ "$RC" -ne 0 && ! -f "$WORK/PWNED" ]]; then pass "forged free-form rollback_command REFUSED, no code executed (rc=$RC)" else fail "RCE not prevented (rc=$RC, marker exists=$([[ -f "$WORK/PWNED" ]] && echo yes || echo no))" fi # 3) Fail-able: forged restore_file whose backup is outside the controlled dir. SECRET="$WORK/secret.txt"; echo "SECRET" > "$SECRET" python3 - "$TX_LOG" "$SECRET" "$WORK/stolen.txt" <<'PY' import json, sys log, backup, target = sys.argv[1], sys.argv[2], sys.argv[3] open(log, "a", encoding="utf-8").write(json.dumps({ "transaction_id": "evil-src", "action": "checkpoint", "op": "restore_file", "backup": backup, "target": target}) + "\n") PY RC="$(rc_of bash "$RM" execute evil-src)" if [[ "$RC" -ne 0 && ! -f "$WORK/stolen.txt" ]]; then pass "forged restore source outside backup dir REFUSED (rc=$RC)" else fail "arbitrary-source restore not prevented (rc=$RC)" fi echo "" echo "===== SEC-03 SUMMARY: PASS=$PASS FAIL=$FAIL =====" [[ "$FAIL" -eq 0 ]] || exit 1