ci: verify the shellcheck download instead of trusting the transport - #108
Merged
Conversation
The two downloads in this repository fetched shellcheck over HTTPS and extracted it unchecked, and shell-ci.yml declined a checksum in a comment: "the tarball comes straight from koalaman/shellcheck's own GitHub release over HTTPS, so there is no separate checksum to embed." Every other tool here arrives through a registry that verifies what it hands over — Go's sumdb, crates.io, PyPI — so in a repository whose doctrine is that the pin is the security boundary, these two were the only places trusting the transport. The tarball now lands in a file and is checked against a sha256 before extraction. Piping curl into tar could never have verified anything: the bytes are extracted as they arrive. The hash is an input beside shellcheck-version, so the two travel together and a consumer bumping only the version fails the install rather than silently downloading unverified. Note what this does and does not buy: koalaman publishes no checksum file and no signatures with the release, so this pins the artifact I measured rather than one upstream attests to. It detects a replaced or re-uploaded asset; it does not detect an upstream that was malicious from the start. actionlint.yml gets the same install and passes the binary through -shellcheck. Its embedded pass was falling back to the runner image's floating shellcheck, which is precisely what workflow-lint installs a pinned one to avoid: this repository was linting itself to a weaker standard than the one it ships. Also gitignore Python bytecode, since running the docs generator leaves scripts/__pycache__ behind. Test plan: ran the install script as a runner would (standalone bash, not inside an && list, where errexit is ignored and my first attempt wrongly showed a pass) — the correct hash extracts and reports 0.11.0, a wrong hash exits 1 with the directory left empty, nothing extracted. actionlint 1.7.12 clean over the tree. Pages regenerated; the two new inputs are the only content change. Signed-off-by: Jaro-c <75870284+Jaro-c@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The only two network downloads in this repository — the shellcheck tarball in
shell-ci.ymland inworkflow-lint.yml— fetched over HTTPS and extracted unchecked, andshell-ci.ymldeclined a checksum in a comment: "the tarball comes straight from koalaman/shellcheck's own GitHub release over HTTPS, so there is no separate checksum to embed."Every other tool here arrives through a registry that verifies what it hands over (Go's sumdb, crates.io, PyPI). These two were the only places in a repository built on "the pin is the security boundary" that trusted the transport instead.
What this buys, stated honestly: koalaman publishes no checksum file and no signatures alongside the release — I checked the assets for
v0.11.0and there are seven tarballs and nothing else. So the hash pins the artifact I measured, not one upstream attests to. It detects a replaced or re-uploaded asset; it does not detect an upstream that was malicious from the start.Changes
sha256sum -cbefore extraction.curl | tarcould never have verified anything — the bytes are extracted as they arrive.shellcheck-version, so the two travel together: a consumer bumping only the version fails the install rather than silently downloading something unverified.actionlint.ymlgets the same install and passes the binary through-shellcheck. Its embedded pass was falling back to the runner image's floating shellcheck, which is exactly whatworkflow-lintinstalls a pinned one to avoid — this repository was linting itself to a weaker standard than the one it ships to consumers..gitignore: Python bytecode, since running the docs generator leavesscripts/__pycache__behind.Test plan
bashscript, not inside an&&list. That distinction mattered: my first attempt wrapped it in&&, where errexit is ignored, and it wrongly showed a wrong hash passing.version: 0.11.0, exit 0sha256sum: WARNING: 1 computed checksum did NOT match, exit 1,shellcheck-bin/empty, nothing extractedNote on the release
This touches two reusables, so it needs a tag for consumers to get it — and per the org rule, no tag before a consumer PR proves it green. That consumer PR is outside this repository, so I have not opened one.