Skip to content

fix(install): tie install.sh tag to the archive, pin archive owner, verify first - #524

Merged
wstein merged 6 commits into
mainfrom
feat/495-install-release-tag-binding
Oct 9, 2026
Merged

wstein merged 6 commits into
mainfrom
feat/495-install-release-tag-binding

Conversation

@wstein

@wstein wstein commented Oct 9, 2026

Copy link
Copy Markdown
Owner

Smaller scope of #495 (items 1, 3, 4, 6; item 2 skipped, 5 resolved by alpha.5, 7 goes to the design lane via #399):

  • install.sh: when checksums.txt sits beside the script, the tag must match the archive next to it, the archive is checked against its checksum, and the files are installed from that unpacked archive (an exported root is ignored). Download mode shares the same checks.
  • goreleaser: archive files are owned root:wheel (builds_info and files[].info).
  • docs: the install page verifies the archive with gh attestation verify before sudo ./install.sh; narrowed claim for the piped form.
  • tests for the tag/archive binding, tampered and missing archive, the note without checksums.txt, the piped script and an exported root.

Closes #495

Review: Opus CLEAR on the whole branch (head 1d5ed0fd before a rebase onto current main; range-diff equal). Open Low (batched on #399): the /bin/bash -s piped form falls into download mode (documented, not changed). Unverified: a real release archive install, root:wheel ownership on the real release runner, sudo ./install.sh on a clean Mac. Known pre-existing failure under macOS bash 3.2: TestHugoRefusesAssetWithWrongChecksum (unrelated).

🤖 Generated with Claude Code

wstein and others added 6 commits October 9, 2026 13:08
Unpacked mode wrote the tag argument to VERSION without looking at the
archive, so a wrong tag corrupted the downgrade guard. When checksums.txt
sits next to the script, the archive of that tag must be there too and
match its checksum line; without checksums.txt the tag stays unchecked
and the script says so. Tests cover matching and wrong tag, a tampered
archive, the missing checksums.txt, a writable prefix (warned, not
refused, per #493) and the piped script in an unpacked directory.

Refs: #495
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Without it the archive records the uid and group of the build runner,
which a root extraction (sudo tar -xzf) would restore on the installed
tree. Pinning root:wheel for every file keeps that identity out of the
archive; harmless today, as the installer sets its own modes.

Refs: #495
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The unpacked mode never checks the attestation, so an upgrade, where gh
is installed, ran as root on the checksum and TLS alone. The manual now
says to run gh attestation verify on the archive before sudo ./install.sh
and describes how the tag is bound to the archive next to the script.

Refs: #495
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The tag was bound to the archive name and checksum, but the installed
files still came from bin/ and guest/ beside the script, which could
differ from the archive. With checksums.txt present the script now
unpacks the checked archive and installs from that, so tag, archive and
installed files are one thing. The wrong-tag message also covers a
removed archive, and stale header and log text is corrected.

Refs: #495
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The attestation check now comes before the tar and sudo install.sh
block, so pasting top to bottom verifies before installing; a first
install without gh skips it. The script now installs the files of the
archive beside checksums.txt, and the piped form is narrowed to the
tested cat install.sh | bash -s.

Refs: #495
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The unpacked branch kept a root variable from the environment when no
checksums.txt sat beside the script, so an exported root decided where
the files were installed from. The no-checksums.txt branch now sets root
itself. A test exports root and checks both branches.

Refs: #495
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
@wstein

wstein commented Oct 9, 2026

Copy link
Copy Markdown
Owner Author

Opus review CLEAR on the whole branch (1d5ed0fd, rebased onto main 3877085 as a59e2f3; git range-diff shows all six commits equal). Reviewer runs on 1d5ed0fd: full go test ./... and make check-ci exit 0 (default bash); mutation checks show TestUnpackedWrongTagIsRefused, TestUnpackedTamperedArchiveIsRefused, TestUnpackedInstallsTheArchiveNotTheStrayTree and TestUnpackedIgnoresAnExportedRoot can fail. The rebased head is verified by this PR's CI. Known pre-existing, unrelated failure under macOS /bin/bash 3.2 only: TestHugoRefusesAssetWithWrongChecksum (scripts/hugo.sh, empty array with set -u), also at the base commit. Lows (batched on #399): /bin/bash -s piped form falls into download mode (documented). Unverified: real release archive install, root:wheel on the real release runner, sudo ./install.sh on a clean Mac.

@wstein
wstein marked this pull request as ready for review October 9, 2026 11:20
@wstein
wstein merged commit f080280 into main Oct 9, 2026
20 of 21 checks passed
@wstein
wstein deleted the feat/495-install-release-tag-binding branch October 9, 2026 11:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

release: follow-ups of the one-archive install (attestation on upgrades, source checks, tag binding)

1 participant