Commit9e4d979dRecorded31 Jul 2026Repositorycore-channel

Verify the seed's inputs instead of its previous output

Message

verify-sources.sh read seed/MANIFEST, which pins the seed TARBALL. So build-seed.sh checked the artifact it was about to overwrite and never verified a single source it was about to compile. The name said verify-sources; the behaviour was verify-the-previous-output. It passed every time, which is why it survived.

It also could not survive a re-pin: once MANIFEST's hash moves ahead of the tarball on disk, a rebuild refuses for a reason that has nothing to do with its sources.

MANIFEST.sources is the input pin, and it is what this now verifies. Added a refusal when the loop verifies zero files, because a loop that checks nothing exits 0 and prints nothing, which reads exactly like a clean run.

Sabotage-tested green -> red -> green: five sources verified, EXIT=0; one byte appended to patch-2.8.tar.xz -> EXIT=1 with expected and actual printed; restored -> EXIT=0.

This commit is separate from the re-pin that follows because it is a pre-existing defect, like the glob fix before it.

Changed
 seed/scripts/verify-sources.sh | 39 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 37 insertions(+), 2 deletions(-)
Diff
seed/scripts/verify-sources.shmodified
@@ -1,9 +1,44 @@
1
#!/bin/sh
+2
# Verify the INPUTS the seed is about to be built from.
+3
#
+4
# This script read `$seed/MANIFEST` until 2026-07-31, and MANIFEST pins the
+5
# seed TARBALL -- the OUTPUT. So `build-seed.sh` checked the artifact it was
+6
# about to overwrite and never checked a single source it was about to compile.
+7
# The name said "verify-sources"; the behaviour was "verify the previous
+8
# output". It passed every time, which is exactly why nobody noticed.
+9
#
+10
# It also could not survive a re-pin: the moment MANIFEST's hash moves ahead of
+11
# the tarball on disk, a rebuild refuses for a reason that has nothing to do
+12
# with its sources.
+13
#
+14
# MANIFEST.sources is the input pin, and it is what this verifies.
15
set -eu
16
seed=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)
+17
manifest="$seed/MANIFEST.sources"
+18
[ -f "$manifest" ] || { echo "verify-sources: no $manifest" >&2; exit 1; }
+19
+20
checked=0
21
while read -r hash url file; do
22
case "$hash" in ''|'#'*) continue ;; esac
+23
[ -f "$seed/$file" ] || {
+24
echo "verify-sources: missing source $file (run bootstrap or gate-crosspath.sh)" >&2
+25
exit 1
+26
}
27
actual=$(sha256sum "$seed/$file" | awk '{print $1}')
7
[ "$actual" = "$hash" ] || { echo "sha256 mismatch: $file" >&2; exit 1; }
+28
[ "$actual" = "$hash" ] || {
+29
echo "verify-sources: sha256 mismatch: $file" >&2
+30
echo " expected $hash" >&2
+31
echo " actual $actual" >&2
+32
exit 1
+33
}
34
echo "verified $file"
9
done < "$seed/MANIFEST"
+35
checked=$((checked+1))
+36
done < "$manifest"
+37
+38
# A loop that verified nothing exits 0 and prints nothing, which reads exactly
+39
# like a clean run. Refuse instead.
+40
[ "$checked" -gt 0 ] || {
+41
echo "verify-sources: REFUSING: $manifest listed no sources to verify" >&2
+42
exit 1
+43
}
+44
echo "verify-sources: $checked source(s) verified against MANIFEST.sources"