Files
gstack/test/redact-prepush-fail-open.sh
T
Lubos BuracinskyandClaude Opus 5 c3c013da72 fix(redact-prepush): fill the slice overlap to its budget, not only when empty
The overlap carried a long line's suffix only when the tail was otherwise
empty. One short line between the long label line and the secret landed in
the tail first, the tail was no longer empty, and the label was dropped
again, so aws.secret_key never fired. The overlap is now filled from the
seam backwards to its exact budget, taking a partial line wherever a whole
one no longer fits; a character is added only if it still fits, so the
slice budget invariant keeps holding.

Gate rows 21 (one short line) and 22 (several) pin it; both fail on the
scanner this branch forks from.

The absent-tip comment claimed the range scans everything reachable. It
scans the net diff of emptyTree..localSha, i.e. the final tree, not each
commit, and now says so, including that a credential added and later
removed inside one push is not in any diff this hook computes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 18:38:55 +02:00

500 lines
20 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# test/redact-prepush-fail-open.sh — regression gate for pre-push credential
# redaction fail-open paths and adjacent range-resolution invariants.
#
# Usage:
# bash test/redact-prepush-fail-open.sh [scanner-path]
#
# Defaults to the in-tree scanner at bin/gstack-redact-prepush.
#
# Every fixture credential below is synthetic: assembled at runtime from
# halves that match nothing on their own. None is, or ever was, a live secret.
set -uo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
SCANNER="${1:-$REPO_ROOT/bin/gstack-redact-prepush}"
command -v bun >/dev/null 2>&1 || { echo "bun not found"; exit 2; }
command -v git >/dev/null 2>&1 || { echo "git not found"; exit 2; }
command -v python3 >/dev/null 2>&1 || { echo "python3 not found"; exit 2; }
[ -f "$SCANNER" ] || { echo "no scanner at $SCANNER"; exit 2; }
_K1='AKIA'; _K2='3QZ7YB2LKWVNPCJD'; KEY="${_K1}${_K2}"
_P1='-----BEGIN '; _P2='RSA PRIVATE KEY'; _P3='-----'
PEM_BEGIN="${_P1}${_P2}${_P3}"; PEM_END="${_P3}END ${_P2}${_P3}"
_S1='wJalrXUtnFEMI/K7MDENG'; _S2='/bPxRfiCYzQ9v2tKzXq'; SEC="${_S1}${_S2}"; export SEC
ZERO40=0000000000000000000000000000000000000000
WORK=$(mktemp -d -t prepush-gate-XXXXXX)
trap 'rm -rf "$WORK"' EXIT
STAGE="$WORK/stage"
mkdir -p "$STAGE/bin"
ln -s "$REPO_ROOT/lib" "$STAGE/lib"
cp "$SCANNER" "$STAGE/bin/cand.ts"
CAND="$STAGE/bin/cand.ts"
WINDOW=$(grep -oE 'const (WINDOW_BYTES|SCAN_CHUNK_BYTES) = [0-9]+ \* 1024' "$CAND" \
| grep -oE '[0-9]+ \* 1024' | head -1 | awk '{print $1*1024}')
: "${WINDOW:=786432}"
R="$WORK/repos"
mkdir -p "$R"
mkrepo() {
local d="$R/$1"
rm -rf "$d"
mkdir -p "$d"
cd "$d" || exit 1
git init -q .
git config user.email t@t.t
git config user.name t
git checkout -q -b probe
}
run_probe() {
cd "$1" || exit 1
local sha
sha=$(git rev-parse HEAD)
printf 'refs/heads/probe %s refs/heads/probe %s\n' "$sha" "${2:-$ZERO40}" \
| bun "$CAND" 2>&1
echo "___EXIT:$?"
}
verdict() {
local out="$1" rc id
rc=$(printf '%s' "$out" | sed -n 's/^___EXIT:\(.*\)$/\1/p' | tail -1)
[ "$rc" = "0" ] && { echo "ALLOW"; return; }
id=$(printf '%s' "$out" | grep -oE 'HIGH [a-z0-9._]+' | head -1 | awk '{print $2}')
if [ -z "$id" ]; then
id=$(printf '%s' "$out" | grep -oE '^ engine\.[a-z_]+' | head -1 | tr -d ' ')
fi
if [ -n "$id" ]; then
echo "BLOCK($id)"
else
echo "BLOCK(unscannable)"
fi
}
TOTAL=0
FAILURES=0
row() {
local name="$1" out="$2" want="$3"
TOTAL=$((TOTAL + 1))
local got
got=$(verdict "$out")
local mark="ok"
if [ "$want" = "BLOCK" ]; then
case "$got" in
BLOCK*) ;;
*) mark="FAIL (want $want)"; FAILURES=$((FAILURES + 1)) ;;
esac
elif [ "$got" != "$want" ]; then
mark="FAIL (want $want)"
FAILURES=$((FAILURES + 1))
fi
printf '%-48s %-26s %-26s %s\n' "$name" "$want" "$got" "$mark"
}
printf '\n%-48s %-26s %-26s %s\n' "SCENARIO" "EXPECTED" "ACTUAL" "GATE"
printf -- '-%.0s' {1..110}; printf '\n'
# 1: 2 MiB innocuous
mkrepo s1; python3 -c "
with open('d.json','w') as f:
f.write('{\n')
for i in range(60000): f.write(' \"prop%07d\": {\"n\": %d},\n' % (i, i*7919%999983))
f.write(' \"end\": 1\n}\n')"
git add -A >/dev/null; git commit -qm big
S1=$(run_probe "$R/s1")
row "1 clean 2 MiB, no credential" "$S1" "ALLOW"
# 2: small + AWS key
mkrepo s2; printf 'cfg = 1\naws_key = "%s"\n' "$KEY" > app.py
git add -A >/dev/null; git commit -qm secret
S2=$(run_probe "$R/s2")
row "2 small + AWS key" "$S2" "BLOCK(aws.access_key)"
# 3: key buried in 3 MiB
mkrepo s3; python3 -c "
k='$KEY'
with open('d.json','w') as f:
f.write('{\n')
for i in range(45000): f.write(' \"prop%07d\": {\"n\": %d},\n' % (i, i*7919%999983))
f.write(' \"aws_key\": \"%s\",\n' % k)
for i in range(45000): f.write(' \"item%07d\": {\"n\": %d},\n' % (i, i*104729%999983))
f.write(' \"end\": 1\n}\n')"
git add -A >/dev/null; git commit -qm buried
S3=$(run_probe "$R/s3")
row "3 AWS key buried in 3 MiB" "$S3" "BLOCK(aws.access_key)"
# 4: PEM straddling the cut
mkrepo s4; PEM_BEGIN="$PEM_BEGIN" PEM_END="$PEM_END" python3 -c "
import base64, os
W=$WINDOW; line='f'*40; n=(W-500)//41
body='\n'.join(base64.b64encode(bytes((i*37+j)%256 for i in range(48))).decode() for j in range(25))
with open('d.txt','w') as f:
for i in range(n): f.write(line+'\n')
f.write(os.environ['PEM_BEGIN']+'\n'+body+'\n'+os.environ['PEM_END']+'\n')
for i in range(2000): f.write('t'*40+'\n')"
git add -A >/dev/null; git commit -qm pem
S4=$(run_probe "$R/s4")
row "4 PEM straddling window cut" "$S4" "BLOCK(pem.private_key)"
# 5: key exactly at the cut
mkrepo s5; python3 -c "
k='$KEY'; W=$WINDOW; line='f'*40; n=(W-60)//41
with open('d.txt','w') as f:
for i in range(n): f.write(line+'\n')
f.write('aws_key = \"%s\"\n' % k)
for i in range(2000): f.write('t'*40+'\n')"
git add -A >/dev/null; git commit -qm atcut
S5=$(run_probe "$R/s5")
row "5 AWS key exactly at window cut" "$S5" "BLOCK(aws.access_key)"
# 6: bogus local sha -> range unreadable -> must fail closed
mkrepo s6; echo hi > a.txt; git add -A >/dev/null; git commit -qm init
run_badlocal() { cd "$1" || exit 1
printf 'refs/heads/probe deadbeefdeadbeefdeadbeefdeadbeefdeadbeef refs/heads/probe 0000000000000000000000000000000000000000\n' \
| bun "$CAND" 2>&1; echo "___EXIT:$?"; }
S6=$(run_badlocal "$R/s6")
row "6 unreadable range (fail-closed)" "$S6" "BLOCK(unscannable)"
# 7: credential on a line whose own content starts with "++"
mkrepo s7; printf 'harmless\n++ aws_key = "%s"\n' "$KEY" > notes.patch
git add -A >/dev/null; git commit -qm plusplus
S7=$(run_probe "$R/s7")
row "7 credential on a '++...' line" "$S7" "BLOCK(aws.access_key)"
# 8: malformed stdin
run_malformed() { cd "$1" || exit 1
printf 'refs/heads/probe\n' | bun "$CAND" 2>&1; echo "___EXIT:$?"; }
mkrepo s8; echo hi > a.txt; git add -A >/dev/null; git commit -qm init
S8=$(run_malformed "$R/s8")
row "8 malformed stdin (fail-closed)" "$S8" "BLOCK(unscannable)"
# 9: gcp key spanning the cut
mkrepo s9; PEM_BEGIN="$PEM_BEGIN" python3 -c "
import os
W=$WINDOW; line='f'*40; n=(W-20)//41
with open('sa.json','w') as f:
for i in range(n): f.write(line+'\n')
f.write('\"private_key\"\n')
f.write(': \"'+os.environ['PEM_BEGIN']+'\n')
for i in range(2000): f.write('t'*40+'\n')"
git add -A >/dev/null; git commit -qm gcpsplit
S9=$(run_probe "$R/s9")
row "9 gcp key spanning the window cut" "$S9" "BLOCK(pem.private_key)"
# 10: diff.external replaces the diff
mkrepo s10; printf 'cfg = 1\naws_key = "%s"\n' "$KEY" > app.py
git add -A >/dev/null; git commit -qm extdiff
git config diff.external /bin/echo
S10=$(run_probe "$R/s10")
row "10 diff.external set + real key" "$S10" "BLOCK(aws.access_key)"
# 11: 2 MiB single minified line carrying a credential
mkrepo s11; KEY="$KEY" python3 -c "
import os
k=os.environ['KEY']
with open('bundle.min.js','w') as f:
f.write('var d={'+','.join('\"k%05d\":%d'%(i,i) for i in range(90000))+',\"aws_key\":\"'+k+'\"};')"
git add -A >/dev/null; git commit -qm minified
S11=$(run_probe "$R/s11")
row "11 credential in a 2 MiB one-liner" "$S11" "BLOCK"
# 12: push to a remote that is NOT origin
run_remote() { cd "$1" || exit 1; local sha; sha=$(git rev-parse HEAD)
printf 'refs/heads/main %s refs/heads/main 0000000000000000000000000000000000000000\n' "$sha" \
| bun "$CAND" publish https://example.invalid/publish.git 2>&1; echo "___EXIT:$?"; }
mkrepo s12; git branch -m main 2>/dev/null || true
printf 'cfg = 1\naws_key = "%s"\n' "$KEY" > app.py
git add -A >/dev/null; git commit -qm seed
git update-ref refs/remotes/origin/main HEAD
git remote add origin https://example.invalid/origin.git
git remote add publish https://example.invalid/publish.git
S12=$(run_remote "$R/s12")
row "12 push to a non-origin remote" "$S12" "BLOCK(aws.access_key)"
# 13: 4 fields, valid local sha, junk REMOTE sha
run_junkremote() { cd "$1" || exit 1; local sha; sha=$(git rev-parse HEAD)
printf 'refs/heads/main %s refs/heads/main not-a-sha\n' "$sha" \
| bun "$CAND" origin https://example.invalid/origin.git 2>&1; echo "___EXIT:$?"; }
S13=$(run_junkremote "$R/s12")
row "13 junk REMOTE sha" "$S13" "BLOCK(unscannable)"
# 14: zero-width padding between proximity label and secret
mkrepo s14; SEC="${_S1}${_S2}" python3 -c "
import os
zw='\u200b'*200000
with open('conf.txt','w') as f:
f.write('aws_secret_access_key =\n')
f.write(zw+'\n')
f.write('\"'+os.environ['SEC']+'\"\n')"
git add -A >/dev/null; git commit -qm zerowidth
S14=$(run_probe "$R/s14")
row "14 zero-width padding at the seam" "$S14" "BLOCK(aws.secret_key)"
# 15: localSha="0" is not a branch delete
run_shortzero() { cd "$1" || exit 1
printf 'refs/heads/probe 0 refs/heads/probe 0000000000000000000000000000000000000000\n' \
| bun "$CAND" origin url 2>&1; echo "___EXIT:$?"; }
S15=$(run_shortzero "$R/s2")
row "15 localSha=\"0\" is not a delete" "$S15" "BLOCK(unscannable)"
# 16: missing-but-shaped remote sha
run_missingremote() { cd "$1" || exit 1; local sha; sha=$(git rev-parse HEAD)
printf 'refs/heads/main %s refs/heads/main cafebabecafebabecafebabecafebabecafebabe\n' "$sha" \
| bun "$CAND" origin https://example.invalid/origin.git 2>&1; echo "___EXIT:$?"; }
S16=$(run_missingremote "$R/s12")
# Now that an absent named tip drops ALL local narrowing, this blocks with the
# actual finding rather than an "unscannable" apology: the whole reachable
# range is scanned and the credential is named.
row "16 missing-but-shaped remote sha" "$S16" "BLOCK(aws.access_key)"
# 17: zero-width across slice boundary
mkrepo s17; SEC="${_S1}${_S2}" python3 -c "
import os
zw='\u200b'*4500
with open('conf.txt','w') as f:
f.write('aws_secret_access_key =\n')
for i in range(70): f.write(zw+'\n')
f.write('\"'+os.environ['SEC']+'\"\n')"
git add -A >/dev/null; git commit -qm zwslice
S17=$(run_probe "$R/s17")
row "17 zero-width across slice boundary" "$S17" "BLOCK(aws.secret_key)"
# 18: proximity split by slice cut
mkrepo s18; SEC="${_S1}${_S2}" python3 -c "
import os
W=$WINDOW; line='-'*40
lab='aws_secret_access_key ='
sec='\"'+os.environ['SEC']+'\"'
n=(W-len(lab)-1)//41
assert 41*n+len(lab)+1+len(sec)+1 > W, 'fixture does not straddle the cut'
with open('conf.txt','w') as f:
for i in range(n): f.write(line+'\n')
f.write(lab+'\n')
f.write(sec+'\n')
for i in range(100): f.write('-'*40+'\n')"
git add -A >/dev/null; git commit -qm proxsplit
S18=$(run_probe "$R/s18")
row "18 proximity split by slice cut" "$S18" "BLOCK(aws.secret_key)"
# 20: an absent named tip whose GUESS is non-empty but wrong.
# Row 16 covers the absent-tip case where the guess scans nothing. This is the
# other half: the stale tracking ref holds the credential, one harmless local
# commit sits on top, so the guessed range is non-empty and credential-free.
# Blocking only the empty guess let this through with exit 0.
mkrepo s20
printf 'aws_access_key_id = %s\n' "$KEY" > secret.txt
git add -A >/dev/null
git commit -qm cred >/dev/null
git update-ref refs/remotes/origin/main HEAD
git remote add origin https://example.invalid/o.git
git remote set-head origin main >/dev/null 2>&1
echo 'harmless = 1' > ok.py
git add -A >/dev/null
git commit -qm harmless >/dev/null
run_absent_tip() {
cd "$1" || exit 1
local sha
sha=$(git rev-parse HEAD)
printf 'refs/heads/main %s refs/heads/main aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa7\n' "$sha" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1
echo "___EXIT:$?"
}
S20=$(run_absent_tip "$R/s20")
row "20 absent named tip, non-empty wrong guess" "$S20" "BLOCK(aws.access_key)"
# E1: ordinary new-branch push must scan ONLY the new commit
mkrepo e1
printf 'aws_key = "%s"\n' "$KEY" > old.py
git add -A >/dev/null; git commit -qm old >/dev/null
git update-ref refs/remotes/origin/main HEAD
git remote add origin https://example.invalid/o.git
git checkout -q -b feature
echo 'harmless = 1' > new.py; git add -A >/dev/null; git commit -qm new >/dev/null
E1=$(cd "$R/e1" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO40" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1; echo "___EXIT:$?")
row "E1 new branch off origin/main stays narrow" "$E1" "ALLOW"
# E2: default branch reachable via origin/HEAD -> trunk
mkrepo e2
printf 'aws_key = "%s"\n' "$KEY" > old.py
git add -A >/dev/null; git commit -qm old >/dev/null
git update-ref refs/remotes/origin/trunk HEAD
git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/trunk
git remote add origin https://example.invalid/o.git
git checkout -q -b feature
echo 'harmless = 1' > new.py; git add -A >/dev/null; git commit -qm new >/dev/null
E2=$(cd "$R/e2" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO40" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1; echo "___EXIT:$?")
row "E2 origin/HEAD -> trunk resolves, stays narrow" "$E2" "ALLOW"
# E3: fetched second remote anchors on its own default branch
mkrepo e3
printf 'aws_key = "%s"\n' "$KEY" > old.py
git add -A >/dev/null; git commit -qm old >/dev/null
git update-ref refs/remotes/publish/main HEAD
git remote add origin https://example.invalid/o.git
git remote add publish https://example.invalid/p.git
git checkout -q -b feature
echo 'harmless = 1' > new.py; git add -A >/dev/null; git commit -qm new >/dev/null
E3=$(cd "$R/e3" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO40" \
| bun "$CAND" publish https://example.invalid/p.git 2>&1; echo "___EXIT:$?")
row "E3 fetched non-origin remote anchors on its own" "$E3" "ALLOW"
# E4: real branch delete stays a skip
mkrepo e4; echo hi > a.txt; git add -A >/dev/null; git commit -qm init >/dev/null
E4=$(cd "$R/e4" && printf 'refs/heads/main %s refs/heads/main %s\n' "$ZERO40" "$(git rev-parse HEAD)" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1; echo "___EXIT:$?")
row "E4 branch delete skipped" "$E4" "ALLOW"
# E5: a URL push must NOT borrow origin's tip as its base.
# The credential is already on origin/main and the local commit adds only
# harmless content. Anchoring on origin (or excluding origin's commits as
# "already pushed") scans the harmless commit only and ships the credential to
# a remote that never had it. A URL target is described by no tracking ref.
mkrepo e5
printf 'aws_key = "%s"\n' "$KEY" > old.py
git add -A >/dev/null; git commit -qm old >/dev/null
git update-ref refs/remotes/origin/main HEAD
git remote add origin https://example.invalid/o.git
git checkout -q -b feature
echo 'harmless = 1' > new.py; git add -A >/dev/null; git commit -qm new >/dev/null
E5=$(cd "$R/e5" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO40" \
| bun "$CAND" https://example.invalid/direct.git '' 2>&1; echo "___EXIT:$?")
row "E5 URL push does not borrow origin's base" "$E5" "BLOCK(aws.access_key)"
# E5b: the other half of E5 — over-scanning must not turn every URL push into a
# block. Same shape, no credential anywhere: the push has to be allowed.
mkrepo e5b
echo 'old = 1' > old.py
git add -A >/dev/null; git commit -qm old >/dev/null
git update-ref refs/remotes/origin/main HEAD
git remote add origin https://example.invalid/o.git
git checkout -q -b feature
echo 'harmless = 1' > new.py; git add -A >/dev/null; git commit -qm new >/dev/null
E5B=$(cd "$R/e5b" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO40" \
| bun "$CAND" https://example.invalid/direct.git '' 2>&1; echo "___EXIT:$?")
row "E5b clean URL push still allowed" "$E5B" "ALLOW"
# 19: a line longer than the overlap must still contribute its END to the seam.
# One ~770 KB ASCII line whose tail is the qualifying label, then a line holding
# the secret. Carrying whole lines only leaves the seam with no context, so
# aws.secret_key never fires even though both halves are in the pushed diff.
mkrepo s19
SEC="$SEC" python3 -c "
import os
sec=os.environ['SEC']
filler='x'*770047
with open('big.txt','w') as f:
f.write(filler+' aws_secret_access_key =\n')
f.write(sec+' '+'y'*20000+'\n')"
git add -A >/dev/null; git commit -qm bigline >/dev/null
S19=$(run_probe "$R/s19")
row "19 label at the end of an over-overlap line" "$S19" "BLOCK(aws.secret_key)"
# 21: same seam, but a SHORT line sits between the long line and the secret.
# Carrying a long line's suffix only when the tail is otherwise empty is not
# enough: the short line lands in the tail first, the tail is no longer empty,
# and the long line's end — where the label is — is dropped again.
mkrepo s21
SEC="$SEC" python3 -c "
import os
sec=os.environ['SEC']
filler='x'*770020
with open('big.txt','w') as f:
f.write(filler+' aws_secret_access_key =\n')
f.write('\n')
f.write(sec+' '+'y'*20000+'\n')"
git add -A >/dev/null; git commit -qm bigline-gap >/dev/null
S21=$(run_probe "$R/s21")
row "21 short line between the long label line and the secret" "$S21" "BLOCK(aws.secret_key)"
# 22: the general form of 19/21. Several short lines between the long label
# line and the secret, still well inside the pattern's proximity window. The
# property being pinned is that the LAST overlap-worth of text before a seam is
# always carried, whatever mix of long and short lines it is made of.
mkrepo s22
SEC="$SEC" python3 -c "
import os
sec=os.environ['SEC']
filler='x'*770000
with open('big.txt','w') as f:
f.write(filler+' aws_secret_access_key =\n')
for _ in range(20): f.write('\n')
f.write(sec+' '+'y'*20000+'\n')"
git add -A >/dev/null; git commit -qm bigline-gaps >/dev/null
S22=$(run_probe "$R/s22")
row "22 several short lines between long label line and secret" "$S22" "BLOCK(aws.secret_key)"
# E6: long-line slicer survives multi-byte text
mkrepo e6; KEY="$KEY" python3 -c "
import os
k=os.environ['KEY']
pad='žřáčě\U0001f600'
with open('b.min.js','w') as f:
f.write('var d=\"'+pad*160000+'\",aws_key=\"'+k+'\";')"
git add -A >/dev/null; git commit -qm mb >/dev/null
E6=$(cd "$R/e6" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO40" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1; echo "___EXIT:$?")
row "E6 long-line slicer survives multi-byte text" "$E6" "BLOCK"
# E7: clean repo, ordinary existing-branch push
mkrepo e7; echo 'a = 1' > a.py; git add -A >/dev/null; git commit -qm one >/dev/null
BASE=$(git rev-parse HEAD)
git update-ref refs/remotes/origin/main "$BASE"
git remote add origin https://example.invalid/o.git
echo 'b = 2' >> a.py; git add -A >/dev/null; git commit -qm two >/dev/null
E7=$(cd "$R/e7" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$BASE" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1; echo "___EXIT:$?")
row "E7 ordinary push of clean content" "$E7" "ALLOW"
# E8: sha256 repo, new branch
if git init -q --object-format=sha256 "$R/.probe256" 2>/dev/null; then
rm -rf "$R/.probe256"
d="$R/e8"; rm -rf "$d"; mkdir -p "$d"; cd "$d" || exit 1
git init -q --object-format=sha256 .
git config user.email t@t.t; git config user.name t; git checkout -q -b main
printf 'cfg = 1\naws_key = "%s"\n' "$KEY" > app.py
git add -A >/dev/null; git commit -qm seed >/dev/null
git remote add origin https://example.invalid/o.git
ZERO64=$(printf '0%.0s' {1..64})
E8=$(cd "$d" && printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO64" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1; echo "___EXIT:$?")
row "E8 sha256 repo, new branch" "$E8" "BLOCK(aws.access_key)"
# E8b: the row that actually proves the empty-tree OID fix. E8 alone does not:
# it carries a credential, so a build that cannot resolve the fallback range
# still "blocks", just with a diff error instead of a finding. A CLEAN sha256
# first push must be ALLOWED — under the hardcoded SHA-1 empty-tree OID it was
# hard-blocked, which is unusable rather than safe.
d="$R/e8b"
rm -rf "$d"
mkdir -p "$d"
cd "$d" || exit 1
git init -q --object-format=sha256 .
git config user.email t@t.t
git config user.name t
git checkout -q -b main
printf 'cfg = 1\nharmless = 2\n' > app.py
git add -A >/dev/null
git commit -qm seed >/dev/null
git remote add origin https://example.invalid/o.git
E8B=$(printf 'refs/heads/main %s refs/heads/main %s\n' "$(git rev-parse HEAD)" "$ZERO64" \
| bun "$CAND" origin https://example.invalid/o.git 2>&1; echo "___EXIT:$?")
row "E8b clean sha256 first push allowed" "$E8B" "ALLOW"
fi
echo
if [ "$FAILURES" -eq 0 ]; then
echo "GATE: PASS ($TOTAL/$TOTAL)"
exit 0
else
echo "GATE: FAIL"
exit 1
fi