fix(security): fail closed when the snapshot checksum is unavailable - #205
Conversation
7f9732c to
ed5051f
Compare
|
Rebased on top of #204 to resolve the schema-version and The two fixes are integrated rather than merely stacked: the sidecar is fetched unconditionally (fail-closed) and the cleartext-transport warning still fires before it, One test fixture changed as a consequence: #204's transport tests used a fake mirror that served no |
Whether a snapshot was verified at all was decided by pre.HasMD5Sidecar, a flag set from a plaintext HEAD of the .md5sum URL. Anyone on the path could answer that HEAD with a 404 and the tarball was then extracted with expectedMD5 empty, no error, exit 0 - the attacker-controlled response was effectively a 'skip integrity checking' switch, and the CLI reported '(md5 sidecar absent - not verified)' as a success. The sidecar is now fetched unconditionally whenever the operator has not opted out, and a 404, non-200 or transport failure returns a typed VerificationUnavailableError before any tarball byte is requested, so nothing is written. Driven A/B against a binary built from develop: base extracts attacker state with exit 0 on a 404, a 500 and a transport reset; this refuses all three with an empty destination. The MCP snapshot_download tool had no opt-out at all, so it always extracted unverified on a 404; it gains a no_verify arg alongside the CLI's existing --no-verify, which survives the --detach re-exec. All eight mirrors were re-probed and every one returns 200 with a coreutils-format body, so no in-repo caller needs the opt-out. The docs still say sidecars can be absent - corrected to say the download now refuses rather than extracting. Adds one additive optional field to snapshot-download.schema.json, so SchemaVersion goes 1.12.2 -> 1.12.3 with a regenerated baseline.
ed5051f to
1156b1e
Compare
Whether a snapshot was verified at all was decided by an attacker-controllable response. Found by an audit of
developat0c654cd.The problem
Downloadgated verification onpre.HasMD5Sidecar, a flag set from a plaintextHEADof the.md5sumURL. Anyone on the path to a mainnet mirror — all bare-IPhttp://— could answer that HEAD with a 404, and the tarball was then extracted withexpectedMD5empty, no error, exit 0. The CLI reported(md5 sidecar absent — not verified)as a successful download. The attacker's own response was, in effect, a switch that turned integrity checking off.What changed
The sidecar is fetched unconditionally whenever the operator has not opted out. A 404, a non-200, or a transport failure returns a typed
VerificationUnavailableErrorbefore any tarball byte is requested, so nothing is written.Driven A/B against a binary built from
develop:A request recorder confirms the refusal precedes any tarball
GET.The MCP
snapshot_downloadtool had no opt-out at all, so it always extracted unverified on a 404 — it gains ano_verifyarg alongside the CLI's existing--no-verify, which survives the--detachre-exec.Does this break real downloads?
No. All eight mirrors in
SourceTablewere re-probed live and every one returns 200 with a coreutils-format body, so none of the in-repo callers — the recipe,build-nile-fixture.sh, the CI job — needs the opt-out, and none is changed here.The docs that said sidecars are sometimes absent are corrected rather than deleted: they now say what actually happens.
Behaviour changes worth reviewing
VERIFICATION_UNAVAILABLEinstead of succeeding. The mainnet mirrors index a backup directory as soon as it exists, so a run landing between tarball and sidecar publication would now fail — the error's first suggestion is to retry, for exactly that reason.error_codefromDOWNLOAD_ERRORtoVERIFICATION_UNAVAILABLE; exit code stays 1.snapshot-download.schema.json, soSchemaVersiongoes 1.12.2 → 1.12.3.Testing
go test ./... -race -count=1,go vet ./...andgofmtclean on the branch, plus the A/B matrix above and a live probe of all eight mirrors.Ordering
This overlaps #204, which touches the same verification block and also bumps
SchemaVersionto 1.12.3. Suggest merging #204 first, then rebasing this and bumping to 1.12.4 withmake snapshot-schema-baseline. Both are independently correct againstdevelopas it stands.