findings X-1: pin every dependency tarball fetch to a SHA256 and enforce it

security-audit-v3 #15. download_file already had a verify branch; no caller
used it and every *_SHA256 in versions.sh was a commented placeholder, so a
tampered mirror tarball flowed straight into configure/make and the shipped
WASM.

- versions.sh: 13 pins (cross-checked against Homebrew/Buildroot/nixpkgs/
  FreeBSD/vcpkg/boost.org/curl PGP; glm .zip is TOFU), boost/curl/libgit2
  versions moved beside their pins.
- all 13 download_file call sites pass "${NAME_SHA256}".
- download_file refuses an empty or malformed pin (PCBJAM_ALLOW_UNPINNED=1
  to bootstrap a new dep); file_sha256 prefers sha256sum, falls back to shasum.
- scripts/deps/check-pins.sh: static 3-arg check + offline file:// enforcement
  test; runs in wasm-build.yml before the deps cache, on cache hits too.

Expect one cold --build-deps run: the deps-cache key hashes versions.sh.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GcsgJZ77bhZatLAVU8R84H
This commit is contained in:
Gergő Törcsvári 2026-08-28 20:34:16 +02:00
commit f6b0aaf122
No known key found for this signature in database
GPG key ID: 8E75F2CDE64E5322
16 changed files with 157 additions and 22 deletions

View file

@ -77,15 +77,41 @@ verify_submodules() {
}
# Download file with optional verification
# sha256 of a file. Prefer coreutils (always in the builder image); fall back
# to perl's shasum (macOS hosts; present in the image only because git pulls
# in perl).
file_sha256() {
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$1" | cut -d' ' -f1
else
shasum -a 256 "$1" | cut -d' ' -f1
fi
}
# download_file URL DEST SHA256
# The pin is mandatory (X-1 / security-audit-v3 #15): a fetched-then-compiled
# tarball with no content pin is the supply-chain hole. Set
# PCBJAM_ALLOW_UNPINNED=1 only while bootstrapping a new dependency, to obtain
# the value that then goes into scripts/common/versions.sh.
download_file() {
local url="$1"
local dest="$2"
local expected_sha256="${3:-}"
if [ -z "$expected_sha256" ] && [ "${PCBJAM_ALLOW_UNPINNED:-0}" != "1" ]; then
log_error "download_file: no SHA256 pin for $url"
log_error " add <NAME>_SHA256 to scripts/common/versions.sh (PCBJAM_ALLOW_UNPINNED=1 to bootstrap)"
return 1
fi
if [ -n "$expected_sha256" ] && ! printf '%s' "$expected_sha256" | grep -Eq '^[0-9a-f]{64}$'; then
log_error "download_file: malformed SHA256 pin for $url: $expected_sha256"
return 1
fi
if [ -f "$dest" ]; then
if [ -n "$expected_sha256" ]; then
local actual_sha256
actual_sha256=$(shasum -a 256 "$dest" 2>/dev/null | cut -d' ' -f1)
actual_sha256=$(file_sha256 "$dest" 2>/dev/null)
if [ "$actual_sha256" = "$expected_sha256" ]; then
log_info "$(basename "$dest") already downloaded and verified"
return 0
@ -133,7 +159,7 @@ download_file() {
if [ -n "$expected_sha256" ]; then
local actual_sha256
actual_sha256=$(shasum -a 256 "$dest" | cut -d' ' -f1)
actual_sha256=$(file_sha256 "$dest")
if [ "$actual_sha256" != "$expected_sha256" ]; then
log_error "SHA256 mismatch for $dest"
log_error " Expected: $expected_sha256"