Repository navigation
fix(installers): verify resq build provenance and stop masking API errors - #100
Conversation
…rors
All three resq installers (install-resq.sh, install.sh, install.ps1):
- pick the highest stable resq-cli-vX.Y.Z by number, not the first-created release;
- when gh 2.68+ can check it, verify the Sigstore attestation with the exact release.yml@refs/tags/<tag> signer, the tag's commit as source digest, hosted runners only, and require that commit to be on crates master. A check that runs and fails installs nothing. RESQ_REQUIRE_PROVENANCE=1 makes an unavailable check fatal;
- require the new binary to report the tag's version, stage it beside the target, keep the old one as resq.prev;
- write an install receipt (${XDG_CONFIG_HOME:-~/.config}/resq/install.json; %APPDATA%\resq on Windows) so a later self-update replaces only binaries a verified release installed.
install-resq.sh no longer turns a GitHub API error into an empty tag and a silent source build, cleans up its staging file on a signal under dash, and refuses RESQ_ALLOW_UNVERIFIED=1 combined with RESQ_REQUIRE_PROVENANCE=1. tests/installers/release-path.sh covers it hermetically (15 cases; 12 of the original 13 fail against the old script), and pinning.sh no longer writes a receipt into the real config dir. VERSION 0.4.7.
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
get-resq-software | efd1ca0 | Oct 03 2026, 10:50 PM |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 🧰 Additional context used📚 Code guidelines (1)No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (9)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe three installers now select stable releases, check release artifacts and provenance, validate binaries before replacement, and write installation receipts. New installer tests cover release selection, verification outcomes, failed checks, upgrades, and receipt creation. ChangesRelease installer flow
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~45 minutes Change: Bug fix Sequence Diagram(s)sequenceDiagram
participant Installer as install-resq.sh
participant GitHubAPI as GitHub releases API
participant GH as gh CLI
participant InstallDir as install directory
Installer->>GitHubAPI: Request release metadata
GitHubAPI-->>Installer: Return release tags and assets
opt Provenance verification is available
Installer->>GH: Verify release attestation
GH-->>Installer: Return verification result
end
Installer->>InstallDir: Stage and replace validated binary
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 52.94% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 17 functions across 5 files. (4 skipped: 4 unsupported.)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
AI Auditor: LGTM! I have reviewed the changes in this pull request and found no security vulnerabilities, logic bugs, or performance issues. The changes significantly improve the security and robustness of the installation scripts by adding build provenance verification, atomic installations, and better error handling. The addition of comprehensive tests is also a great improvement. Well done!
|
Summary
The three
resqinstallers (scripts/install-resq.sh,install.sh,install.ps1) verified a download only againstSHA256SUMS. That file comes from the same release as the archive, so anyone able to publish the release can publish matching sums.resq-software/cratesattaches a Sigstore build attestation to every binary release (0.5.0–0.5.2 checked), but none of the installers looked at it.This PR also fixes a few long-standing defects in how
install-resq.shpicks and installs a release.Changes (all three installers unless noted)
Tag choice. The installers now pick the highest stable
resq-cli-vX.Y.Zby number. The API lists releases by creation date and crates releases several crates at once, so "first match" picked the newest-created release, not the newest version. Pre-releases, drafts and other crates' tags are never picked.API errors are errors (
install-resq.sh). The oldcurl | sed | headran underset -euwithout pipefail, so a failed request (network, proxy, the 60/hour rate limit) came out as an empty tag. That read as "no release exists" and the script quietly fell back to building from source.Provenance check. When
gh2.68+ is available (probed by--source-digest, not just the subcommand: 2.49–2.67 haveattestation verifywithout it), the installers:master;--cert-identity, never the prefix-matching--signer-workflow).A check that runs and fails installs nothing. Without a capable
gh, the install continues on the checksum and says so.RESQ_REQUIRE_PROVENANCE=1makes that a refusal.Safer install. The extracted binary must report the tag's version before it replaces anything. It is staged beside the target and renamed into place, and the old one is kept as
resq.prev. Underdash, a signal no longer leaves the staging file behind.Install receipt at
${XDG_CONFIG_HOME:-~/.config}/resq/install.json(Windows:%APPDATA%\resq\install.json):{"schema": 1, "method": "release|cargo", "ref": "resq-cli-v0.5.2", "path": "...", "sha256": "...", "provenance": "verified|unchecked|n/a", "installed_at": "2026-10-03T22:10:00Z"}This is so a future
resq self updatereplaces only a binary a verified release installed, never a cargo-, nix- or hand-built one.Flag conflict (
install-resq.sh).RESQ_ALLOW_UNVERIFIED=1together withRESQ_REQUIRE_PROVENANCE=1is refused as a contradiction.Docs. README's
install-resq.shrow said the cargo fallback was "the only path". It no longer is. The new variable and the receipt are documented, andinstall.sh --helplistsRESQ_REQUIRE_PROVENANCE.VERSION 0.4.7, re-stamped. Merging this is a release.
Verified against the real releases
gh attestation verifypasses forresq-cli-v0.5.2's Linux x64 asset, and fails for a wrong tag identity and for a tampered file.resq-cli-v0.5.2namesrelease.yml@refs/tags/resq-cli-v0.5.2, source digest72e0ae4, on a GitHub-hosted runner.resq-cli-v0.5.0,v0.5.1andv0.5.2are all attested, and their tag commits are on cratesmaster.Test plan
tests/installers/release-path.sh(new, hermetic: stubbed curl/gh/cargo, each case underenv -iwith a scratch HOME): 15/15. Against the oldinstall-resq.sh, 12 of the original 13 cases fail; the one that passes is the existing checksum-mismatch guard.install.ps1harness (52 checks, run against the real function via its AST): 52/52 with a moderngh. With aghlacking--source-digest, exactly the provenance-verified cases turn into "unchecked".install.shfunction smoke-tested undersh,dash, andbash -o pipefailwith a stubgh. stdout stays empty.sh tests/run_all.sh: all 6 suites pass.tests/installers/pinning.shno longer writes a receipt into the real~/.config.install.shas POSIX sh,dash -n. Both.ps1files parse under pwsh 7.6. actionlint and zizmor are clean onrequired.yml. Everyrequired.ymlshellcheck-job step runs locally and passes.required,windows-smoke(Windows PowerShell 5.1 runs only there)SHA256SUMS+ SBOM with an attestation. That depends on fix(release): attest and attach the SBOM before the immutable Release exists #99 merging first.Not in this PR
resq self update) lives in resq-software/crates. The location and schema above are the contract.install.shandinstall.ps1keep their existing "warn and skip" convention for a failed check during onboarding, rather than aborting the whole setup.Summary by CodeRabbit
New Features
Bug Fixes
Documentation