feat(release): string leak-guard in promote.sh + shell test

feat/v1-foundation
boban 2026-07-03 19:49:31 +02:00
parent e02f4d036a
commit 54518c1f30
2 changed files with 53 additions and 0 deletions

View File

@ -40,6 +40,20 @@ for f in CLAUDE.md rules.md handoff.md kickoff-prompt.md docs; do
fi
done
# String leak-guard: refuse to publish a tree that mentions the private upstream by name. The
# patterns are the always-forbidden literals plus any passed in CLUSEV_PROMOTE_FORBIDDEN (the
# private host/slug — supplied from a CI secret so they never live in a tracked file). This script
# is itself export-ignored, so grepping the assembled tree never trips on the guard's own source.
forbidden=$'gitea\n.env.gitea'
[ -n "${CLUSEV_PROMOTE_FORBIDDEN:-}" ] && forbidden+=$'\n'"${CLUSEV_PROMOTE_FORBIDDEN// /$'\n'}"
while IFS= read -r pat; do
[ -n "$pat" ] || continue
if grep -rIiF --exclude-dir=.git -- "$pat" "$pub" >/dev/null 2>&1; then
echo "leak-guard: REFUSING to publish $tag — forbidden pattern present in the public tree." >&2
exit 1
fi
done <<< "$forbidden"
git -C "$pub" add -A
# --allow-empty: stable promotion re-tags the same source commit as the last beta, so the
# extracted tree is byte-identical and there is nothing to stage — still produce a release commit.

View File

@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Verifies scripts/promote.sh: (a) export-ignored dev-release paths never reach the public tree,
# (b) a planted forbidden string is caught by the leak-guard. Self-contained: builds throwaway
# src/pub git repos in a temp dir. Run from the repo root: bash tests/scripts/promote-guard.test.sh
set -euo pipefail
cd "$(dirname "$0")/../.."
ROOT="$(pwd)"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
# A minimal "src" repo whose .gitattributes mirrors ours (so export-ignore is honoured), containing
# one public file, one export-ignored dev-release file, and the CHANGELOG.
mk_src() {
git init -q "$TMP/src"; git -C "$TMP/src" config user.email t@t; git -C "$TMP/src" config user.name t
cp "$ROOT/.gitattributes" "$TMP/src/.gitattributes"
mkdir -p "$TMP/src/app/Livewire/Release" "$TMP/src/docker/release"
echo 'public content' > "$TMP/src/README.md"
echo 'DEV-RELEASE-BRIDGE' > "$TMP/src/app/Livewire/Release/Index.php"
echo '# changelog' > "$TMP/src/CHANGELOG.md"
git -C "$TMP/src" add -A; git -C "$TMP/src" commit -qm init; git -C "$TMP/src" tag v1.0.0
}
mk_pub() { git init -q "$TMP/pub"; git -C "$TMP/pub" config user.email t@t; git -C "$TMP/pub" config user.name t
git -C "$TMP/pub" commit -q --allow-empty -m base; }
# (a) Clean run: dev-release path dropped, promote succeeds.
mk_src; mk_pub
bash "$ROOT/scripts/promote.sh" "$TMP/src" v1.0.0 "$TMP/pub" "Release v1.0.0"
[ ! -e "$TMP/pub/app/Livewire/Release/Index.php" ] || { echo "FAIL: dev-release path leaked"; exit 1; }
[ -e "$TMP/pub/README.md" ] || { echo "FAIL: public file missing"; exit 1; }
echo "PASS: clean promote drops the dev-release path"
# (b) Planted forbidden string in a PUBLIC file → guard must refuse.
rm -rf "$TMP/src" "$TMP/pub"; mk_src; mk_pub
echo 'clone from git.example-forbidden.test' > "$TMP/src/README.md"
git -C "$TMP/src" commit -qam plant; git -C "$TMP/src" tag -f v1.0.0
if CLUSEV_PROMOTE_FORBIDDEN='example-forbidden.test' bash "$ROOT/scripts/promote.sh" "$TMP/src" v1.0.0 "$TMP/pub" "Release v1.0.0" 2>/dev/null; then
echo "FAIL: guard did not catch the planted forbidden string"; exit 1
fi
echo "PASS: guard refuses a planted forbidden string"
echo "ALL PASS"