feat(release): string leak-guard in promote.sh + shell test
parent
e02f4d036a
commit
54518c1f30
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
Loading…
Reference in New Issue