mirror of
https://github.com/jmcorgan/fips.git
synced 2026-08-12 09:33:23 +00:00
Verify the zig download before extracting it
The OpenWrt cross-compile fetched zig through `curl | sudo tar xJ`, so a short read reached tar as a truncated archive and failed the build with "Unexpected EOF in archive". A pipe leaves nowhere to check the bytes, and curl's own --retry does not cover it: exit 18 is not in its transient set. Download to a staging directory first, verify a pinned SHA-256, then extract. Each architecture now sets its hash on the same case branch that sets its name, so an architecture cannot be added without one, and a guard fails with the jq recipe for deriving it if the hash is ever empty. Three attempts with 10s and 20s backoff, matching the retry idiom already in this workflow, and an early exit when two attempts return identical bytes, since a stable mismatch is a wrong pin rather than a bad transfer. The step also gains `set -euo pipefail` and a trap that removes the staging directory on every exit path. It previously ran under the default shell without pipefail, so a failure inside the pipe could be masked by tar. The hashes come from ziglang.org's download index and were checked against the bytes of both tarballs. That is integrity, not authenticity: index and archive share an origin, and upstream publishes no detached sums.
This commit is contained in:
@@ -138,15 +138,92 @@ jobs:
|
||||
run: cargo install cargo-zigbuild --version 0.19.8 --locked
|
||||
|
||||
- name: Install zig (required by cargo-zigbuild)
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ZIG_VERSION="0.13.0"
|
||||
# Each arch carries the expected SHA-256 of its upstream tarball,
|
||||
# taken from ziglang.org's own https://ziglang.org/download/index.json,
|
||||
# field .["<version>"]["<arch>-linux"].shasum:
|
||||
# jq -r '.["0.13.0"]["x86_64-linux"].shasum' index.json
|
||||
# Upstream publishes no .sha256 sidecar and no SHA256SUMS, so
|
||||
# index.json is the only checksum document offered, and it lists only
|
||||
# recent releases: once a version ages out of it the pin can no longer
|
||||
# be re-derived upstream. Bumping ZIG_VERSION means replacing every
|
||||
# hash below, and adding an arch means adding its hash here too.
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64|amd64) ZIG_ARCH="x86_64" ;;
|
||||
aarch64|arm64) ZIG_ARCH="aarch64" ;;
|
||||
*) echo "Unsupported architecture: $ARCH"; exit 1 ;;
|
||||
x86_64|amd64)
|
||||
ZIG_ARCH="x86_64"
|
||||
ZIG_SHA256="d45312e61ebcc48032b77bc4cf7fd6915c11fa16e4aad116b66c9468211230ea"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
ZIG_ARCH="aarch64"
|
||||
ZIG_SHA256="041ac42323837eb5624068acd8b00cd5777dac4cf91179e8dad7a7e90dd0c556"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture: $ARCH"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
curl -fsSL "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz" | sudo tar xJ -C /opt
|
||||
if [ -z "${ZIG_SHA256:-}" ]; then
|
||||
echo "No SHA-256 pinned for zig ${ZIG_VERSION} on ${ZIG_ARCH}."
|
||||
echo "Add one to the case above, from https://ziglang.org/download/index.json:"
|
||||
echo " jq -r '.[\"${ZIG_VERSION}\"][\"${ZIG_ARCH}-linux\"].shasum'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NAME="zig-linux-${ZIG_ARCH}-${ZIG_VERSION}.tar.xz"
|
||||
URL="https://ziglang.org/download/${ZIG_VERSION}/${NAME}"
|
||||
# Stage outside the checkout so a failed attempt cannot leave a stray
|
||||
# tarball in the working tree.
|
||||
ZIG_TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$ZIG_TMP"' EXIT
|
||||
TARBALL="${ZIG_TMP}/${NAME}"
|
||||
|
||||
# Download to a file and check it before anything consumes it: piping
|
||||
# curl straight into tar let a truncated transfer reach the extractor,
|
||||
# which is how this step failed. curl's own --retry does not cover a
|
||||
# short read (exit 18), and a checksum mismatch needs a fresh download
|
||||
# anyway, so the retry is an explicit bounded loop.
|
||||
verified=""
|
||||
previous=""
|
||||
for attempt in 1 2 3; do
|
||||
rm -f "$TARBALL"
|
||||
if curl -fsSL -o "$TARBALL" "$URL" && [ -s "$TARBALL" ]; then
|
||||
actual="$(sha256sum < "$TARBALL" | cut -d' ' -f1)"
|
||||
if [ "$actual" = "$ZIG_SHA256" ]; then
|
||||
echo "zig tarball matches its pinned SHA-256 (${actual})"
|
||||
verified=yes
|
||||
break
|
||||
fi
|
||||
echo "zig tarball failed its checksum on attempt ${attempt}:"
|
||||
echo " expected ${ZIG_SHA256}"
|
||||
echo " actual ${actual}"
|
||||
echo " size $(wc -c < "$TARBALL") bytes"
|
||||
if [ "$actual" = "$previous" ]; then
|
||||
echo "Two attempts fetched byte-identical content, so retrying is not"
|
||||
echo "going to help: the pin is stale, upstream re-published, or the"
|
||||
echo "source is serving the same bad file every time."
|
||||
break
|
||||
fi
|
||||
previous="$actual"
|
||||
else
|
||||
echo "zig tarball download failed on attempt ${attempt}"
|
||||
fi
|
||||
if [ "$attempt" -lt 3 ]; then
|
||||
sleep $((attempt * 10))
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$verified" ]; then
|
||||
echo "zig ${ZIG_VERSION} (${ZIG_ARCH}) did not download with its pinned"
|
||||
echo "SHA-256 after ${attempt} attempt(s). Refusing to extract a tarball"
|
||||
echo "that does not match the pin; failing the build."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo tar xJ -C /opt -f "$TARBALL"
|
||||
sudo ln -sf /opt/zig-linux-${ZIG_ARCH}-${ZIG_VERSION}/zig /usr/local/bin/zig
|
||||
zig version
|
||||
|
||||
|
||||
Reference in New Issue
Block a user