From 18ecc2244fe84f581b51977e099a78be2f26a03d Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Thu, 27 Aug 2026 06:05:42 -0400 Subject: [PATCH] Fix: Isolate installer GPG inspection --- setup/lib/artifact-provenance | 28 ++++++++++++++++--------- tests/setup/package-provenance-contract | 21 ++++++++++++++++--- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/setup/lib/artifact-provenance b/setup/lib/artifact-provenance index 5638423..62776ca 100755 --- a/setup/lib/artifact-provenance +++ b/setup/lib/artifact-provenance @@ -6,10 +6,16 @@ declare -gA INSTALLER_PROVENANCE=() -_primary_key_fingerprints() { - gpg --batch --with-colons --import-options show-only --import "$1" 2>/dev/null \ +_primary_key_fingerprints() ( + local home + home="$(mktemp -d)" || exit 1 + chmod 700 "$home" + trap 'rm -rf -- "$home"' EXIT + trap 'exit 130' INT + trap 'exit 143' TERM + GNUPGHOME="$home" gpg --batch --with-colons --import-options show-only --import "$1" 2>/dev/null \ | awk -F: '$1 == "pub" { primary = 1; next } primary && $1 == "fpr" { print $10; primary = 0 }' -} +) key_fingerprint_matches() { local file="$1" expected="$2" @@ -27,13 +33,15 @@ _key_has_one_primary() { verify_detached_signature() { local key="$1" signature="$2" content="$3" home _key_has_one_primary "$key" || return 1 - home="$(mktemp -d)" || return 1 - chmod 700 "$home" - GNUPGHOME="$home" gpg --batch --quiet --import "$key" >/dev/null 2>&1 \ - && GNUPGHOME="$home" gpg --batch --verify "$signature" "$content" >/dev/null 2>&1 - local status=$? - rm -rf -- "$home" - return "$status" + ( + home="$(mktemp -d)" || exit 1 + chmod 700 "$home" + trap 'rm -rf -- "$home"' EXIT + trap 'exit 130' INT + trap 'exit 143' TERM + GNUPGHOME="$home" gpg --batch --quiet --import "$key" >/dev/null 2>&1 \ + && GNUPGHOME="$home" gpg --batch --verify "$signature" "$content" >/dev/null 2>&1 + ) } download_sha256() { diff --git a/tests/setup/package-provenance-contract b/tests/setup/package-provenance-contract index 813a60f..c0f68a6 100755 --- a/tests/setup/package-provenance-contract +++ b/tests/setup/package-provenance-contract @@ -10,6 +10,7 @@ config="$repo_dir/setup/provenance/installers.conf" test_tmp="$(mktemp -d)" host_gnupg="${GNUPGHOME:-$HOME/.gnupg}" host_rpmdb="/usr/lib/sysimage/rpm/rpmdb.sqlite" +ambient_gnupg="$test_tmp/fresh-ambient-gnupg" cleanup() { rm -rf -- "$test_tmp" @@ -55,6 +56,14 @@ snapshot_gpg_state() { done < <(find "$path" -maxdepth 1 -type f \( -name pubring.kbx -o -name trustdb.gpg \) | sort) } +export GNUPGHOME="$ambient_gnupg" +mkdir -m 700 "$ambient_gnupg" +ambient_before="$(snapshot "$ambient_gnupg")" +ambient_gpg_files_before="$(snapshot_gpg_state "$ambient_gnupg")" +before_gnupg="$(snapshot "$host_gnupg")" +before_gpg_files="$(snapshot_gpg_state "$host_gnupg")" +before_rpmdb="$(snapshot "$host_rpmdb")" + # This must be the only production file sourced by the contract. # shellcheck source=../../setup/lib/artifact-provenance source "$repo_dir/setup/lib/artifact-provenance" @@ -70,6 +79,10 @@ base64 --decode "$fixtures/wrong-signer-fixture.rpm.base64" > "$test_tmp/wrong-s || fail 'tampered artifact does not differ by exactly one byte' expect_success key_fingerprint_matches "$fixtures/fixture-key.asc" "$fixture_fingerprint" +[[ "$ambient_before" == "$(snapshot "$ambient_gnupg")" ]] \ + || fail 'fingerprint inspection created ambient GPG state' +[[ "$ambient_gpg_files_before" == "$(snapshot_gpg_state "$ambient_gnupg")" ]] \ + || fail 'fingerprint inspection created an ambient GPG keybox or trust database' expect_failure key_fingerprint_matches "$fixtures/fixture-key.asc" '0000000000000000000000000000000000000000' cat "$fixtures/fixture-key.asc" "$fixtures/wrong-signer-key.asc" > "$test_tmp/combined-key.asc" expect_failure key_fingerprint_matches "$test_tmp/combined-key.asc" "$fixture_fingerprint" @@ -78,6 +91,8 @@ expect_success verify_detached_signature \ "$fixtures/fixture-key.asc" "$fixtures/SHASUMS256.txt.asc" "$fixtures/SHASUMS256.txt" expect_failure verify_detached_signature \ "$fixtures/fixture-key.asc" "$fixtures/SHASUMS256.txt.asc" "$fixtures/tiny-artifact" +expect_failure verify_detached_signature \ + "$fixtures/fixture-key.asc" "$fixtures/SHASUMS256.txt.asc" "$fixtures/tiny-artifact-tampered" cp "$fixtures/SHASUMS256.txt.asc" "$test_tmp/bad-signature.asc" sed -i 's/^=MJqv$/=MJqa/' "$test_tmp/bad-signature.asc" expect_failure verify_detached_signature \ @@ -159,9 +174,6 @@ expect_failure download_sha256 'https://fixture.invalid/interrupted' "$tiny_sha2 assert_file_bytes "$destination" 'known-good' [[ ! -e "$destination.part" ]] || fail 'interrupted download left a part file' -before_gnupg="$(snapshot "$host_gnupg")" -before_gpg_files="$(snapshot_gpg_state "$host_gnupg")" -before_rpmdb="$(snapshot "$host_rpmdb")" expect_success rpm_signature_matches \ "$test_tmp/signed-fixture.rpm" "$fixtures/fixture-key.asc" "$fixture_fingerprint" expect_failure rpm_signature_matches \ @@ -175,6 +187,9 @@ expect_failure rpm_signature_matches \ [[ "$before_gnupg" == "$(snapshot "$host_gnupg")" ]] || fail 'host GPG state changed' [[ "$before_gpg_files" == "$(snapshot_gpg_state "$host_gnupg")" ]] || fail 'host GPG keybox or trust database changed' [[ "$before_rpmdb" == "$(snapshot "$host_rpmdb")" ]] || fail 'host RPM database changed' +[[ "$ambient_before" == "$(snapshot "$ambient_gnupg")" ]] || fail 'production helper created ambient GPG state' +[[ "$ambient_gpg_files_before" == "$(snapshot_gpg_state "$ambient_gnupg")" ]] \ + || fail 'production helper created an ambient GPG keybox or trust database' expect_success load_installer_provenance "$config" [[ "${INSTALLER_PROVENANCE[BUN_VERSION]:-}" == '1.4.0' ]] || fail 'valid provenance was not loaded'