Repository navigation
ci(native-arch): verify the Go tarball before running the compiler out of it - #781
Merged
Merged
Conversation
…t of it
Both remote scripts did
( cd "$workdir" && curl -fsSL "https://go.dev/dl/${tarball}" -o "$tarball" && tar -xzf "$tarball" )
and then put that directory on PATH and executed `go` from it. HTTPS and nothing
else: no integrity check between downloading a 64 MB archive and running a
compiler out of it, on the GCC Compile Farm and a LinuxONE host -- machines
shared under an acceptable-use policy, where we are a guest.
Now it downloads, verifies SHA256 against a sum pinned in this file, and only
then extracts.
The sums are pinned HERE rather than fetched alongside the tarball on purpose: a
checksum served from the same origin as the file it describes cannot attest to
that file. Fetching go.dev's JSON index at run time would protect against a
truncated download and nothing else, which is the weaker thing wearing the
stronger thing's clothes.
The cost is that bumping GO_VERSION means updating three lines. The `*)` arm is
what makes that cost visible instead of silent: an unpinned version or
architecture exits 1 naming what to add, rather than skipping the check. A guard
that quietly does nothing when it does not recognise its input is the failure
mode worth designing against here.
Sums taken from https://go.dev/dl/?mode=json for go1.26.4, and cross-checked
programmatically against that index after being written into the file, so a
transcription slip cannot survive:
ppc64le 53f49b8c...bc22e
riscv64 2b9ba137...40555
s390x 1e12a231...b3ca1
The compare logic was exercised locally in all four states before pushing --
matching sum passes; a mismatch exits 1 printing both values; an unpinned
GOVER and an unpinned GOARCH each exit 1 with the "refusing to run an
unverified toolchain" message. The local run used `shasum -a 256` since this is
a mac; the workflow uses `sha256sum`, which is what the Linux hosts have.
Applied at both sites. The cfarm job covers ppc64le and riscv64, the s390x job
covers s390x, and they are near-duplicate blocks 100 lines apart -- fixing one
would have left the other running an unverified toolchain.
Deferred from #778, which fixed the other defect in these same two blocks.
Found in the build/CI audit for #768.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Deferred from #778, which fixed the other defect in these same two blocks. Found in the build/CI audit for #768.
What was there
Both remote scripts in
native-arch.ymldid, in one subshell:and then put that directory on
PATH, setGOROOTto it, and executedgoout of it. HTTPS and nothing else — no integrity check at all between downloading a 64 MB archive and running a compiler from it.Where it runs is part of the finding: the GCC Compile Farm (
cfarm433,cfarm95) and a LinuxONE host. Shared machines, under an acceptable-use policy, where we are a guest.What it is now
Download → verify → extract, with the sum pinned in this file.
Two choices worth arguing for explicitly
The sums are pinned here, not fetched at run time. Fetching go.dev's JSON index alongside the tarball would have been self-updating and needed no maintenance — and would protect against a truncated download and nothing else. A checksum served from the same origin as the file it describes cannot attest to that file. That is the weaker thing wearing the stronger thing's clothes, so it is not what this does.
The
*)arm exits 1. The cost of pinning is that bumpingGO_VERSIONmeans updating three lines, and that arm is what makes the cost visible rather than silent. A guard that quietly does nothing when it does not recognise its input is precisely the failure mode worth designing against — this repository already has one of those in #778, where|| trueturned a checkout failure into a green tick.Verification
The sums come from
https://go.dev/dl/?mode=jsonforgo1.26.4, and were cross-checked programmatically against that index after being written into the file, so a transcription slip cannot survive:The compare logic was exercised locally in all four states, because it will next run on hosts I cannot watch:
== fake.tar.gz sha256 verified, exit 0== SHA256 mismatch, both values printed, exit 1GOVER(1.27.1)refusing to run an unverified toolchain, exit 1GOARCH(loong64)refusing to run an unverified toolchain, exit 1The two failing cases matter as much as the passing one: a verification step that cannot be made to fail has not been tested. The local run used
shasum -a 256because this is a mac; the workflow usessha256sum, which is what the Linux hosts have.native-arch.ymlstill parses (jobs: guard, native-cfarm, native-s390x;GO_VERSION: 1.26.4unchanged).Applied at both sites
The cfarm job covers
ppc64le/riscv64and the s390x job coverss390x; they are near-duplicate blocks 100 lines apart. Fixing one would have left the other running an unverified toolchain — the same half-fix trap as #778.This lane is
schedule+workflow_dispatchonly, so this PR's own checks cannot exercise it; that is why the logic was tested standalone rather than left to be reviewed by reading.🤖 Generated with Claude Code