From 86431959515b97eec387207a7a4a3103149d30af Mon Sep 17 00:00:00 2001 From: Jakub Zych Date: Sun, 18 Oct 2026 18:05:28 +0200 Subject: [PATCH] chore(19.1): scan sibling goldens for secrets in phase 19.1 gate The docs stage now refuses secret-shaped content (key names, PEM private keys, JWTs) in each sibling's testdata/goldens; --self-test plants each pattern. Closes T-19.1-12. --- scripts/check-phase19.1.sh | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/scripts/check-phase19.1.sh b/scripts/check-phase19.1.sh index 7fbcad48..b750d298 100755 --- a/scripts/check-phase19.1.sh +++ b/scripts/check-phase19.1.sh @@ -10,7 +10,8 @@ # sibling, then go_named required short tests. A skipped, # missing or red required test fails. # docs bash -n plus greps of the three decision-note paths for -# locked module path + version. +# locked module path + version, and a secret scan of each +# sibling's testdata/goldens (T-19.1-12). # postgres Docker host only: named testcontainers suites (HTTP tracers, # TestSendEventRollback, GDPR cascades, LiveKit reconcile). # Without a reachable Docker daemon this stage fails. --all @@ -184,6 +185,19 @@ go_named() { REQUIRE_TESTS="${names[*]}" go_json "$dir" $flags -count=1 -run "$regex" $pkgs } +# SECRET_RE matches secret-shaped golden content: key names, PEM private +# keys and JWTs. The fixtures carry claims only, never a signed token. +SECRET_RE='(private_?key|api_?secret|vapid_private|livekit_api_secret|-----BEGIN [A-Z ]*PRIVATE KEY-----|eyJ[A-Za-z0-9_-]{8,}\.eyJ[A-Za-z0-9_-]{8,}\.)' + +# detect_secrets reads a file or directory. Exit 1 when it holds +# secret-shaped content. +detect_secrets() { + if grep -rniE "$SECRET_RE" "$1" >&2; then + return 1 + fi + return 0 +} + expect_detector() { local detector="$1" name="$2" want="$3" payload="$4" log dc=0 log="$(mktemp)" @@ -218,6 +232,12 @@ run_self_test() { REQUIRE_TESTS="TestPHPMethodMap" expect_detector detect_go subtest-is-not-the-test 5 \ '{"Action":"pass","Package":"p","Test":"TestGoldenEvents/TestPHPMethodMap"} {"Action":"pass","Package":"p","Test":"TestOther"}' + expect_detector detect_secrets clean 0 '{"_source": "HS256 claims only; no API secret.", "sub": "42"}' + expect_detector detect_secrets vapid-key 1 '{"vapid_private_key": "x"}' + expect_detector detect_secrets env-line 1 'VAPID_PRIVATE_KEY=x' + expect_detector detect_secrets livekit-secret 1 '{"api_secret": "devsecret"}' + expect_detector detect_secrets pem 1 '-----BEGIN EC PRIVATE KEY-----' + expect_detector detect_secrets jwt 1 '{"token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiJ9.c2ln"}' grep -qF '"${PHASE19_1_REQUIRE_POSTGRES:-0}" == "1"' "${BASH_SOURCE[0]}" || { echo "refuse: --all does not honour PHASE19_1_REQUIRE_POSTGRES" >&2 @@ -323,6 +343,15 @@ run_docs() { echo "refuse: forum-policy note missing v1.0.27 pin" >&2 return 1 } + local repo + for repo in "$USERFRIENDS" "$FORUM" "$CHATBOTS" "$HERALD" "$CHATVIDEO"; do + require_dir "$repo/testdata/goldens" "goldens" + detect_secrets "$repo/testdata/goldens" || { + echo "refuse: secret-shaped content in $repo/testdata/goldens" >&2 + return 1 + } + done + echo "goldens: no secrets in five sibling fixture trees" echo "phase19.1 docs passed" }