#!/usr/bin/env bash set -uo pipefail # CASAN Plan-16 SEC-08 (M-06) — pii-mask fails CLOSED. # # Previously a missing rules file, an unreadable file, or a broken rule regex made # pii-mask emit the RAW stdin — silently leaking the very PII a mask rule was meant # to hide. Now any such condition emits NOTHING and exits non-zero. Normal masking # with valid rules is unchanged. # # Deterministic; hermetic; no model/network. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/../scripts/bash/casan-paths.sh" PROJECT_ROOT="$CASAN_APP_ROOT" PM="$CASAN_HARNESS_ROOT/scripts/bash/pii-mask.py" RULES="$CASAN_HARNESS_ROOT/security/pii-rules.yaml" WORK="$(mktemp -d)" trap 'rm -rf "$WORK"' EXIT PASS=0; FAIL=0 pass() { echo "PASS: $1"; PASS=$((PASS + 1)); } fail() { echo "FAIL: $1"; FAIL=$((FAIL + 1)); } echo "===== Plan-16 SEC-08: pii-mask fail-closed =====" # 1) Valid rules still mask (regression). OUT="$(printf 'contact bob@example.com now' | python3 "$PM" "$RULES" 2>/dev/null)" if [[ "$OUT" == *"MASKED"* && "$OUT" != *"bob@example.com"* ]]; then pass "valid rules mask the email (no leak)" else fail "valid masking broken (out='$OUT')" fi # 2) Missing rules file → no output, non-zero, and NO raw email leaks. set +e OUT="$(printf 'secret bob@example.com' | python3 "$PM" "$WORK/nope.yaml" 2>/dev/null)"; RC=$? set -e 2>/dev/null || true if [[ "$RC" -ne 0 && -z "$OUT" ]]; then pass "missing rules file → empty output, non-zero (fail-closed)" else fail "missing rules leaked (rc=$RC out='$OUT')" fi # 3) Broken regex → no output, non-zero (that PII type would otherwise leak). printf -- '- id: email\n type: "email"\n regex: "([unterminated"\n action: mask\n' > "$WORK/bad.yaml" set +e OUT="$(printf 'secret bob@example.com' | python3 "$PM" "$WORK/bad.yaml" 2>/dev/null)"; RC=$? set -e 2>/dev/null || true if [[ "$RC" -ne 0 && -z "$OUT" ]]; then pass "broken rule regex → empty output, non-zero (fail-closed)" else fail "broken regex leaked (rc=$RC out='$OUT')" fi # 4) No rules argument at all → fail-closed. set +e OUT="$(printf 'secret bob@example.com' | python3 "$PM" 2>/dev/null)"; RC=$? set -e 2>/dev/null || true if [[ "$RC" -ne 0 && -z "$OUT" ]]; then pass "no rules argument → empty output, non-zero (fail-closed)" else fail "no-arg leaked (rc=$RC out='$OUT')" fi echo "" echo "===== SEC-08 SUMMARY: PASS=$PASS FAIL=$FAIL =====" [[ "$FAIL" -eq 0 ]] || exit 1