feat: land the revision-preflight guard, fixed and wired into contract execution
PR Pipeline — Authorize → Validate → Review → Merge / auth (pull_request) Successful in 6s
PR Pipeline — Authorize → Validate → Review → Merge / validate (pull_request) Successful in 4s
PR Pipeline — Authorize → Validate → Review → Merge / lint (pull_request) Failing after 6s
PR Pipeline — Authorize → Validate → Review → Merge / ai-review (pull_request) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / gate (pull_request) Skipped
PR Pipeline — Authorize → Validate → Review → Merge / auth (pull_request) Successful in 6s
PR Pipeline — Authorize → Validate → Review → Merge / validate (pull_request) Successful in 4s
PR Pipeline — Authorize → Validate → Review → Merge / lint (pull_request) Failing after 6s
PR Pipeline — Authorize → Validate → Review → Merge / ai-review (pull_request) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / gate (pull_request) Skipped
A contract verdict is only meaningful if it came from the merged copy. The fleet has been bitten three times on 2026-09-25 (a clone parked on a merged feature branch while executing from another clone; a script copied into the runner clone by hand; a stale local origin/master making an ancestry check report unlanded work). The control for this existed as an untracked draft and protected nobody, because it was entirely fail-open. Defect in the draft, preserved verbatim as tests/fixtures/revision-preflight.prefix.sh: git -C "$CLONE" show "origin/master:$(basename "$SCRIPT")" basename drops the scripts/ prefix, so for any script under scripts/ it queried the repo root, failed, took the "warn but don't block" branch and exited 0 - passing a script that exists in no revision at all. Reproduced: pre-fix + scripts/demo.sh under scripts/ -> 'could not resolve', EXIT=0 pre-fix + a script in no revision -> EXIT=0 Fixed guard (scripts/revision-preflight.sh): * resolves the repo-relative path inside the clone, so scripts/ paths resolve; * FAILS CLOSED - a path absent from the ref, an unresolvable ref, or a failed fetch is a failure, never a warning; * fetches the remote by default, because a stale local ref would otherwise pass a stale script as current; --no-fetch states the assumption instead of hiding it. Wiring (scripts/contract-run.sh): before executing, the wrapper runs the guard against the clone it lives in. Default CONTRACT_REVISION_PREFLIGHT=enforce withholds the verdict, alerts and exits 2 on mismatch; =warn logs and continues; =off skips. Verified live: match -> contract proceeds and PASSes; mismatch -> 'VERDICT WITHHELD', exit 2; =warn -> continues. Pinning (docs/contract-execution-pinning.md): every contract pins the clone contract-run.sh lives in - the deployed runner being /opt/contract-runner on CT 100. Documented that daily-health-digest has no contract file at all, which is why its execution copy was silently operator-chosen. Tests: tests/test_revision_preflight.sh, 15 assertions over a throwaway clone with a real bare remote. It runs the pre-fix draft against the same cases and shows it passing a ghost script, so the tests provably bite. shellcheck: scripts/revision-preflight.sh and the new test are clean. The three findings remaining in contract-run.sh (SC2086 x2, SC2034) are pre-existing and byte-identical on master.
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env bash
|
||||
# Revision preflight guard: verify the script being executed matches origin/master
|
||||
# Usage: revision-preflight.sh <script-path> <clone-path>
|
||||
# Returns 0 if match, 1 if mismatch (prints both revisions)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT="${1:?Usage: revision-preflight.sh <script-path> <clone-path>}"
|
||||
CLONE="${2:?Usage: revision-preflight.sh <script-path> <clone-path>}"
|
||||
|
||||
# Compute sha256 of the script being executed
|
||||
EXEC_SHA=$(sha256sum "$SCRIPT" | cut -d' ' -f1)
|
||||
|
||||
# Compute sha256 of the merged origin/master version
|
||||
# Extract to a temp file to avoid pipe issues
|
||||
TMPFILE=$(mktemp)
|
||||
trap 'rm -f "$TMPFILE"' EXIT
|
||||
|
||||
# Try to extract the file from origin/master
|
||||
if git -C "$CLONE" show "origin/master:$(basename "$SCRIPT")" > "$TMPFILE" 2>/dev/null; then
|
||||
MASTER_SHA=$(sha256sum "$TMPFILE" | cut -d' ' -f1)
|
||||
else
|
||||
echo "⚠️ revision-preflight: could not resolve origin/master revision for $(basename "$SCRIPT")" >&2
|
||||
exit 0 # Warn but don't block if git show fails
|
||||
fi
|
||||
|
||||
if [[ -z "$MASTER_SHA" || "$MASTER_SHA" == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" ]]; then
|
||||
echo "⚠️ revision-preflight: could not resolve origin/master revision for $(basename "$SCRIPT")" >&2
|
||||
exit 0 # Warn but don't block if git show fails
|
||||
fi
|
||||
|
||||
if [[ "$EXEC_SHA" != "$MASTER_SHA" ]]; then
|
||||
echo "⚠️ revision-preflight: MISMATCH detected" >&2
|
||||
echo " Executed: $EXEC_SHA ($(basename "$SCRIPT"))" >&2
|
||||
echo " Merged: $MASTER_SHA (origin/master:$(basename "$SCRIPT"))" >&2
|
||||
exit 1
|
||||
else
|
||||
echo "✅ revision-preflight: $SCRIPT matches origin/master ($EXEC_SHA)" >&2
|
||||
exit 0
|
||||
fi
|
||||
Executable
+182
@@ -0,0 +1,182 @@
|
||||
#!/usr/bin/env bash
|
||||
# Behavioural tests for scripts/revision-preflight.sh
|
||||
#
|
||||
# Every case builds a throwaway clone with a real bare remote, so origin/master
|
||||
# is genuine and the guard's fetch path is exercised. Nothing outside mktemp is
|
||||
# touched.
|
||||
#
|
||||
# The pre-fix draft is kept at tests/fixtures/revision-preflight.prefix.sh and
|
||||
# is run against the SAME cases, to prove these tests bite: the pre-fix guard
|
||||
# exits 0 where the fixed guard exits 1.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO="$(cd "$HERE/.." && pwd)"
|
||||
GUARD="$REPO/scripts/revision-preflight.sh"
|
||||
PREFIX_GUARD="$HERE/fixtures/revision-preflight.prefix.sh"
|
||||
|
||||
PASS=0
|
||||
FAIL=0
|
||||
FAILED_CASES=()
|
||||
|
||||
pass() { printf ' ✓ %s\n' "$1"; PASS=$((PASS + 1)); }
|
||||
fail() { printf ' ✗ %s\n' "$1"; FAIL=$((FAIL + 1)); FAILED_CASES+=("$1"); }
|
||||
|
||||
# Build a clone with a real remote; echo the clone path.
|
||||
make_clone() {
|
||||
local tmp
|
||||
tmp="$(mktemp -d)"
|
||||
git init --bare -q "$tmp/remote.git"
|
||||
git init -q "$tmp/clone"
|
||||
(
|
||||
cd "$tmp/clone" || exit 1
|
||||
git config user.email test@example.invalid
|
||||
git config user.name test
|
||||
mkdir -p scripts
|
||||
printf '#!/bin/bash\necho hello\n' > scripts/demo.sh
|
||||
chmod +x scripts/demo.sh
|
||||
git add -A
|
||||
git commit -qm init
|
||||
git branch -M master
|
||||
git remote add origin "$tmp/remote.git"
|
||||
git push -q origin master
|
||||
git fetch -q origin
|
||||
)
|
||||
echo "$tmp/clone"
|
||||
}
|
||||
|
||||
echo "== revision-preflight behavioural tests =="
|
||||
|
||||
# ── 1. match → exit 0 ────────────────────────────────────────────────────────
|
||||
echo "1. matching copy"
|
||||
C=$(make_clone)
|
||||
if out=$("$GUARD" "$C/scripts/demo.sh" "$C" 2>&1); then
|
||||
pass "matching copy exits 0"
|
||||
else
|
||||
fail "matching copy should exit 0 (got $?, output: $out)"
|
||||
fi
|
||||
if [[ -z "$( "$GUARD" --quiet "$C/scripts/demo.sh" "$C" 2>&1 )" ]]; then
|
||||
pass "--quiet prints nothing on a match"
|
||||
else
|
||||
fail "--quiet should print nothing on a match"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 2. mismatch → exit 1 and names both hashes ───────────────────────────────
|
||||
echo "2. mismatched copy"
|
||||
C=$(make_clone)
|
||||
printf '#!/bin/bash\necho TAMPERED\n' > "$C/scripts/demo.sh"
|
||||
out=$("$GUARD" "$C/scripts/demo.sh" "$C" 2>&1); rc=$?
|
||||
if [[ $rc -eq 1 ]]; then pass "mismatch exits 1"; else fail "mismatch should exit 1 (got $rc)"; fi
|
||||
if [[ "$out" == *"MISMATCH"* ]]; then pass "mismatch says MISMATCH"; else fail "mismatch should say MISMATCH"; fi
|
||||
if [[ "$out" == *"executed:"* && "$out" == *"merged:"* ]]; then
|
||||
pass "mismatch prints both revisions"
|
||||
else
|
||||
fail "mismatch should print both revisions"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 3. paths under scripts/ resolve (the original basename defect) ───────────
|
||||
echo "3. repo-relative path resolution"
|
||||
C=$(make_clone)
|
||||
if "$GUARD" --quiet "$C/scripts/demo.sh" "$C" >/dev/null 2>&1; then
|
||||
pass "script under scripts/ resolves against origin/master"
|
||||
else
|
||||
fail "script under scripts/ must resolve (basename defect)"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 4. script that exists in NO revision → must fail ─────────────────────────
|
||||
echo "4. untracked script present in no revision"
|
||||
C=$(make_clone)
|
||||
printf '#!/bin/bash\necho never committed\n' > "$C/scripts/ghost.sh"
|
||||
out=$("$GUARD" "$C/scripts/ghost.sh" "$C" 2>&1); rc=$?
|
||||
if [[ $rc -eq 1 ]]; then pass "ghost script exits 1"; else fail "ghost script must exit 1 (got $rc)"; fi
|
||||
if [[ "$out" == *"does not exist in"* ]]; then
|
||||
pass "ghost script says it is absent from the ref"
|
||||
else
|
||||
fail "ghost script should say it is absent from the ref"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 5. unresolvable ref → must fail ──────────────────────────────────────────
|
||||
echo "5. unresolvable ref"
|
||||
C=$(make_clone)
|
||||
out=$("$GUARD" --no-fetch --ref origin/nope "$C/scripts/demo.sh" "$C" 2>&1); rc=$?
|
||||
if [[ $rc -eq 1 ]]; then pass "unresolvable ref exits 1"; else fail "unresolvable ref must exit 1 (got $rc)"; fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 6. missing script → must fail ────────────────────────────────────────────
|
||||
echo "6. missing script"
|
||||
C=$(make_clone)
|
||||
out=$("$GUARD" "$C/scripts/nope.sh" "$C" 2>&1); rc=$?
|
||||
if [[ $rc -eq 1 ]]; then pass "missing script exits 1"; else fail "missing script must exit 1 (got $rc)"; fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 7. script outside the clone → must fail ──────────────────────────────────
|
||||
echo "7. script outside the clone"
|
||||
C=$(make_clone)
|
||||
OUTSIDE=$(mktemp)
|
||||
printf '#!/bin/bash\necho outside\n' > "$OUTSIDE"
|
||||
out=$("$GUARD" "$OUTSIDE" "$C" 2>&1); rc=$?
|
||||
if [[ $rc -eq 1 ]]; then pass "outside script exits 1"; else fail "outside script must exit 1 (got $rc)"; fi
|
||||
rm -f "$OUTSIDE"; rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 8. --no-fetch states the freshness assumption ────────────────────────────
|
||||
echo "8. --no-fetch states its assumption"
|
||||
C=$(make_clone)
|
||||
out=$("$GUARD" --no-fetch "$C/scripts/demo.sh" "$C" 2>&1)
|
||||
if [[ "$out" == *"freshness is assumed"* ]]; then
|
||||
pass "--no-fetch states the freshness assumption"
|
||||
else
|
||||
fail "--no-fetch should state the freshness assumption"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# ── 9. the pre-fix guard must FAIL these same cases (proves the tests bite) ──
|
||||
echo "9. pre-fix draft fails the same cases (bite proof)"
|
||||
if [[ ! -f "$PREFIX_GUARD" ]]; then
|
||||
fail "pre-fix fixture missing: $PREFIX_GUARD"
|
||||
else
|
||||
# 9a. repo-relative path: pre-fix drops scripts/ and cannot resolve
|
||||
C=$(make_clone)
|
||||
out=$("$PREFIX_GUARD" "$C/scripts/demo.sh" "$C" 2>&1); rc=$?
|
||||
if [[ $rc -eq 0 && "$out" == *"could not resolve"* ]]; then
|
||||
pass "pre-fix: exits 0 and cannot resolve scripts/demo.sh (defect confirmed)"
|
||||
else
|
||||
fail "pre-fix should exit 0 with 'could not resolve' (got rc=$rc)"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# 9b. ghost script: pre-fix passes a script that exists in no revision
|
||||
C=$(make_clone)
|
||||
printf '#!/bin/bash\necho never committed\n' > "$C/scripts/ghost.sh"
|
||||
out=$("$PREFIX_GUARD" "$C/scripts/ghost.sh" "$C" 2>&1); rc=$?
|
||||
if [[ $rc -eq 0 ]]; then
|
||||
pass "pre-fix: PASSES a ghost script that exists in no revision (defect confirmed)"
|
||||
else
|
||||
fail "pre-fix was expected to wrongly pass the ghost script (got rc=$rc)"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
|
||||
# 9c. a file that DOES exist at the repo root still works pre-fix, showing
|
||||
# the defect is specific to nested paths
|
||||
C=$(make_clone)
|
||||
printf '#!/bin/bash\necho root\n' > "$C/rootlevel.sh"
|
||||
( cd "$C" && git add rootlevel.sh && git commit -qm root && git push -q origin master && git fetch -q origin )
|
||||
if "$PREFIX_GUARD" "$C/rootlevel.sh" "$C" >/dev/null 2>&1; then
|
||||
pass "pre-fix: root-level path resolves (so the defect is the basename, not git)"
|
||||
else
|
||||
fail "pre-fix should resolve a root-level tracked file"
|
||||
fi
|
||||
rm -rf "$(dirname "$C")"
|
||||
fi
|
||||
|
||||
echo
|
||||
echo " passed: $PASS failed: $FAIL"
|
||||
if [[ $FAIL -gt 0 ]]; then
|
||||
printf ' FAILED: %s\n' "${FAILED_CASES[@]}"
|
||||
exit 1
|
||||
fi
|
||||
echo "All revision-preflight tests passed."
|
||||
Reference in New Issue
Block a user