diff --git a/Dockerfile.universal b/Dockerfile.universal index c9f71c603a..95e36ae71a 100644 --- a/Dockerfile.universal +++ b/Dockerfile.universal @@ -29,7 +29,7 @@ USER root # or run `npx playwright@ install-deps --dry-run` to diff. (Tracked # only via this comment, not an ARG, since the apt package names are hardcoded # below and a build-arg override would silently disagree with what's installed.) -ARG GO_VERSION=1.26.0 +ARG GO_VERSION=1.26.3 ARG GOLANGCI_LINT_VERSION=1.62.0 ARG PNPM_VERSION=9.15.9 @@ -78,11 +78,12 @@ RUN set -eux; \ # Go toolchain - tarball install to /usr/local/go (the standard layout the # go.dev installer creates). Add to PATH below. # -# Integrity: go.dev publishes a per-tarball `.sha256` file (a bare hex hash, -# no filename), fetched alongside the tarball and verified before extraction. -# This is TOFU on first fetch; if the go.dev CDN itself is compromised both -# files are bad. Strongest available option without bumping the hash inline -# every Go release. +# Integrity: go.dev's `?mode=json&include=all` endpoint lists every released +# version with a per-file `sha256` field. We fetch that index, pick the SHA +# for our tarball, and verify before extraction. (The `.sha256` sidecar URLs +# that look like they should work redirect to the HTML downloads page, so +# they cannot be used here.) This is TOFU on first fetch; if go.dev itself +# is compromised both the JSON and the tarball are bad. RUN set -eux; \ arch="$(dpkg --print-architecture)"; \ case "$arch" in \ @@ -90,11 +91,18 @@ RUN set -eux; \ arm64) goarch=arm64 ;; \ *) echo "unsupported arch: $arch" >&2; exit 1 ;; \ esac; \ - curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${goarch}.tar.gz" -o /tmp/go.tar.gz; \ - curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${goarch}.tar.gz.sha256" -o /tmp/go.tar.gz.sha256; \ - echo "$(cat /tmp/go.tar.gz.sha256) /tmp/go.tar.gz" | sha256sum -c -; \ + tarball="go${GO_VERSION}.linux-${goarch}.tar.gz"; \ + expected="$(curl -fsSL 'https://go.dev/dl/?mode=json&include=all' \ + | jq -r --arg v "go${GO_VERSION}" --arg f "${tarball}" \ + 'first(.[] | select(.version == $v) | .files[] | select(.filename == $f) | .sha256)')"; \ + if [ -z "$expected" ]; then \ + echo "no upstream sha256 for ${tarball} at version ${GO_VERSION}" >&2; \ + exit 1; \ + fi; \ + curl -fsSL "https://go.dev/dl/${tarball}" -o /tmp/go.tar.gz; \ + echo "${expected} /tmp/go.tar.gz" | sha256sum -c -; \ tar -C /usr/local -xzf /tmp/go.tar.gz; \ - rm /tmp/go.tar.gz /tmp/go.tar.gz.sha256 + rm /tmp/go.tar.gz # golangci-lint - prebuilt binary, no compilation needed. # Integrity: each release publishes a single `*-checksums.txt` file with @@ -131,7 +139,10 @@ RUN set -eux; \ # # Integrity: download rustup-init for our arch and verify its SHA-256 # against the upstream .sha256 sidecar before executing - matches the -# pattern used for Go/golangci-lint/yq above, rather than `curl | sh`. +# pattern used for golangci-lint/yq above, rather than `curl | sh`. +# (Go uses the JSON release index instead - go.dev doesn't actually +# publish per-tarball .sha256 sidecars despite the URL pattern looking +# like it would work.) ENV RUSTUP_HOME=/usr/local/rustup \ CARGO_HOME=/usr/local/cargo RUN set -eux; \