#!/usr/bin/env bash set -uo pipefail # CASAN Plan-16 SEC-30 (X-06) — approval replay prevention (one-time-use nonce). # # A verified approval (offline signature or IdP JWT) is valid for its whole exp # window, so it could be replayed to approve repeatedly. approval-verify now records # a per-token nonce and rejects any repeat (in enforced mode / when a nonce ledger # is configured). Proves first use is accepted, replay is denied, and dev mode # (no ledger) is unchanged. # # Self-contained: ephemeral reviewer key + committed registry. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../scripts/bash/casan-paths.sh" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" S="$CASAN_HARNESS_ROOT/scripts/bash" REG="$CASAN_GOVERNANCE_ROOT/reviewers.registry" WORK="$(mktemp -d)"; RV="$WORK/reviewers"; mkdir -p "$RV" trap 'rm -rf "$WORK"' EXIT openssl genrsa -out "$WORK/sl.priv" 2048 2>/dev/null openssl rsa -in "$WORK/sl.priv" -pubout -out "$RV/security-lead.pub.pem" 2>/dev/null printf 'security.strict' > "$WORK/inp" bash "$S/approval-sign.sh" policy_change alice "$WORK/inp" security-lead "$WORK/sl.priv" "$WORK/a.sig" >/dev/null 2>&1 export CASAN_REVIEWERS_FILE="$REG" CASAN_REVIEWERS_DIR="$RV" 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-30: approval replay prevention =====" # With a nonce ledger configured, the first use is accepted, a replay is denied. [[ "$(rc_of env CASAN_APPROVAL_NONCE_FILE="$WORK/nonces.txt" bash "$S/approval-verify.sh" policy_change alice "$WORK/inp" security-lead "$WORK/a.sig")" -eq 0 ]] \ && pass "first use of a valid approval accepted" || fail "first use rejected" [[ "$(rc_of env CASAN_APPROVAL_NONCE_FILE="$WORK/nonces.txt" bash "$S/approval-verify.sh" policy_change alice "$WORK/inp" security-lead "$WORK/a.sig")" -ne 0 ]] \ && pass "replay of the same approval DENIED (one-time-use)" || fail "replay accepted" # Dev default (no ledger, no prod profile): no replay tracking (backward compatible). R1="$(rc_of bash "$S/approval-verify.sh" policy_change alice "$WORK/inp" security-lead "$WORK/a.sig")" R2="$(rc_of bash "$S/approval-verify.sh" policy_change alice "$WORK/inp" security-lead "$WORK/a.sig")" [[ "$R1" -eq 0 && "$R2" -eq 0 ]] \ && pass "dev mode: no nonce ledger → replay allowed (backward compatible)" \ || fail "dev mode changed (r1=$R1 r2=$R2)" echo "" echo "===== SEC-30 SUMMARY: PASS=$PASS FAIL=$FAIL =====" [[ "$FAIL" -eq 0 ]] || exit 1