Skip to content

fix(install): verify Sigstore build provenance at first install (#61) - #66

Merged
xjoker merged 1 commit into
devfrom
cursor/installer-verify-provenance-5a91
Aug 26, 2026
Merged

fix(install): verify Sigstore build provenance at first install (#61)#66
xjoker merged 1 commit into
devfrom
cursor/installer-verify-provenance-5a91

Conversation

@xjoker

@xjoker xjoker commented Aug 25, 2026

Copy link
Copy Markdown
Owner

Fixes #61.

Problem

scripts/install.sh and scripts/install.ps1 download the archive and its ${asset}.sha256 from the same GitHub Release and only check that the two match. That trusts anyone who can replace both files together. self-update already does more — SHA-256 plus gh attestation verify against this repo and release workflow (src/update.rs) — but first install did not.

Fix

After the checksum passes, verify the archive's Sigstore build provenance:

gh attestation verify <archive> \
  --bundle codex-switch-build-provenance.json \
  --repo xjoker/codex-switch \
  --signer-workflow xjoker/codex-switch/.github/workflows/release.yml \
  --deny-self-hosted-runners

The provenance bundle is already published as a release asset (used by self-update). This proves the artifact was built by this repository's release workflow on a GitHub-hosted runner and cannot be forged, which the checksum cannot.

Key property: offline --bundle mode needs neither gh auth login nor any GitHub API call, so it works during a fresh curl … | bash / irm … | iex install.

Behavior

  • GitHub CLI with attestation support present → verify; a failed verification (tampered artifact or wrong signer) aborts the install.
  • Not present → the checksum is still enforced and provenance is skipped with a warning. Set CS_REQUIRE_PROVENANCE=1 to make a missing verifier a hard failure.

Applied symmetrically to install.sh (bash) and install.ps1 (PowerShell).

Verification

Empirically validated gh attestation verify against the real v20260804.1.0 release before writing the scripts, then ran the modified install.sh end-to-end against the live release (sandboxed HOME):

  • Genuine archive, gh + attestation: Build provenance verified: cs-linux-amd64.tar.gz → installs, exit 0.
  • gh without attestation support, default: warns skipping build-provenance verification (checksum was still verified) → installs, exit 0.
  • gh without attestation support, CS_REQUIRE_PROVENANCE=1: errors → exit 1 (fail closed).
  • Tampered archive / wrong --signer-workflow: gh attestation verify exits 1 → install aborts.
  • Confirmed bundle mode succeeds with no gh auth (GH_CONFIG_DIR empty, GH_TOKEN/GITHUB_TOKEN unset), while online mode required auth — hence bundle mode.

bash -n scripts/install.sh passes; cargo fmt --check, clippy -D warnings, cargo test --all all pass (no Rust changed). install.ps1 mirrors the logic; its syntax is validated by the existing CI PowerShell parse step (no local pwsh available).

Residual

Unlike self-update, the installer does not additionally pin --source-ref / --source-digest (that needs a tag→commit resolution via the GitHub API and would reintroduce an auth/rate-limit dependency). The --repo + --signer-workflow + --deny-self-hosted-runners pins already defeat the asset-replacement attack in the issue; tighter source pinning can be a follow-up.

Docs

Updated the wiki Getting-Started.md install section to describe checksum + provenance verification and CS_REQUIRE_PROVENANCE.

Note: the CI Format and audit job will be red until #63 (webbrowser RUSTSEC-2026-0257 lockfile bump) merges to dev; that failure is pre-existing and unrelated to this change.

Open in Web Open in Cursor 

…#61)

install.sh and install.ps1 downloaded the archive and its .sha256 from the
same release and only compared them to each other, so an attacker able to
replace both files was trusted. self-update already verifies build
provenance with gh attestation verify; first install did not.

Add a provenance check after the checksum passes: download the release's
codex-switch-build-provenance.json bundle and run
  gh attestation verify <archive> --bundle <bundle> --repo xjoker/codex-switch
    --signer-workflow xjoker/codex-switch/.github/workflows/release.yml
    --deny-self-hosted-runners
which proves the artifact was built by this repo's release workflow on a
GitHub-hosted runner and cannot be forged. Offline --bundle mode needs no
gh auth login and no GitHub API call, so it works during curl | bash.

Graceful by default: without a GitHub CLI that supports attestation the
checksum is still enforced and provenance is skipped with a warning; set
CS_REQUIRE_PROVENANCE=1 to make that a hard failure. A failed verification
(tampered artifact or wrong signer) always aborts the install.

Co-authored-by: xJoker <xjoker@users.noreply.github.com>
@xjoker
xjoker marked this pull request as ready for review August 25, 2026 23:43
@xjoker
xjoker merged commit c4f7c90 into dev Aug 26, 2026
3 of 4 checks passed
@cursor
cursor Bot deleted the cursor/installer-verify-provenance-5a91 branch August 26, 2026 07:04
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.

2 participants