Skip to content

Commit a330f80

Browse files
fix(install): make provenance check best-effort
gh attestation verify downloads Sigstore's trusted root, which since Rekor v2 GA contains an Ed25519 (PKIX_ED25519) tlog key. gh builds older than 2.56.0 bundle a sigstore-go that cannot parse it and error out. The installer was fail-closed, so this aborted the whole install even though the sha256 checksum had already matched (END-609). Make the attestation a best-effort layer on top of the mandatory sha256: an unusable verifier (old gh, absent, rate-limited, offline, or no attestation published) now warns and continues with an "upgrade gh >= 2.56.0" hint, while a genuine provenance mismatch still fails closed. Also drop the `gh auth status` gate, since verifying a public repo's attestation needs no login. Set GGSHIELD_REQUIRE_ATTESTATION=1 to restore strict, fail-closed behavior. Refs: END-609 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3b69733 commit a330f80

2 files changed

Lines changed: 56 additions & 8 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Fixed
2+
3+
- The `curl | bash` installer no longer aborts when an outdated `gh` cannot verify the build provenance attestation (for example a `gh` older than 2.56.0, which fails to parse Sigstore's Ed25519 trusted-root key). Provenance is now best-effort on top of the mandatory sha256 checksum: an unusable verifier warns and continues, while a genuine provenance mismatch still fails the install. Set `GGSHIELD_REQUIRE_ATTESTATION=1` to treat any attestation problem as fatal.

scripts/install/install.sh

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,9 @@ asset_http_status() {
153153
2>/dev/null || echo 000
154154
}
155155

156-
# Fail closed: a download we cannot verify is never installed.
156+
# The sha256 check is mandatory and fails closed: a download whose checksum we
157+
# cannot confirm is never installed. The provenance attestation layered on top
158+
# is best-effort — see verify_attestation.
157159
verify_download() {
158160
local file="$1" asset="$2" digest sum_tool
159161
digest=$(asset_digest "$asset")
@@ -171,14 +173,57 @@ verify_download() {
171173
echo "${digest#sha256:} $file" | $sum_tool -c - >/dev/null ||
172174
die "checksum mismatch for $asset"
173175

174-
# older gh releases have no `attestation` subcommand
175-
if command -v gh >/dev/null 2>&1 &&
176-
gh attestation --help >/dev/null 2>&1 &&
177-
gh auth status >/dev/null 2>&1; then
178-
say "Verifying build provenance attestation"
179-
gh attestation verify "$file" --repo "$GITHUB_REPO" >/dev/null ||
180-
die "artifact attestation verification failed for $asset"
176+
verify_attestation "$file" "$asset"
177+
}
178+
179+
# Best-effort build-provenance check, on top of the mandatory sha256. A verifier
180+
# that is merely *unusable* — gh too old to parse Sigstore's trusted root
181+
# (END-609: "unsupported tlog public key type: PKIX_ED25519"), absent,
182+
# rate-limited or offline — must not block an install whose checksum already
183+
# matched; only a genuine provenance MISMATCH fails closed. gh has no exit code
184+
# for that distinction, so we classify its output. GGSHIELD_REQUIRE_ATTESTATION=1
185+
# makes any attestation problem fatal.
186+
verify_attestation() {
187+
local file="$1" asset="$2" out msg rc=0
188+
189+
# gh, or its `attestation` subcommand, may be absent — provenance is optional.
190+
if ! command -v gh >/dev/null 2>&1 || ! gh attestation --help >/dev/null 2>&1; then
191+
return 0
192+
fi
193+
194+
say "Verifying build provenance attestation"
195+
# No `gh auth status` gate: verifying a PUBLIC repo's attestation needs no
196+
# login (confirmed in END-609); gh uses a token when present, anonymous API
197+
# otherwise. '|| rc=$?' stops `set -e` aborting us on a non-zero exit.
198+
out=$(gh attestation verify "$file" --repo "$GITHUB_REPO" 2>&1) || rc=$?
199+
[ "$rc" -eq 0 ] && return 0
200+
201+
if [ "${GGSHIELD_REQUIRE_ATTESTATION:-0}" = 1 ]; then
202+
die "artifact attestation verification failed for $asset:
203+
$out"
181204
fi
205+
206+
msg=$(printf '%s\n' "$out" | grep -m1 -iE 'error|failed|unsupported' |
207+
sed 's/^[[:space:]]*//') || true
208+
case "$out" in
209+
*"unsupported tlog public key type"* | *"trusted root"* | \
210+
*"Public Good Sigstore verifier"* | *"unknown command"* | *"unknown flag"* | \
211+
*"no attestations found"* | *"HTTP 401"* | *"HTTP 403"* | *"HTTP 404"* | \
212+
*"HTTP 5"* | *"rate limit"* | *"timeout"* | \
213+
*"connection refused"* | *"could not resolve"* | *"TLS"*)
214+
# Verifier unusable or transient failure — fall open onto the checksum.
215+
warn "could not check build provenance: ${msg:-gh attestation verify failed}"
216+
warn " the sha256 checksum already matched, so the download is intact."
217+
warn " for the provenance check too, upgrade gh to >= 2.56.0 (e.g. 'brew upgrade gh')."
218+
warn " (set GGSHIELD_REQUIRE_ATTESTATION=1 to make this fatal instead.)"
219+
return 0
220+
;;
221+
*)
222+
# Anything else is a real provenance rejection: fail closed.
223+
die "build provenance verification FAILED for $asset — does not match $GITHUB_REPO:
224+
$out"
225+
;;
226+
esac
182227
}
183228

184229
install_tarball() {

0 commit comments

Comments
 (0)