Summary
fetch() downloads every dependency tarball and verifies nothing about it. There is no
checksum field, no signature check, and no integrity mechanism anywhere in the tree — the
only thing standing between a tampered tarball and the compiler is TLS transport security
and whatever the far end happens to serve.
Evidence
lib/download.sh:30 is the entire trust decision:
if curl -fL -sS -o "$DISTDIR/$_file" "$_url"; then
A grep for any verification primitive across the whole repo returns nothing:
$ grep -rniE 'sha256|sha512|checksum|gpg|signify|md5' --include='*.sh' --include='*.conf' .
recipes/crypto/libressl.sh:53: # aesni_encrypt / sha256_block_data_order. <- a comment
tests/avs2-reorder-dts.sh:66: ... -f framemd5 - <- unrelated test
Two properties make it worse than "no checksum":
- The cache is trusted unconditionally.
lib/download.sh:24 is
if [ ! -f "$DISTDIR/$_file" ]. Once a file exists in packages/, it is reused
forever with no check of any kind. A tarball that was corrupted, truncated, or
swapped after landing is never re-examined. The retry loop rm -fs a failed curl,
but a curl that returns 200 with wrong bytes is kept and cached.
-L follows redirects, including cross-host ones, so the host named in PKG_URL
is not necessarily the host that serves the bytes.
Why this is worth doing
110 recipes carry a PKG_URL, spread over 24 distinct hosts. Nothing uses plain
http:// — transport is at least TLS everywhere, which is the good news.
The sharp part is that 18 recipes fetch through mirror redirectors
(ftpmirror.gnu.org, sourceforge.net) that deliberately hand off to third-party
volunteer mirrors:
recipes/crypto/gmp.sh recipes/crypto/nettle.sh
recipes/audio/lame.sh recipes/audio/twolame.sh
recipes/audio/soxr.sh recipes/audio/opencore.sh
recipes/audio/vo_amrwbenc.sh recipes/audio/lv2.sh
recipes/image/libpng.sh recipes/other/freetype2.sh
recipes/other/freetype2-harfbuzz.sh recipes/other/gettext.sh
recipes/other/bs2b.sh recipes/tools/m4.sh
recipes/tools/autoconf.sh recipes/tools/automake.sh
recipes/tools/libtool.sh recipes/tools/giflib.sh
gmp and nettle are gnutls's crypto dependencies, and gnutls is the default TLS
backend. So the default build's crypto stack is fetched from a rotating set of volunteer
mirrors with no integrity check — which is precisely the scenario GNU's own download
guidance tells you to verify signatures for.
This surfaced during review of the libressl cluster (#16/#17/#18). That PR spent its
effort making one TLS library current and correctly configured; this is the weakest link
in the same chain, and it applies to all 110 recipes rather than one.
Prior art in our own tree
The sibling mediaforge-meson port already solves this: every wrap carries a
source_hash, and subprojects/libressl.wrap documents the discipline explicitly —
prefer upstream's published digest over one computed from your own download (a hash
derived from the artifact it authenticates is trust-on-first-use and authenticates
nothing), and prefer the signify-signed SHA256.sig over the plain SHA256 served from
the same host as the tarball.
The shell port has no equivalent. Bringing it to parity is the ask.
Suggested shape
- A
PKG_SHA256 recipe field, verified in fetch() after download and on every cache
hit — the cache path is where the current design is weakest, so verifying only on
fresh download would fix the smaller half.
die on mismatch, naming expected vs actual, and remove the offending file so a retry
cannot silently reuse it.
- Populate from upstream-published digests where they exist, not from our own downloads.
- Roll out by risk rather than alphabetically: the crypto/TLS arms and the mirror-redirector
recipes above first, then the rest.
- A
--skip-checksum escape hatch is probably needed for bumping a version locally, but it
should be loud.
One complication to settle before implementing
35 of the 61 GitHub URLs use the archive/refs/tags/... auto-generated tarball form.
Auto-generated archives are not guaranteed byte-stable — a compression change on GitHub's
side in early 2023 altered checksums ecosystem-wide and broke pinned hashes for a lot of
packagers. This needs confirming against GitHub's current guarantees before we commit to
a design, but if it still holds, the options are to prefer real release assets where a
project publishes them, or to accept that those 35 pins will occasionally churn. Worth
deciding up front rather than discovering it on the first mass-bump.
Not claimed
This is not a report of a compromise, and there is no evidence any fetched tarball has
ever been tampered with. It is a missing defence-in-depth control. Nothing is blocked on
it, and the current builds are fine.
Environment
- branch
develop @ 3f76539, SCRIPT_VERSION="3.0"
- Counts above measured 2026-08-23 against
recipes/ on that commit.
Summary
fetch()downloads every dependency tarball and verifies nothing about it. There is nochecksum field, no signature check, and no integrity mechanism anywhere in the tree — the
only thing standing between a tampered tarball and the compiler is TLS transport security
and whatever the far end happens to serve.
Evidence
lib/download.sh:30is the entire trust decision:A grep for any verification primitive across the whole repo returns nothing:
Two properties make it worse than "no checksum":
lib/download.sh:24isif [ ! -f "$DISTDIR/$_file" ]. Once a file exists inpackages/, it is reusedforever with no check of any kind. A tarball that was corrupted, truncated, or
swapped after landing is never re-examined. The retry loop
rm -fs a failed curl,but a curl that returns 200 with wrong bytes is kept and cached.
-Lfollows redirects, including cross-host ones, so the host named inPKG_URLis not necessarily the host that serves the bytes.
Why this is worth doing
110 recipes carry a
PKG_URL, spread over 24 distinct hosts. Nothing uses plainhttp://— transport is at least TLS everywhere, which is the good news.The sharp part is that 18 recipes fetch through mirror redirectors
(
ftpmirror.gnu.org,sourceforge.net) that deliberately hand off to third-partyvolunteer mirrors:
gmpandnettleare gnutls's crypto dependencies, and gnutls is the default TLSbackend. So the default build's crypto stack is fetched from a rotating set of volunteer
mirrors with no integrity check — which is precisely the scenario GNU's own download
guidance tells you to verify signatures for.
This surfaced during review of the libressl cluster (#16/#17/#18). That PR spent its
effort making one TLS library current and correctly configured; this is the weakest link
in the same chain, and it applies to all 110 recipes rather than one.
Prior art in our own tree
The sibling
mediaforge-mesonport already solves this: every wrap carries asource_hash, andsubprojects/libressl.wrapdocuments the discipline explicitly —prefer upstream's published digest over one computed from your own download (a hash
derived from the artifact it authenticates is trust-on-first-use and authenticates
nothing), and prefer the signify-signed
SHA256.sigover the plainSHA256served fromthe same host as the tarball.
The shell port has no equivalent. Bringing it to parity is the ask.
Suggested shape
PKG_SHA256recipe field, verified infetch()after download and on every cachehit — the cache path is where the current design is weakest, so verifying only on
fresh download would fix the smaller half.
dieon mismatch, naming expected vs actual, and remove the offending file so a retrycannot silently reuse it.
recipes above first, then the rest.
--skip-checksumescape hatch is probably needed for bumping a version locally, but itshould be loud.
One complication to settle before implementing
35 of the 61 GitHub URLs use the
archive/refs/tags/...auto-generated tarball form.Auto-generated archives are not guaranteed byte-stable — a compression change on GitHub's
side in early 2023 altered checksums ecosystem-wide and broke pinned hashes for a lot of
packagers. This needs confirming against GitHub's current guarantees before we commit to
a design, but if it still holds, the options are to prefer real release assets where a
project publishes them, or to accept that those 35 pins will occasionally churn. Worth
deciding up front rather than discovering it on the first mass-bump.
Not claimed
This is not a report of a compromise, and there is no evidence any fetched tarball has
ever been tampered with. It is a missing defence-in-depth control. Nothing is blocked on
it, and the current builds are fine.
Environment
develop@ 3f76539,SCRIPT_VERSION="3.0"recipes/on that commit.