#!/usr/bin/env bash
# tests/creds-probe/run-tests.sh -- harness for scripts/creds-probe.sh (D-137 tier 3
# behavioural). Offline: every case uses `--list` or non-authenticating kinds, so the
# harness itself NEVER authenticates against anything. One case per safety property.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
PROBE="$REPO/scripts/creds-probe.sh"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
P=0; F=0
ok() { echo " [ok] $1"; P=$((P+1)); }
no() { echo "FAIL $1"; F=$((F+1)); }
mkfix() { # mkfix <name> -> dir with a matrix, stages and probes
local d="$TMP/$1"; mkdir -p "$d"
cat > "$d/matrix.tsv" <<'EOF'
alpha singleton vr1-office1 jumphost a.key ssh service consolidated stage2 operator-terminal - -
later singleton vr1-office1 jumphost b.key api service off-manifest-known stage5 operator-terminal - -
tenat per-tenant - jumphost c.key api service off-manifest-known stage2 operator-terminal - -
EOF
printf 'stage2 reached\nstage5 pending\n' > "$d/stages"
echo "$d"
}
run() { # run <dir> <mode> ; sets RC and $OUT
OUT="$(MATRIX="$1/matrix.tsv" STAGES="$1/stages" PROBES="$1/probes" \
bash "$PROBE" "$2" 2>&1)"; RC=$?
}
echo "=== creds-probe harness ==="
# T1 -- --list must not authenticate. The whole point of a dry mode on a credential-using
# tool is that an operator can see the blast radius before accepting it.
D=$(mkfix listonly); printf 'alpha ssh nonexistent.invalid\n' > "$D/probes"
run "$D" --list
[ "$RC" = 0 ] && grep -q 'would probe' <<<"$OUT" && ! grep -q 'FAILED to authenticate' <<<"$OUT" \
&& ok "T1 --list reports intent without authenticating" \
|| { no "T1 --list must not authenticate (rc=$RC)"; sed 's/^/ /' <<<"$OUT"; }
# T2 -- TENANT SURFACE IS REFUSED BY CONSTRUCTION. D-069 and ruling 3 keep tenant custody
# out of this control; probing one means authenticating AS a tenant. A declaration must not
# be able to opt back in.
D=$(mkfix tenant); printf 'tenat ssh somewhere\n' > "$D/probes"
run "$D" --list
[ "$RC" = 1 ] && grep -q 'per-tenant credentials are REFUSED' <<<"$OUT" \
&& ok "T2 a per-tenant probe is REFUSED even when declared (D-069)" \
|| { no "T2 tenant probes must be refused (rc=$RC)"; sed 's/^/ /' <<<"$OUT"; }
# T3 -- a credential whose mint stage is pending is SKIPPED, never reported failed. Most
# OpenStack service credentials are stage5 and would otherwise all read as broken.
D=$(mkfix pending); printf 'later ssh somewhere\n' > "$D/probes"
run "$D" --list
[ "$RC" = 0 ] && grep -q "mint-stage 'stage5' is pending" <<<"$OUT" \
&& ok "T3 a not-yet-minted credential SKIPS, it does not fail" \
|| { no "T3 pending stages must skip (rc=$RC)"; sed 's/^/ /' <<<"$OUT"; }
# T4 -- a probe for an id in no matrix row is a stale declaration, not a silent no-op.
D=$(mkfix stale); printf 'ghost ssh somewhere\n' > "$D/probes"
run "$D" --list
[ "$RC" = 1 ] && grep -q 'in NO matrix row' <<<"$OUT" \
&& ok "T4 a probe for an unknown id -> stale-declaration FAIL" \
|| { no "T4 stale probe declarations must fail (rc=$RC)"; sed 's/^/ /' <<<"$OUT"; }
# T5 -- THE FALSE GREEN THIS TOOL SHIPPED WITH IN DRAFT. `ssh` inside a `while read` loop
# consumes the loop's stdin, so probe 1 ran and probes 2..13 were silently skipped -- and
# the run still exited 0. The accounting check makes a partial run VOID rather than clean.
D=$(mkfix accounting)
printf 'alpha declared-only reason-one\nlater declared-only reason-two\n' > "$D/probes"
run "$D" --run
COUNT=$(grep -cE '^ \[(ok|FAIL|skip)\]' <<<"$OUT")
[ "$COUNT" = 2 ] && ! grep -q 'ACCOUNTING' <<<"$OUT" \
&& ok "T5 every declared probe is reported in exactly one bucket (stdin-eating regression)" \
|| { no "T5 probe accounting (reported=$COUNT)"; sed 's/^/ /' <<<"$OUT"; }
# T6 -- an unknown probe kind must fail loudly rather than being treated as not-probeable.
D=$(mkfix unknown); printf 'alpha telepathy somewhere\n' > "$D/probes"
run "$D" --run
[ "$RC" = 1 ] && grep -q "unknown probe kind" <<<"$OUT" \
&& ok "T6 an unknown probe kind -> FAIL, not a silent skip" \
|| { no "T6 unknown kinds must fail (rc=$RC)"; sed 's/^/ /' <<<"$OUT"; }
# T7 -- NO RETRY. Retry loops are what lock accounts, and the MAAS admin identity backs 19
# automation call sites. The source must contain no retry construct around authentication.
grep -qE '\b(for|while)\b[^;]*\bretry\b|--retry|until .*ssh' "$PROBE" \
&& no "T7 the probe tool gained a retry construct -- one attempt, then report" \
|| ok "T7 no retry construct around authentication"
# T8 -- ssh probes must carry -n (or a stdin redirect); this is the T5 defect's actual fix
# and it is easy to drop when editing the command.
grep -q 'ssh -n ' "$PROBE" \
&& ok "T8 ssh probes run with -n so they cannot eat the loop's stdin" \
|| no "T8 ssh must be invoked with -n"
# T9 -- never echo a secret: the tool reports outcomes, not values.
grep -qE 'echo .*\$(PASS|SECRET|TOKEN|KEY)\b' "$PROBE" \
&& no "T9 the probe tool echoes a credential variable" \
|| ok "T9 no credential value is ever echoed"
echo
if [ "$F" = 0 ]; then echo "creds-probe: $P/$P PASS"; exit 0; fi
echo "creds-probe: FAILURES: $F (passed $P)"; exit 1