The cargo-dist-generated workflow's plan, build-local-artifacts, build-global-artifacts, and host jobs all start with:
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/axodotdev/cargo-dist/releases/download/v0.32.0/cargo-dist-installer.sh | sh
There's no SHA256 (or other integrity) check of the installer script or the binary it then downloads. The only guarantees are HTTPS + TLS plus the fact that the asset is hosted on a GitHub release. If the axodotdev release were tampered with β or an in-path actor intercepted the response β the workflow silently executes a hostile script that has secrets.GITHUB_TOKEN access for the calling repo.
This is distinct from:
#2339 β corruption of generated output installer scripts during Actions artifact transfer (fixed by upgrading to download-artifact@v8).
#1146 / #1147 β GitHub artifact attestations for output artifacts.
#120 β sigstore signing of output binaries.
All of those address the output. This issue is about the bootstrap of dist itself.
Proposal
Variant A β pin the bootstrap script via SHA256. Bake the expected hash into the workflow at generation time, derived from cargo-dist-version:
curl --proto '=https' --tlsv1.2 -LsSf $URL -o installer.sh
echo "$EXPECTED_SHA256 installer.sh" | sha256sum --check
sh installer.sh
The hash is a known-at-generation-time constant per pinned cargo-dist version. The cargo-dist release process would need to publish the SHA alongside the installer script (it's easy to compute from the existing release asset).
Variant B β opt-in cargo install bootstrap. A config knob, e.g.:
[workspace.metadata.dist]
self-install = "cargo" # default "curl-sh"
Swaps the bootstrap for cargo install --locked --version <pinned> cargo-dist. Slower (compile from source) but inherits crates.io's existing integrity guarantees and produces no new supply-chain dependency.
Let me know what you think!
The cargo-dist-generated workflow's
plan,build-local-artifacts,build-global-artifacts, andhostjobs all start with:There's no SHA256 (or other integrity) check of the installer script or the binary it then downloads. The only guarantees are HTTPS + TLS plus the fact that the asset is hosted on a GitHub release. If the axodotdev release were tampered with β or an in-path actor intercepted the response β the workflow silently executes a hostile script that has
secrets.GITHUB_TOKENaccess for the calling repo.This is distinct from:
#2339 β corruption of generated output installer scripts during Actions artifact transfer (fixed by upgrading to download-artifact@v8).
#1146 / #1147 β GitHub artifact attestations for output artifacts.
#120 β sigstore signing of output binaries.
All of those address the output. This issue is about the bootstrap of dist itself.
Proposal
Variant A β pin the bootstrap script via SHA256. Bake the expected hash into the workflow at generation time, derived from cargo-dist-version:
The hash is a known-at-generation-time constant per pinned cargo-dist version. The cargo-dist release process would need to publish the SHA alongside the installer script (it's easy to compute from the existing release asset).
Variant B β opt-in cargo install bootstrap. A config knob, e.g.:
Swaps the bootstrap for
cargo install --locked --version <pinned> cargo-dist. Slower (compile from source) but inherits crates.io's existing integrity guarantees and produces no new supply-chain dependency.Let me know what you think!