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.
This commit is contained in:
Jakub Zych
2026-10-18 18:05:28 +02:00
parent 2785bebd32
commit 8643195951

View File

@@ -10,7 +10,8 @@
# sibling, then go_named required short tests. A skipped, # sibling, then go_named required short tests. A skipped,
# missing or red required test fails. # missing or red required test fails.
# docs bash -n plus greps of the three decision-note paths for # 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, # postgres Docker host only: named testcontainers suites (HTTP tracers,
# TestSendEventRollback, GDPR cascades, LiveKit reconcile). # TestSendEventRollback, GDPR cascades, LiveKit reconcile).
# Without a reachable Docker daemon this stage fails. --all # 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 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() { expect_detector() {
local detector="$1" name="$2" want="$3" payload="$4" log dc=0 local detector="$1" name="$2" want="$3" payload="$4" log dc=0
log="$(mktemp)" log="$(mktemp)"
@@ -218,6 +232,12 @@ run_self_test() {
REQUIRE_TESTS="TestPHPMethodMap" expect_detector detect_go subtest-is-not-the-test 5 \ 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":"TestGoldenEvents/TestPHPMethodMap"}
{"Action":"pass","Package":"p","Test":"TestOther"}' {"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]}" || { grep -qF '"${PHASE19_1_REQUIRE_POSTGRES:-0}" == "1"' "${BASH_SOURCE[0]}" || {
echo "refuse: --all does not honour PHASE19_1_REQUIRE_POSTGRES" >&2 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 echo "refuse: forum-policy note missing v1.0.27 pin" >&2
return 1 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" echo "phase19.1 docs passed"
} }