Fix: Verify third-party package repositories

This commit is contained in:
Gabriel Brown
2026-08-27 06:27:35 -04:00
parent 3f8539176a
commit 4abf3f8acd
2 changed files with 792 additions and 64 deletions
+460
View File
@@ -74,6 +74,18 @@ snapshot_gpg_state() {
} | sha256sum | awk '{ print $1 }'
}
snapshot_file_state() {
local path="$1"
if [[ -f "$path" ]]; then
printf 'file:%s:%s\n' "$(stat -c '%a:%s:%Y:%Z' "$path")" \
"$(sha256sum "$path" | awk '{ print $1 }')"
elif [[ -L "$path" ]]; then
printf 'symlink:%s\n' "$(readlink -- "$path")"
else
printf 'absent\n'
fi
}
export GNUPGHOME="$ambient_gnupg"
mkdir -m 700 "$ambient_gnupg"
printf 'baseline-value\n' > "$ambient_gnupg/unexpected-entry"
@@ -87,6 +99,11 @@ 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")"
before_repo_files="$(snapshot_gpg_state /etc/yum.repos.d)"
before_rpm_key_files="$(snapshot_gpg_state /etc/pki/rpm-gpg)"
before_system_flatpak="$(snapshot_file_state /var/lib/flatpak/repo/config)"
before_user_flatpak="$(snapshot_file_state "$HOME/.local/share/flatpak/repo/config")"
before_bashrc="$(snapshot_file_state "$HOME/.bashrc")"
# This must be the only production file sourced by the contract.
# shellcheck source=../../setup/lib/artifact-provenance
@@ -255,4 +272,447 @@ cp "$config" "$parser_fixture"
printf 'BUN_ARMV7_URL=https://fixture.invalid/bun\n' >> "$parser_fixture"
expect_failure load_installer_provenance "$parser_fixture"
# Repository setup runs from a fixture copy of the installer with every
# external command replaced. A contract failure can therefore inspect exact
# ordering and staged bytes without consulting or changing the host.
installer_fixture="$test_tmp/installer-fixture"
mkdir -p "$installer_fixture/setup/lib" "$installer_fixture/setup/provenance/keys" \
"$installer_fixture/setup/scripts"
cp "$repo_dir/setup/lib/artifact-provenance" "$repo_dir/setup/lib/extras-catalog" \
"$repo_dir/setup/lib/machine-role" "$installer_fixture/setup/lib/"
cp "$config" "$installer_fixture/setup/provenance/installers.conf"
cp "$repo_dir"/setup/provenance/keys/*.asc "$installer_fixture/setup/provenance/keys/"
sed '/^# --- The server path/,$d' "$repo_dir/setup/scripts/install-packages" \
> "$installer_fixture/setup/scripts/install-packages"
make_stub_commands() {
local case_root="$1"
mkdir -p "$case_root/bin" "$case_root/home" "$case_root/tmp" "$case_root/etc/yum.repos.d" \
"$case_root/etc/pki/rpm-gpg"
cat > "$case_root/bin/rpm" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
if [[ "$*" == '-E %fedora' ]]; then
printf 'rpm:release\n' >> "$COMMAND_LOG"
printf '%s\n' "${STUB_FEDORA_RELEASE:-44}"
elif [[ "${1:-}" == -q ]]; then
printf 'rpm:query:%s\n' "${2:-}" >> "$COMMAND_LOG"
case "${2:-}" in
terra-release) [[ "${STUB_TERRA_INSTALLED:-0}" == 1 ]] ;;
claude-desktop-extra) [[ "${STUB_CLAUDE_DESKTOP_INSTALLED:-0}" == 1 ]] ;;
*) exit 1 ;;
esac
else
exit 64
fi
STUB
cat > "$case_root/bin/curl" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
output='' max_filesize='' connect_timeout='' max_time='' url=''
while (($#)); do
case "$1" in
--output) output="$2"; shift 2 ;;
--max-filesize) max_filesize="$2"; shift 2 ;;
--connect-timeout) connect_timeout="$2"; shift 2 ;;
--max-time) max_time="$2"; shift 2 ;;
--fail|--location) shift ;;
*) url="$1"; shift ;;
esac
done
[[ -n "$output" && "$connect_timeout" == 10 && "$max_time" == 600 ]] || exit 65
output_name="$(basename "$output")"
output_name="${output_name#.}"
output_name="${output_name%.part.*}"
printf 'curl:%s:max=%s:output=%s\n' "$url" "$max_filesize" "$output_name" >> "$COMMAND_LOG"
case "$url" in
*rpmfusion-free*) cp "$SIGNED_RPM" "$output" ;;
*rpmfusion-nonfree*) cp "$SIGNED_RPM" "$output" ;;
*flathub.flatpakrepo) cp "$FLATHUB_DESCRIPTOR" "$output" ;;
*) exit 66 ;;
esac
STUB
cat > "$case_root/bin/gpg" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
key="${!#}"
fingerprint=''
for candidate in "$REVIEWED_KEYS"/*.asc; do
if cmp -s "$key" "$candidate"; then
case "$(basename "$candidate")" in
terra44.asc) fingerprint='AE09157A4DE88B497EA1D5D300CDAB43DE226D6F' ;;
claude-code.asc) fingerprint='31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE' ;;
rpmfusion-free.asc) fingerprint='E9A491A3DE247814E7E067EAE06F8ECDD651FF2E' ;;
rpmfusion-nonfree.asc) fingerprint='79BDB88F9BBF73910FD4095B6A2AF96194843C65' ;;
hyprland-copr.asc) fingerprint='97E23476C89635135407C7D5E9BA41342C4B2995' ;;
flathub.asc) fingerprint='6E5C05D979C76DAF93C081354184DD4D907A7CAE' ;;
claude-desktop.asc) fingerprint='825A7D15D78BABE45646D5DF382409F597908867' ;;
esac
break
fi
done
[[ -n "$fingerprint" ]] || exit 1
printf 'gpg:fingerprint:%s\n' "$fingerprint" >> "$COMMAND_LOG"
printf 'pub:-:4096:1:0000000000000000:0:0::-:::scESC::::::23::0:\n'
printf 'fpr:::::::::%s:\n' "$fingerprint"
STUB
cat > "$case_root/bin/rpmkeys" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
action=''
package=''
while (($#)); do
case "$1" in
--dbpath) shift 2 ;;
--import) action=import; package="$2"; shift 2 ;;
--checksig) action=checksig; shift; [[ "${1:-}" == --verbose ]] && shift; package="$1"; shift ;;
*) shift ;;
esac
done
printf 'rpmkeys:%s:%s\n' "$action" "$(basename "$package")" >> "$COMMAND_LOG"
if [[ "$action" == checksig ]]; then
[[ "${STUB_RPM_SIGNATURE_FAIL:-}" != "$(basename "$package")" ]] || exit 1
printf 'Header OpenPGP signature: OK\n'
fi
STUB
cat > "$case_root/bin/sudo" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
if [[ "${1:-}" == install ]]; then
shift
[[ "${1:-}" == -m && "${2:-}" == 0644 ]] || exit 67
source_file="$3"
destination="$4"
printf 'sudo:install:%s:%s\n' "$(basename "$source_file")" "$destination" >> "$COMMAND_LOG"
mapped="$STUB_ETC${destination#/etc}"
mkdir -p "$(dirname "$mapped")"
/usr/bin/install -m 0644 "$source_file" "$mapped"
exit 0
fi
original="$*"
logged=()
for argument in "$@"; do
if [[ "$argument" == --gpg-import=*/flathub-key.asc ]]; then
logged+=(--gpg-import=FLATHUB_KEY)
continue
fi
case "$(basename "$argument")" in
rpmfusion-free-release.rpm) logged+=(RPMFUSION_FREE) ;;
rpmfusion-nonfree-release.rpm) logged+=(RPMFUSION_NONFREE) ;;
flathub-key.asc) logged+=(FLATHUB_KEY) ;;
*) logged+=("$argument") ;;
esac
done
printf 'sudo:%s\n' "${logged[*]}" >> "$COMMAND_LOG"
if [[ -n "${STUB_DNF_FAIL_MATCH:-}" && "$original" == *"$STUB_DNF_FAIL_MATCH"* ]]; then
exit 68
fi
if [[ "${1:-}" == flatpak && "${2:-}" == remote-add ]]; then
printf 'mutated\n' > "$STUB_FLATPAK_STATE"
fi
STUB
for command in dnf flatpak; do
cat > "$case_root/bin/$command" <<'STUB'
#!/usr/bin/env bash
exit 69
STUB
done
chmod +x "$case_root/bin"/*
}
reset_installer_fixture() {
cp "$config" "$installer_fixture/setup/provenance/installers.conf"
cp "$repo_dir"/setup/provenance/keys/*.asc "$installer_fixture/setup/provenance/keys/"
}
write_flathub_descriptor() {
local destination="$1" key="$2" verify_line="${3:-}" url="${4:-https://dl.flathub.org/repo/}"
local encoded
encoded="$(base64 -w 0 "$key")"
printf '[Flatpak Repo]\nTitle=Flathub\nUrl=%s\nGPGKey=%s\n%s\n' \
"$url" "$encoded" "$verify_line" > "$destination"
}
run_installer_function() {
local name="$1" function_name="$2" case_root
case_root="$test_tmp/cases/$name"
rm -rf -- "$case_root"
make_stub_commands "$case_root"
: > "$case_root/commands.log"
printf 'preserved\n' > "$case_root/flatpak-state"
write_flathub_descriptor "$case_root/flathub.flatpakrepo" \
"${STUB_FLATHUB_KEY_FILE:-$installer_fixture/setup/provenance/keys/flathub.asc}" \
"${STUB_FLATHUB_VERIFY_LINE:-}" "${STUB_FLATHUB_URL:-https://dl.flathub.org/repo/}"
if [[ "${STUB_EXISTING_REPOSITORY:-}" == hyprland ]]; then
printf 'known key\n' > "$case_root/etc/pki/rpm-gpg/RPM-GPG-KEY-panama-hyprland"
printf 'known repo\n' > "$case_root/etc/yum.repos.d/panama-hyprland.repo"
fi
case "${STUB_CLAUDE_DESKTOP_REPO_MODE:-absent}" in
trusted)
cp "$installer_fixture/setup/provenance/keys/claude-desktop.asc" \
"$case_root/etc/pki/rpm-gpg/claude-desktop-local.asc"
printf '[claude-desktop]\nbaseurl=https://patrickjaja.github.io/claude-desktop-extra/rpm/\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=file://%s\n' \
"$case_root/etc/pki/rpm-gpg/claude-desktop-local.asc" \
> "$case_root/etc/yum.repos.d/claude-desktop.repo"
;;
untrusted)
cp "$installer_fixture/setup/provenance/keys/claude-desktop.asc" \
"$case_root/etc/pki/rpm-gpg/claude-desktop-local.asc"
printf '[claude-desktop]\nbaseurl=https://evil.invalid/rpm/\nenabled=1\ngpgcheck=0\nrepo_gpgcheck=0\ngpgkey=file://%s\n' \
"$case_root/etc/pki/rpm-gpg/claude-desktop-local.asc" \
> "$case_root/etc/yum.repos.d/claude-desktop.repo"
;;
esac
COMMAND_LOG="$case_root/commands.log" \
FIXTURE_ROOT="$installer_fixture" \
REVIEWED_KEYS="$repo_dir/setup/provenance/keys" \
SIGNED_RPM="$test_tmp/signed-fixture.rpm" \
FLATHUB_DESCRIPTOR="$case_root/flathub.flatpakrepo" \
STUB_ETC="$case_root/etc" \
STUB_FLATPAK_STATE="$case_root/flatpak-state" \
HOME="$case_root/home" \
TMPDIR="$case_root/tmp" \
PANAMA_PATH="$installer_fixture" \
PATH="$case_root/bin:/usr/bin:/bin" \
bash -c 'source "$PANAMA_PATH/setup/scripts/install-packages"; PANAMA_SYSTEM_ETC="$STUB_ETC"; declare -F "$1" >/dev/null; "$1"' \
bash "$function_name" > "$case_root/output" 2>&1
}
assert_log() {
local name="$1" expected="$2" path
path="$test_tmp/cases/$name/commands.log"
[[ "$(<"$path")" == "$expected" ]] || {
printf 'package provenance contract: unexpected %s command log\n' "$name" >&2
diff -u <(printf '%s\n' "$expected") "$path" >&2 || true
exit 1
}
}
reset_installer_fixture
expect_success run_installer_function rpmfusion install_rpmfusion_repositories
assert_log rpmfusion "$(cat <<'EXPECTED'
rpm:release
curl:https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-44.noarch.rpm:max=4194304:output=rpmfusion-free-release.rpm
gpg:fingerprint:E9A491A3DE247814E7E067EAE06F8ECDD651FF2E
rpmkeys:import:rpmfusion-free.asc
rpmkeys:checksig:rpmfusion-free-release.rpm
curl:https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-44.noarch.rpm:max=4194304:output=rpmfusion-nonfree-release.rpm
gpg:fingerprint:79BDB88F9BBF73910FD4095B6A2AF96194843C65
rpmkeys:import:rpmfusion-nonfree.asc
rpmkeys:checksig:rpmfusion-nonfree-release.rpm
sudo:dnf install -y --setopt=localpkg_gpgcheck=1 RPMFUSION_FREE RPMFUSION_NONFREE
EXPECTED
)"
reset_installer_fixture
expect_success run_installer_function terra install_terra_repository
assert_log terra "$(cat <<'EXPECTED'
rpm:release
rpm:query:terra-release
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
sudo:install:terra44.asc:/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama
sudo:dnf install -y --repofrompath terra,https://repos.fyralabs.com/terra44 --setopt=terra.pkg_gpgcheck=1 --setopt=terra.repo_gpgcheck=1 --setopt=terra.gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama terra-release
EXPECTED
)"
cmp -s "$installer_fixture/setup/provenance/keys/terra44.asc" \
"$test_tmp/cases/terra/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama" \
|| fail 'Terra privileged install did not preserve the fully staged reviewed key'
reset_installer_fixture
expect_success run_installer_function hyprland configure_hyprland_repository
assert_log hyprland "$(cat <<'EXPECTED'
rpm:release
gpg:fingerprint:97E23476C89635135407C7D5E9BA41342C4B2995
sudo:install:hyprland-copr.asc:/etc/pki/rpm-gpg/RPM-GPG-KEY-panama-hyprland
sudo:install:panama-hyprland.repo:/etc/yum.repos.d/panama-hyprland.repo
EXPECTED
)"
cmp -s "$installer_fixture/setup/provenance/keys/hyprland-copr.asc" \
"$test_tmp/cases/hyprland/etc/pki/rpm-gpg/RPM-GPG-KEY-panama-hyprland" \
|| fail 'Hyprland privileged install did not preserve the fully staged reviewed key'
assert_file_bytes "$test_tmp/cases/hyprland/etc/yum.repos.d/panama-hyprland.repo" "$(cat <<'EXPECTED'
[panama-hyprland]
name=Panama reviewed Hyprland COPR
baseurl=https://download.copr.fedorainfracloud.org/results/lionheartp/Hyprland/fedora-$releasever-$basearch/
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-panama-hyprland
EXPECTED
)"
reset_installer_fixture
expect_success run_installer_function flathub ensure_flathub_remote
assert_log flathub "$(cat <<'EXPECTED'
rpm:release
curl:https://flathub.org/repo/flathub.flatpakrepo:max=1048576:output=flathub.flatpakrepo
gpg:fingerprint:6E5C05D979C76DAF93C081354184DD4D907A7CAE
sudo:flatpak remote-add --if-not-exists --gpg-import=FLATHUB_KEY flathub https://dl.flathub.org/repo/
EXPECTED
)"
reset_installer_fixture
expect_success run_installer_function claude-code install_claude_code
assert_log claude-code "$(cat <<'EXPECTED'
rpm:release
gpg:fingerprint:31DDDE24DDFAB679F42D7BD2BAA929FF1A7ECACE
sudo:install:claude-code.asc:/etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama
sudo:install:claude-code.repo:/etc/yum.repos.d/claude-code.repo
sudo:dnf install -y claude-code
EXPECTED
)"
cmp -s "$installer_fixture/setup/provenance/keys/claude-code.asc" \
"$test_tmp/cases/claude-code/etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama" \
|| fail 'Claude Code privileged install did not preserve the fully staged reviewed key'
assert_file_bytes "$test_tmp/cases/claude-code/etc/yum.repos.d/claude-code.repo" "$(cat <<'EXPECTED'
[claude-code]
name=Claude Code
baseurl=https://downloads.claude.ai/claude-code/rpm/stable
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama
EXPECTED
)"
reset_installer_fixture
expect_success run_installer_function claude-desktop-absent install_claude_desktop_if_trusted
assert_log claude-desktop-absent 'rpm:release'
[[ "$(grep -c 'Claude Desktop is optional; configure its reviewed local-key repository manually' \
"$test_tmp/cases/claude-desktop-absent/output")" -eq 1 ]] \
|| fail 'absent Claude Desktop repository did not produce exactly one manual message'
reset_installer_fixture
STUB_CLAUDE_DESKTOP_REPO_MODE=trusted \
expect_success run_installer_function claude-desktop-trusted install_claude_desktop_if_trusted
assert_log claude-desktop-trusted "$(cat <<'EXPECTED'
rpm:release
gpg:fingerprint:825A7D15D78BABE45646D5DF382409F597908867
gpg:fingerprint:825A7D15D78BABE45646D5DF382409F597908867
sudo:dnf install -y claude-desktop-extra
EXPECTED
)"
# A Fedora version outside the reviewed policy stops every public transaction
# before curl, sudo, Flatpak, or repository inspection can act.
for function_name in install_rpmfusion_repositories install_terra_repository \
configure_hyprland_repository ensure_flathub_remote install_claude_code \
install_claude_desktop_if_trusted; do
reset_installer_fixture
name="wrong-fedora-${function_name}"
STUB_FEDORA_RELEASE=45 expect_failure run_installer_function "$name" "$function_name"
assert_log "$name" 'rpm:release'
done
reset_installer_fixture
sed -i 's#^RPMFUSION_FREE_RELEASE_URL=.*#RPMFUSION_FREE_RELEASE_URL=https://evil.invalid/free.rpm#' \
"$installer_fixture/setup/provenance/installers.conf"
expect_failure run_installer_function rpmfusion-wrong-url install_rpmfusion_repositories
assert_log rpmfusion-wrong-url 'rpm:release'
for policy_case in \
'terra-wrong-url TERRA_BASEURL install_terra_repository' \
'hyprland-wrong-url HYPRLAND_COPR_BASEURL configure_hyprland_repository' \
'flathub-wrong-url FLATHUB_DESCRIPTOR_URL ensure_flathub_remote' \
'claude-code-wrong-url CLAUDE_CODE_BASEURL install_claude_code' \
'claude-desktop-wrong-url CLAUDE_DESKTOP_BASEURL install_claude_desktop_if_trusted'; do
read -r name config_name function_name <<<"$policy_case"
reset_installer_fixture
sed -i "s#^${config_name}=.*#${config_name}=https://evil.invalid/#" \
"$installer_fixture/setup/provenance/installers.conf"
expect_failure run_installer_function "$name" "$function_name"
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'curl:'* \
&& "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:'* ]] \
|| fail "$config_name mismatch reached a download or mutation"
done
reset_installer_fixture
cp "$installer_fixture/setup/provenance/keys/terra44.asc" \
"$installer_fixture/setup/provenance/keys/rpmfusion-free.asc"
expect_failure run_installer_function rpmfusion-wrong-key install_rpmfusion_repositories
assert_log rpmfusion-wrong-key "$(cat <<'EXPECTED'
rpm:release
curl:https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-44.noarch.rpm:max=4194304:output=rpmfusion-free-release.rpm
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
EXPECTED
)"
reset_installer_fixture
STUB_RPM_SIGNATURE_FAIL=rpmfusion-free-release.rpm \
expect_failure run_installer_function rpmfusion-bad-signature install_rpmfusion_repositories
[[ "$(<"$test_tmp/cases/rpmfusion-bad-signature/commands.log")" != *'rpmfusion-nonfree'* ]] \
|| fail 'RPM Fusion signature failure did not stop the dependent download'
[[ "$(<"$test_tmp/cases/rpmfusion-bad-signature/commands.log")" != *'sudo:'* ]] \
|| fail 'RPM Fusion signature failure reached a privileged mutation'
reset_installer_fixture
cp "$installer_fixture/setup/provenance/keys/terra44.asc" \
"$installer_fixture/setup/provenance/keys/hyprland-copr.asc"
STUB_EXISTING_REPOSITORY=hyprland \
expect_failure run_installer_function hyprland-wrong-key configure_hyprland_repository
assert_file_bytes "$test_tmp/cases/hyprland-wrong-key/etc/pki/rpm-gpg/RPM-GPG-KEY-panama-hyprland" \
'known key'
assert_file_bytes "$test_tmp/cases/hyprland-wrong-key/etc/yum.repos.d/panama-hyprland.repo" \
'known repo'
[[ "$(<"$test_tmp/cases/hyprland-wrong-key/commands.log")" != *'sudo:'* ]] \
|| fail 'Hyprland key mismatch replaced known-good repository files'
reset_installer_fixture
STUB_FLATHUB_VERIFY_LINE='NoGPGVerify=true' \
expect_failure run_installer_function flathub-no-gpg ensure_flathub_remote
assert_file_bytes "$test_tmp/cases/flathub-no-gpg/flatpak-state" 'preserved'
[[ "$(<"$test_tmp/cases/flathub-no-gpg/commands.log")" != *'sudo:'* ]] \
|| fail 'Flathub disabled-GPG descriptor mutated a remote'
reset_installer_fixture
STUB_FLATHUB_KEY_FILE="$installer_fixture/setup/provenance/keys/terra44.asc" \
expect_failure run_installer_function flathub-wrong-key ensure_flathub_remote
assert_file_bytes "$test_tmp/cases/flathub-wrong-key/flatpak-state" 'preserved'
[[ "$(<"$test_tmp/cases/flathub-wrong-key/commands.log")" != *'sudo:'* ]] \
|| fail 'Flathub key mismatch mutated an existing remote'
reset_installer_fixture
STUB_FLATHUB_URL='https://evil.invalid/repo/' \
expect_failure run_installer_function flathub-wrong-repo-url ensure_flathub_remote
assert_file_bytes "$test_tmp/cases/flathub-wrong-repo-url/flatpak-state" 'preserved'
[[ "$(<"$test_tmp/cases/flathub-wrong-repo-url/commands.log")" != *'sudo:'* ]] \
|| fail 'Flathub repository URL mismatch mutated an existing remote'
reset_installer_fixture
STUB_CLAUDE_DESKTOP_REPO_MODE=untrusted \
expect_success run_installer_function claude-desktop-untrusted install_claude_desktop_if_trusted
assert_log claude-desktop-untrusted 'rpm:release'
[[ "$(grep -c 'Claude Desktop is optional; configure its reviewed local-key repository manually' \
"$test_tmp/cases/claude-desktop-untrusted/output")" -eq 1 ]] \
|| fail 'untrusted Claude Desktop repository did not produce one manual message'
reset_installer_fixture
STUB_DNF_FAIL_MATCH=terra-release expect_failure run_installer_function terra-dnf-failure install_terra_repository
[[ "$(tail -n 1 "$test_tmp/cases/terra-dnf-failure/commands.log")" == *'terra-release' ]] \
|| fail 'Terra DNF failure ran a later transaction command'
[[ -z "$(find "$test_tmp/cases/terra-dnf-failure/tmp" -mindepth 1 -print -quit)" ]] \
|| fail 'Terra DNF failure left private staging files behind'
[[ "$before_gnupg" == "$(snapshot "$host_gnupg")" ]] || fail 'repository cases changed host GPG state'
[[ "$before_gpg_files" == "$(snapshot_gpg_state "$host_gnupg")" ]] \
|| fail 'repository cases changed host GPG files'
[[ "$before_rpmdb" == "$(snapshot "$host_rpmdb")" ]] || fail 'repository cases changed host RPM database'
[[ "$before_repo_files" == "$(snapshot_gpg_state /etc/yum.repos.d)" ]] \
|| fail 'repository cases changed host repository files'
[[ "$before_rpm_key_files" == "$(snapshot_gpg_state /etc/pki/rpm-gpg)" ]] \
|| fail 'repository cases changed host RPM key files'
[[ "$before_system_flatpak" == "$(snapshot_file_state /var/lib/flatpak/repo/config)" ]] \
|| fail 'repository cases changed the system Flatpak remote'
[[ "$before_user_flatpak" == "$(snapshot_file_state "$HOME/.local/share/flatpak/repo/config")" ]] \
|| fail 'repository cases changed the user Flatpak remote'
[[ "$before_bashrc" == "$(snapshot_file_state "$HOME/.bashrc")" ]] \
|| fail 'repository cases changed the protected bashrc'
printf 'package provenance contract: PASS\n'