Skip to content

download: no checksum or signature verification on any fetched tarball #19

Description

@crippledgeek

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":

  1. 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.
  2. -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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions