verification: pin the harness, audit drivers and policy files (P1-c)
This repository has the estate's strongest gates, which makes them the most
valuable to switch off. Until now every one of them was executed by scripts
that nothing pinned.
Phase 0c requires every harness file to match HARNESS.sha256 — 15 files:
check.sh, lean-guard, inventory_gate.sh, run_bare.sh, all three self-tests,
both audit drivers (Proofs/Inventory.lean, Proofs/AxiomCheck.lean), the policy
tables (inventory-allowlist.txt, AUDIT-MANIFEST.txt), the toolchain pin, the
fidelity harness and its Python transcription, and the extracted model.
WHICH files must be pinned is policy and lives in check.sh, never in the map
being consulted: the required set is derived from the filesystem (the
executable bit, plus gen/**.lean, plus an explicit list for the rest), so
deleting a pin entry is a set mismatch rather than a silent un-pinning.
gen/LTLAcc/HashExternal.lean was previously bound by nothing at all — it was
compiled and trusted. It is now pinned, and the derivation is by set, so a new
model file fails closed.
selftest_audit.sh case 9 is split rather than relabelled. Phase 0c now catches
an unpinned rogue gen module BEFORE the dead-file gate runs, so asserting only
the new diagnostic would have quietly retired the dead-file gate from the test
suite. 9a asserts the harness-set mismatch on the unpinned file; 9b pins it —
an author who added it deliberately — and asserts Phase 2 still dies with DEAD
FILE (gen). Ten cases now, all defeated.
KNOWN-GAPS and the trusted base record the circularity plainly: an author who
edits a script and refreshes its pin in one commit passes every phase. The pin
removes the silent path, not the possibility. Review at the pinned commit
remains the consumer's protection.
Verified green after the fix: button (75s), harness self-test, binding
self-test, and the ten-case audit self-test. ATTESTATION GREEN (Lean +
fidelity), all fidelity case counts identical to the pre-change run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 18:13:21 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
# selftest-harness.sh — adversarial self-test for check.sh Phase 0c.
|
|
|
|
|
#
|
|
|
|
|
# Phase 0c pins the scripts and policy files the button itself runs on. The
|
|
|
|
|
# attack it exists to stop is the cheapest one in the estate: don't touch the
|
|
|
|
|
# proofs at all, edit the checker. Round-5 review of the companion SLH-DSA
|
|
|
|
|
# repository stubbed the compiler wrapper alone and got ALL GREEN in 3.6
|
|
|
|
|
# seconds over deliberately destroyed proofs.
|
|
|
|
|
#
|
|
|
|
|
# Cases, each asserting a SPECIFIC diagnostic:
|
|
|
|
|
# 0 positive control: untouched tree passes
|
|
|
|
|
# 1 a pinned harness file edited by one byte → does not match its pin
|
|
|
|
|
# 2 a NEW executable appears, unpinned → set mismatch
|
|
|
|
|
# 3 an entry DELETED from HARNESS.sha256 → set mismatch, NOT a
|
|
|
|
|
# silent un-pin (this is the shape of the defect SLH-DSA round-6 found:
|
|
|
|
|
# dropping a key un-pinned two files with no diagnostic at all)
|
|
|
|
|
# 4 HARNESS.sha256 itself removed → fail-closed
|
|
|
|
|
#
|
|
|
|
|
# Phase 0c is lifted out of check.sh at run time, so the tested logic IS the
|
|
|
|
|
# shipping logic. Cheap: no Lean, runs in about a second.
|
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
|
set -uo pipefail
|
|
|
|
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
|
|
|
|
FAILURES=0
|
|
|
|
|
STASH="$(mktemp -d)"
|
|
|
|
|
NEWEXE="$HERE/zz-selftest-helper.sh"
|
|
|
|
|
|
|
|
|
|
cleanup() {
|
|
|
|
|
[ -f "$STASH/HARNESS.sha256" ] && cp "$STASH/HARNESS.sha256" "$HERE/HARNESS.sha256"
|
|
|
|
|
[ -f "$STASH/victim" ] && cp "$STASH/victim" "$HERE/$VICTIM"
|
|
|
|
|
rm -f "$NEWEXE"
|
|
|
|
|
rm -rf "$STASH"
|
|
|
|
|
}
|
|
|
|
|
trap cleanup EXIT INT TERM
|
|
|
|
|
|
|
|
|
|
cp "$HERE/HARNESS.sha256" "$STASH/HARNESS.sha256"
|
|
|
|
|
|
|
|
|
|
# Lift Phase 0c. The two repo families end the phase differently, so accept
|
|
|
|
|
# either terminator rather than hardcoding one and silently lifting nothing.
|
|
|
|
|
DRIVER="$STASH/phase0c.sh"
|
|
|
|
|
{
|
verification: lifted phases run under the buttons shell options, enforced in lift-guard
A lift is evidence about the shipping gate only if it executes the way the
shipping gate executes. Every button here runs `set -euo pipefail`. Eighteen
lift sites prefixed their driver with `set -uo pipefail` and no -e — four per
fork (selftest-harness, selftest-scalar-statements, selftest-statements twice)
and both accumulator lifts — while sixteen other sites got it right. The estate
did it both ways, which is what made this a defect and not a convention.
Without -e a failing command does not abort: execution continues and the driver
returns the LAST command status. A lifted phase can therefore reach a verdict
the shipping phase would never reach while the self-test reports the gate
"works". selftest_statements.sh:27 claims the tested logic IS the shipping
logic; in this respect it was not.
NO VERDICT CHANGED. Every self-test that passed before passes now, at the same
timings, so -e was not masking a false green at these commits. The defect was
fidelity and the claim is no larger than that.
The durable part is not the 18 edits. lift-guard.sh already inspects every
driver, so it now REFUSES any lift whose driver does not enable errexit and
prints the drivers actual shell options. Negative-tested four ways: no -e
rejects, `set -euo` passes, bare `set -e` passes, no set line at all rejects.
Byte-identical across the four forks. The nineteenth lift someone writes fails
closed rather than silently testing a more permissive shell.
Certified by the round-15 sweep: 50/50 GREEN, six repositories, both buttons
and every self-test, exit 0.
Registered and NOT fixed here: ltl-accumulator-verified has no lift-guard.sh
at all and calls it zero times, so its two lifts remain unguarded — the one
place this class can still recur silently.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 10:58:29 +00:00
|
|
|
echo 'set -euo pipefail' # -e matches the button; see lift-drivers-drop-errexit
|
verification: pin the harness, audit drivers and policy files (P1-c)
This repository has the estate's strongest gates, which makes them the most
valuable to switch off. Until now every one of them was executed by scripts
that nothing pinned.
Phase 0c requires every harness file to match HARNESS.sha256 — 15 files:
check.sh, lean-guard, inventory_gate.sh, run_bare.sh, all three self-tests,
both audit drivers (Proofs/Inventory.lean, Proofs/AxiomCheck.lean), the policy
tables (inventory-allowlist.txt, AUDIT-MANIFEST.txt), the toolchain pin, the
fidelity harness and its Python transcription, and the extracted model.
WHICH files must be pinned is policy and lives in check.sh, never in the map
being consulted: the required set is derived from the filesystem (the
executable bit, plus gen/**.lean, plus an explicit list for the rest), so
deleting a pin entry is a set mismatch rather than a silent un-pinning.
gen/LTLAcc/HashExternal.lean was previously bound by nothing at all — it was
compiled and trusted. It is now pinned, and the derivation is by set, so a new
model file fails closed.
selftest_audit.sh case 9 is split rather than relabelled. Phase 0c now catches
an unpinned rogue gen module BEFORE the dead-file gate runs, so asserting only
the new diagnostic would have quietly retired the dead-file gate from the test
suite. 9a asserts the harness-set mismatch on the unpinned file; 9b pins it —
an author who added it deliberately — and asserts Phase 2 still dies with DEAD
FILE (gen). Ten cases now, all defeated.
KNOWN-GAPS and the trusted base record the circularity plainly: an author who
edits a script and refreshes its pin in one commit passes every phase. The pin
removes the silent path, not the possibility. Review at the pinned commit
remains the consumer's protection.
Verified green after the fix: button (75s), harness self-test, binding
self-test, and the ten-case audit self-test. ATTESTATION GREEN (Lean +
fidelity), all fidelity case counts identical to the pre-change run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 18:13:21 +00:00
|
|
|
echo "HERE=\"$HERE\""
|
|
|
|
|
awk '/^# ── Phase 0c/{f=1} f{print} /^# ── Phase 1|^echo "=== Phase 1/{if(f && !/Phase 0c/) exit}' "$HERE/check.sh" \
|
|
|
|
|
| sed '/^# ── Phase 1/d; /^echo "=== Phase 1/d'
|
|
|
|
|
} > "$DRIVER"
|
|
|
|
|
if [ "$(grep -c . "$DRIVER")" -lt 20 ]; then
|
|
|
|
|
echo "FATAL: could not lift Phase 0c out of check.sh — the phase markers moved."
|
|
|
|
|
echo "This self-test must attack the shipping gate; refusing to run against nothing."
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
expect() { # expect <label> <want-rc> <want-substring>
|
|
|
|
|
local label="$1" want_rc="$2" want_txt="$3" out rc
|
|
|
|
|
out=$(bash "$DRIVER" 2>&1); rc=$?
|
|
|
|
|
if [ "$rc" -ne "$want_rc" ]; then
|
|
|
|
|
echo " ✗ $label: exit $rc, expected $want_rc"; echo "$out" | sed 's/^/ /'
|
|
|
|
|
FAILURES=$((FAILURES+1)); return
|
|
|
|
|
fi
|
|
|
|
|
if ! grep -qF "$want_txt" <<<"$out"; then
|
|
|
|
|
echo " ✗ $label: exit code right, diagnostic wrong (rejected for the wrong reason)"
|
|
|
|
|
echo " wanted: $want_txt"; echo "$out" | sed 's/^/ /'
|
|
|
|
|
FAILURES=$((FAILURES+1)); return
|
|
|
|
|
fi
|
|
|
|
|
echo " ✓ $label"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
echo "=== selftest-harness: attacking check.sh Phase 0c ==="
|
|
|
|
|
|
|
|
|
|
# ── 0. positive control ────────────────────────────────────────────────────
|
|
|
|
|
expect "case 0 control: untouched harness passes" 0 "match their pins"
|
|
|
|
|
|
|
|
|
|
# ── 1. edit a pinned file. lean-guard is the pointed choice: it is the memory
|
|
|
|
|
# cap protecting this machine, and stubbing it is the demonstrated
|
|
|
|
|
# 3.6-second path to a false green.
|
|
|
|
|
VICTIM=lean-guard
|
|
|
|
|
cp "$HERE/$VICTIM" "$STASH/victim"
|
|
|
|
|
printf '\n# selftest\n' >> "$HERE/$VICTIM"
|
|
|
|
|
expect "case 1: edited lean-guard caught" 1 "does not match its pin"
|
|
|
|
|
cp "$STASH/victim" "$HERE/$VICTIM"
|
|
|
|
|
|
|
|
|
|
# ── 2. a new executable the button could shell out to ──────────────────────
|
|
|
|
|
printf '#!/bin/sh\necho "unpinned"\n' > "$NEWEXE"; chmod +x "$NEWEXE"
|
|
|
|
|
expect "case 2: new unpinned executable caught" 1 "does not match HARNESS.sha256"
|
|
|
|
|
rm -f "$NEWEXE"
|
|
|
|
|
|
|
|
|
|
# ── 3. delete a pin entry: the set must be derived from the filesystem, not
|
|
|
|
|
# read out of the map being consulted, or this is a silent un-pin.
|
|
|
|
|
grep -v " ${VICTIM}\$" "$STASH/HARNESS.sha256" > "$HERE/HARNESS.sha256"
|
|
|
|
|
expect "case 3: deleted pin entry is a failure, not a silent un-pin" 1 "does not match HARNESS.sha256"
|
|
|
|
|
cp "$STASH/HARNESS.sha256" "$HERE/HARNESS.sha256"
|
|
|
|
|
|
|
|
|
|
# ── 4. absence must not pass for cleanliness ───────────────────────────────
|
|
|
|
|
rm -f "$HERE/HARNESS.sha256"
|
|
|
|
|
expect "case 4: missing pin file is fail-closed" 1 "the harness is unpinned"
|
|
|
|
|
cp "$STASH/HARNESS.sha256" "$HERE/HARNESS.sha256"
|
|
|
|
|
|
|
|
|
|
# ── 5. restored ────────────────────────────────────────────────────────────
|
|
|
|
|
expect "case 5: restored to green" 0 "match their pins"
|
|
|
|
|
|
|
|
|
|
echo ""
|
|
|
|
|
if [ "$FAILURES" -eq 0 ]; then
|
|
|
|
|
echo "SELFTEST PASSED — Phase 0c rejects harness tampering for the stated reason."
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
echo "SELFTEST FAILED: $FAILURES case(s) did not behave as claimed."
|
|
|
|
|
exit 1
|