The install instructions for rustup, Homebrew, nvm, Docker, most Kubernetes tooling, most observability agents, and thousands of smaller projects share a single opening line:
1curl -sSL https://example.com/install.sh | sudo bash
Most operators run it. Most operators know, dimly, that they probably shouldn’t. This series is about what “probably shouldn’t” actually means, and about a workflow that gives you the convenience of a one-liner without the trust-me-bro leap of faith.
What can actually go wrong
Four failure modes, ordered by realism. None of them are hypothetical.
1. The endpoint gets compromised
Someone gets write access to example.com/install.sh — a
stolen deploy key, a leaked S3 credential, a wildcard bucket
policy, a compromised admin account. The next time you run the
one-liner, you run whatever they swapped in. No malware scanner
catches it because it never touches disk long enough. Your box is
running as root during the pipe, so the malicious script does too.
Real example: in 2024, several npm and PyPI package accounts were
taken over via credential-stuffing against 2FA-free accounts.
Any downstream curl | bash install that fetched from those
accounts inherited the compromise for the duration.
2. The DNS resolves somewhere unexpected
You typed example.com; something answered as example.com. If a
resolver upstream of you (public Wi-Fi, coffee-shop router,
compromised ISP, malicious cache poisoning attack) points that name
at their server for a minute, you fetch and execute their bash.
HTTPS with a valid cert would catch a MitM — but only if the
attacker doesn’t also have a certificate for that name (which recent
misissuance events have shown is not a zero-probability event).
3. The install page is fine — but you ran the wrong one
The typosquat variant. You Googled the project name, clicked the
top result, and the top result was install-rustup.dev not
rustup.rs. Everything below the browser bar looks correct.
4. The install page is a mirror — of a compromised project
Legitimate publisher, legitimate DNS, legitimate cert — but the upstream project itself was compromised last week and nobody’s noticed yet. Downstream users of the install script pull the compromised version until the maintainer catches it, cuts a release, updates the pinned version. Historical examples: xz-utils backdoor (2024), event-stream (2018), left-pad (not a compromise but the same class of “downstream inherits upstream without validation”).
Failure modes 1 and 2 are what verification catches. Failure mode 3 is what “get the URL from a trusted source” catches (see Week 3). Failure mode 4 is a longer conversation about pinning and lockfiles — out of scope for this series but worth knowing exists.
Why the piped form specifically is worse than the two-line form
Compare:
1# The one-liner
2curl -sSL https://example.com/install.sh | sudo bash
3
4# The two-line form
5curl -sSL https://example.com/install.sh -o /tmp/install.sh
6sudo bash /tmp/install.sh
Functionally they run the same script. Security-wise they are different in one specific way: the two-line form leaves an artifact on disk that you can read, hash, and audit later. The piped form leaves nothing — if it did something bad, your only evidence is whatever the script itself wrote to your system.
The two-line form is also serving-condition-aware. A defensive
publisher can serve different content when the request looks like it
came from a shell (Accept: */*, no Referer) versus a browser
inspecting the file. That’s a real observed attack pattern: the
curl fetch gets the malicious version; the browser check gets the
benign one. If you download once and read the same file you’ll run,
you sidestep the differential.
The four-step alternative
Add three commands and one habit:
- Download the script to disk (not straight to bash).
- Verify the signature against a publisher key you trust.
- Read the script — at least skim it for anything
surprising: unexpected
curlcalls,chmodon system dirs, base64 blobs, calls to obscure infrastructure. - Run it — only if steps 2 and 3 look right.
1# 1. Download the script + its signature
2curl -sSL https://example.com/install.sh -o install.sh
3curl -sSL https://example.com/install.sh.sig -o install.sh.sig
4
5# 2. Verify
6gpg --verify install.sh.sig install.sh
7# → "Good signature from Publisher <[email protected]>"
8
9# 3. Read
10less install.sh
11
12# 4. Run
13sudo bash install.sh
Costs you roughly thirty seconds — the time to eyeball a few hundred lines of shell — and closes failure modes 1 and 2 entirely.
The rub: most publishers don’t ship signatures, and most operators don’t know how to verify one when they do. Weeks 2 and 3 of this series fix both.
Why signatures matter more than checksums
You’ll often see install pages publish a SHA-256 checksum alongside the file:
sha256sum: 3e2d…f01b install.sh
Checksums prove the file wasn’t corrupted in transit. They do not prove the file wasn’t tampered with by whoever hosts it. An attacker who can substitute the script can also substitute the published checksum on the same page. Checksums are a data-integrity tool, not a supply-chain-integrity tool.
Signatures are different: they prove the file was signed with a specific private key that only the publisher holds. An attacker who compromises the publisher’s website can swap the script, but cannot sign the new version without also compromising the publisher’s signing key — which lives elsewhere and (if the publisher is doing it properly) is offline.
That’s why signatures are the load-bearing bit, and why the rest of this series focuses on them.
What this series covers
- Week 2 — GPG signature verification for operators. Not
the full “how to encrypt email” tutorial. Just: how to import a
publisher’s key, how to run
gpg --verify, and how to tell the difference between “signature valid” and “signer trusted”. - Week 3 — Using GitHub as a source of trust: signed commits, signed tags, release attestations, how to read raw source without cloning, how to spot a typosquat.
- Week 4 — The four-step workflow, end-to-end, against a real signed release — the StackHarden Hardening Audit script. You install it the right way. If the pattern sticks, every future install feels the same.
What to do this week
Before reading Week 2:
- Open the install pages of two or three tools you use regularly. Notice which ones publish signatures at all. (Most don’t.)
- Pick one that does — e.g. tor-browser, nodejs, bitcoin-core — and skim its verification instructions. Don’t try to run through them yet; just look at the shape.
- Next week we walk through exactly what those instructions are asking you to do.
Next in this series
- Week 2 — GPG for operators who ship, not receive.
- Week 3 — GitHub as a source of trust, not just code.
- Week 4 — A verified script, end-to-end (worked example).
Further reading
- ENISA (2023). ENISA Threat Landscape for Supply Chain Attacks.
- NIST SP 800-218 — Secure Software Development Framework.
- CISA (2024). Software supply chain security guidance for customers.