Files
Panama/tests/setup/package-provenance-contract
Gabriel Brown fa8b14e05e Fix: Adopt a Terra the machine already trusts
The repository audit made any Terra that is not Panama's own pinned form a
trust-root failure, and status 78 then stopped every stage before it ran. A
machine that installed Terra the way Terra documents it -- terra-release's own
repo file, a metalink, the key at its stock path -- was classified hostile and
had no way back, because install_terra_repository refused to touch a machine
terra-release had already reached. A gate with no door.

The trust root is the signing key, and that key is byte-for-byte the
fingerprint this repository reviewed and pinned, with every signature check
already on. So verify the fingerprint and adopt the configuration into the
pinned form instead of refusing it. Adoption needs no network and no DNF, it
runs before any other transaction in the stage, and it is repeatable, which it
has to be: terra-release owns that file and restores it on update.

Adoption stays narrow. The pinned fingerprint must match both the reviewed key
and the key the machine actually verifies against, the gpgkey must be a local
file under the system trust directory, and the endpoint must be one Terra
itself serves -- so the reviewed baseurl or the reviewed metalink host, now
pinned as TERRA_METALINK_BASEURL. An unknown key, a redirected baseurl, a
second enabled Terra, or a disabled signature check is still a hard refusal.

A refusal also stops less than it did. It suppresses the stages that open DNF
and the migrations, which may run a transaction of their own, and the run still
exits 78. It no longer stops link-dotfiles, link-skills or link-user, which
read no repository and install no package. Exiting before them is what left
this laptop with a stale ~/.claude/skills and no shipped skill reachable.

Also stub ensure_flathub_remote in the extras contract, which has been failing
since that call was added to install_extra_category without one.

Claude-Session: https://claude.ai/code/session_01PeTrG9dGY89UWuhGm4Pr1s
2026-08-28 14:53:48 -04:00

2069 lines
96 KiB
Bash
Executable File

#!/usr/bin/env bash
# The production change that breaks these cases is accepting an unverified
# download, a wrong signer, or executable/malformed provenance data.
set -euo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
fixtures="$repo_dir/tests/setup/fixtures/provenance"
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"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
fail() {
printf 'package provenance contract: %s\n' "$*" >&2
exit 1
}
expect_success() {
"$@" || fail "expected success: $*"
}
expect_failure() {
if "$@"; then
fail "expected failure: $*"
fi
}
assert_file_bytes() {
local path="$1" expected="$2"
[[ "$(<"$path")" == "$expected" ]] || fail "unexpected bytes in $path"
}
snapshot() {
local path="$1"
if [[ -e "$path" ]]; then
stat -c '%i:%s:%Y:%Z' "$path"
else
printf 'absent'
fi
}
snapshot_gpg_state() {
local path="$1" file relative
[[ -d "$path" ]] || {
printf 'absent\n'
return 0
}
{
printf 'directory\0'
find "$path" -mindepth 1 -print0 | LC_ALL=C sort -z \
| while IFS= read -r -d '' file; do
relative="${file#"$path"/}"
printf '%s\0' "$relative"
if [[ -L "$file" ]]; then
printf 'symlink\0%s\0' "$(readlink -- "$file")"
elif [[ -f "$file" ]]; then
printf 'file\0%s\0' "$(sha256sum -- "$file" | awk '{ print $1 }')"
elif [[ -d "$file" ]]; then
printf 'directory\0'
else
printf 'other\0%s\0' "$(stat -c '%F:%a:%s:%Y:%Z' "$file")"
fi
done
} | 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"
ambient_before="$(snapshot "$ambient_gnupg")"
ambient_gpg_files_before="$(snapshot_gpg_state "$ambient_gnupg")"
printf 'tampered-value\n' > "$ambient_gnupg/unexpected-entry"
[[ "$ambient_gpg_files_before" != "$(snapshot_gpg_state "$ambient_gnupg")" ]] \
|| fail 'ambient GPG snapshot ignored unexpected file content'
printf 'baseline-value\n' > "$ambient_gnupg/unexpected-entry"
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_system_flathub_key="$(snapshot_file_state /var/lib/flatpak/repo/flathub.trustedkeys.gpg)"
before_user_flatpak="$(snapshot_file_state "$HOME/.local/share/flatpak/repo/config")"
before_bashrc="$(snapshot_file_state "$HOME/.bashrc")"
# Runtime and agent installs must consume the reviewed provenance table. Keep
# this scan at the public script boundary because a command hidden elsewhere in
# the installer can bypass every archive-level test below.
installer="$repo_dir/setup/scripts/install-packages"
unsafe_installers=()
for forbidden in \
'curl[^|]*\|[[:space:]]*bash' \
'nvm[[:space:]]+install[[:space:]]+--lts' \
'npm[[:space:]]+install[[:space:]]+-g[[:space:]]+pnpm' \
'npm[[:space:]]+install[[:space:]]+-g[[:space:]]+@openai/codex' \
'releases/latest' \
'api\.github\.com/.*/releases/latest'; do
while IFS= read -r finding; do
[[ -n "$finding" ]] && unsafe_installers+=("$finding")
done < <(grep -nE "$forbidden" "$installer" || true)
done
if (( ${#unsafe_installers[@]} > 0 )); then
printf 'package provenance contract: moving or piped installer inputs:\n' >&2
printf ' %s\n' "${unsafe_installers[@]}" >&2
fail 'replace each finding with a reviewed, verified installation path'
fi
# Re-running install-packages must be keyed to every reviewed trust input it
# consumes. The update-command fixture proves each input changes the digest;
# this public-boundary guard keeps any of those inputs from being silently
# removed from the installer state definition.
for state_input in \
'setup/packages' \
'setup/scripts/install-packages' \
'setup/lib/artifact-provenance' \
'setup/provenance'; do
grep -Fq "$state_input" "$repo_dir/install" \
|| fail "packages hash does not name required state input: $state_input"
done
# This must be the only production file sourced by the contract.
# shellcheck source=../../setup/lib/artifact-provenance
source "$repo_dir/setup/lib/artifact-provenance"
fixture_fingerprint='6016FF18CAE298CE3648EE2325E01F765E1EF9FA'
tiny_sha256='291bd319ae85488101e908e37fc0fa1b0da1429ba27e10d2b391cb3f60dd44ea'
base64 --decode "$fixtures/signed-fixture.rpm.base64" > "$test_tmp/signed-fixture.rpm"
base64 --decode "$fixtures/unsigned-fixture.rpm.base64" > "$test_tmp/unsigned-fixture.rpm"
base64 --decode "$fixtures/wrong-signer-fixture.rpm.base64" > "$test_tmp/wrong-signer-fixture.rpm"
[[ "$(wc -l < "$fixtures/SHASUMS256.txt")" -eq 4 ]] || fail 'signed checksum fixture is not four lines'
[[ "$(cmp -l "$fixtures/tiny-artifact" "$fixtures/tiny-artifact-tampered" | wc -l)" -eq 1 ]] \
|| 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"
# A parser must not accept plausible output from a GPG process that failed.
# The later import and verify calls succeed so both public helpers depend on
# the show-only producer's status rather than failing for an unrelated reason.
producer_failure_bin="$test_tmp/gpg-producer-failure-bin"
mkdir "$producer_failure_bin"
cat > "$producer_failure_bin/gpg" <<EOF
#!/usr/bin/env bash
if [[ " \$* " == *' --import-options show-only '* ]]; then
printf 'pub:::::::::\n'
printf 'fpr:::::::::$fixture_fingerprint:\n'
exit 42
fi
exit 0
EOF
chmod +x "$producer_failure_bin/gpg"
expect_failure env PATH="$producer_failure_bin:$PATH" bash -c '
source "$1"
key_fingerprint_matches "$2" "$3"
' _ "$repo_dir/setup/lib/artifact-provenance" "$fixtures/fixture-key.asc" "$fixture_fingerprint"
expect_failure env PATH="$producer_failure_bin:$PATH" bash -c '
source "$1"
verify_detached_signature "$2" "$3" "$4"
' _ "$repo_dir/setup/lib/artifact-provenance" "$fixtures/fixture-key.asc" \
"$fixtures/SHASUMS256.txt.asc" "$fixtures/SHASUMS256.txt"
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 \
"$fixtures/fixture-key.asc" "$test_tmp/bad-signature.asc" "$fixtures/SHASUMS256.txt"
expect_failure verify_detached_signature \
"$test_tmp/combined-key.asc" "$fixtures/wrong-signer-SHASUMS256.txt.asc" "$fixtures/SHASUMS256.txt"
mkdir "$test_tmp/bin"
cat > "$test_tmp/bin/curl" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
output=''
connect_timeout=''
max_time=''
max_filesize=''
while (($#)); do
case "$1" in
--output) output="$2"; shift 2 ;;
--connect-timeout) connect_timeout="$2"; shift 2 ;;
--max-time) max_time="$2"; shift 2 ;;
--max-filesize) max_filesize="$2"; shift 2 ;;
*) shift ;;
esac
done
[[ "$connect_timeout" == 10 && "$max_time" == 600 && "$max_filesize" == "${CURL_EXPECTED_MAX_BYTES:?}" ]] || exit 65
case "${CURL_FIXTURE:?}" in
good) cp "$CURL_FIXTURE_ROOT/tiny-artifact" "$output" ;;
oversized) head -c 1025 /dev/zero > "$output" ;;
interrupted) printf 'partial' > "$output"; exit 42 ;;
*) exit 64 ;;
esac
STUB
chmod +x "$test_tmp/bin/curl"
PATH="$test_tmp/bin:$PATH"
export PATH CURL_FIXTURE_ROOT="$fixtures" CURL_EXPECTED_MAX_BYTES=1024
destination="$test_tmp/destination"
export CURL_FIXTURE=good
expect_success download_sha256 'https://fixture.invalid/good' "$tiny_sha256" 1024 "$destination"
cmp -s "$fixtures/tiny-artifact" "$destination" || fail 'verified download changed artifact bytes'
[[ ! -e "$destination.part" ]] || fail 'successful download left a part file'
export CURL_FIXTURE=good
expect_failure download_sha256 'https://fixture.invalid/uppercase-digest' \
'291BD319AE85488101E908E37FC0FA1B0DA1429BA27E10D2B391CB3F60DD44EA' 1024 \
"$test_tmp/uppercase-destination"
printf 'known-good\n' > "$destination"
export CURL_FIXTURE=good
expect_failure download_sha256 'https://fixture.invalid/bad-digest' \
'0000000000000000000000000000000000000000000000000000000000000000' 1024 "$destination"
assert_file_bytes "$destination" 'known-good'
[[ ! -e "$destination.part" ]] || fail 'bad digest left a part file'
protected="$test_tmp/protected-known-good"
printf 'known-good\n' > "$protected"
ln -s "$protected" "$destination.part"
expect_failure download_sha256 'https://fixture.invalid/symlink-part' \
'0000000000000000000000000000000000000000000000000000000000000000' 1024 "$destination"
assert_file_bytes "$protected" 'known-good'
[[ -L "$destination.part" ]] || fail 'symlinked destination part was not preserved'
rm -f -- "$destination.part"
ln "$protected" "$destination.part"
expect_failure download_sha256 'https://fixture.invalid/hard-link-part' \
'0000000000000000000000000000000000000000000000000000000000000000' 1024 "$destination"
assert_file_bytes "$protected" 'known-good'
[[ "$(stat -c %i "$protected")" == "$(stat -c %i "$destination.part")" ]] \
|| fail 'hard-linked destination part was not preserved'
rm -f -- "$destination.part"
export CURL_FIXTURE=oversized
expect_failure download_sha256 'https://fixture.invalid/oversized' "$tiny_sha256" 1024 "$destination"
assert_file_bytes "$destination" 'known-good'
[[ ! -e "$destination.part" ]] || fail 'oversized download left a part file'
export CURL_FIXTURE=interrupted
expect_failure download_sha256 'https://fixture.invalid/interrupted' "$tiny_sha256" 1024 "$destination"
assert_file_bytes "$destination" 'known-good'
[[ ! -e "$destination.part" ]] || fail 'interrupted download left a part file'
expect_success rpm_signature_matches \
"$test_tmp/signed-fixture.rpm" "$fixtures/fixture-key.asc" "$fixture_fingerprint"
expect_failure rpm_signature_matches \
"$test_tmp/unsigned-fixture.rpm" "$fixtures/fixture-key.asc" "$fixture_fingerprint"
expect_failure rpm_signature_matches \
"$test_tmp/signed-fixture.rpm" "$fixtures/fixture-key.asc" '0000000000000000000000000000000000000000'
expect_failure rpm_signature_matches \
"$test_tmp/wrong-signer-fixture.rpm" "$fixtures/fixture-key.asc" "$fixture_fingerprint"
expect_failure rpm_signature_matches \
"$test_tmp/wrong-signer-fixture.rpm" "$test_tmp/combined-key.asc" "$fixture_fingerprint"
expect_success load_installer_provenance "$config"
[[ "${INSTALLER_PROVENANCE[BUN_VERSION]:-}" == '1.4.0' ]] || fail 'valid provenance was not loaded'
for reviewed_value in \
'NODE_VERSION 24.20.0' \
'NODE_X86_64_SHA256 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
'NODE_X86_64_BINARY_SHA256 89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7' \
'NODE_AARCH64_SHA256 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7' \
'NODE_AARCH64_BINARY_SHA256 23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477' \
'BUN_VERSION 1.4.0' \
'BUN_X86_64_SHA256 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
'BUN_X86_64_BINARY_SHA256 33d56b070be6a9e3da0ab013038b43d1645d0534ca811ecdba4472599117eb4b' \
'BUN_AARCH64_SHA256 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e' \
'BUN_AARCH64_BINARY_SHA256 086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9' \
'CODEX_VERSION 0.150.1' \
'CODEX_X86_64_SHA256 00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17' \
'CODEX_X86_64_BINARY_SHA256 abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386' \
'CODEX_AARCH64_SHA256 1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639' \
'CODEX_AARCH64_BINARY_SHA256 7a49aabe11fd95a1c968d79e16b5f1b17c3219002c5f7129d467f415f8460feb' \
'RUSTDESK_VERSION 1.4.9' \
'RUSTDESK_X86_64_SHA256 eb1b053ac5b2f774f2271f7fbbfd2ea475899f7a55135c5e172bc54b9388f108'; do
read -r name expected <<<"$reviewed_value"
[[ "${INSTALLER_PROVENANCE[$name]:-}" == "$expected" ]] \
|| fail "$name does not match the reviewed release"
done
for key_spec in \
'terra44 TERRA_FINGERPRINT' \
'claude-code CLAUDE_CODE_FINGERPRINT' \
'bun BUN_FINGERPRINT' \
'rpmfusion-free RPMFUSION_FREE_FINGERPRINT' \
'rpmfusion-nonfree RPMFUSION_NONFREE_FINGERPRINT' \
'hyprland-copr HYPRLAND_COPR_FINGERPRINT' \
'flathub FLATHUB_FINGERPRINT' \
'claude-desktop CLAUDE_DESKTOP_FINGERPRINT'; do
read -r key_file fingerprint_name <<<"$key_spec"
expect_success key_fingerprint_matches "$repo_dir/setup/provenance/keys/$key_file.asc" \
"${INSTALLER_PROVENANCE[$fingerprint_name]}"
done
[[ "$before_gnupg" == "$(snapshot "$host_gnupg")" ]] || fail 'host GPG state changed'
[[ "$before_gpg_files" == "$(snapshot_gpg_state "$host_gnupg")" ]] || fail 'host GPG state 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 or changed an ambient GPG entry'
parser_fixture="$test_tmp/installers.conf"
cp "$config" "$parser_fixture"
printf 'UNKNOWN_KEY=value\n' >> "$parser_fixture"
expect_failure load_installer_provenance "$parser_fixture"
cp "$config" "$parser_fixture"
printf 'BUN_VERSION=1.4.0\n' >> "$parser_fixture"
expect_failure load_installer_provenance "$parser_fixture"
grep -v '^NODE_VERSION=' "$config" > "$parser_fixture"
expect_failure load_installer_provenance "$parser_fixture"
cp "$config" "$parser_fixture"
sed -i 's/^BUN_VERSION=/ BUN_VERSION=/' "$parser_fixture"
expect_failure load_installer_provenance "$parser_fixture"
cp "$config" "$parser_fixture"
sed -i 's/^BUN_VERSION=.*/BUN_VERSION=$(id -u)/' "$parser_fixture"
expect_failure load_installer_provenance "$parser_fixture"
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/chatgpt-package" \
"$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"
artifact_root="$test_tmp/runtime-artifacts"
mkdir -p "$artifact_root/build"
for arch_spec in \
'x86_64 x64 x64' \
'aarch64 arm64 aarch64'; do
read -r machine node_arch bun_arch <<<"$arch_spec"
node_top="node-v24.20.0-linux-$node_arch"
mkdir -p "$artifact_root/build/$node_top/bin"
printf '#!/usr/bin/env bash\n# fixture %s\nprintf "v24.20.0\\n"\n' "$machine" \
> "$artifact_root/build/$node_top/bin/node"
chmod +x "$artifact_root/build/$node_top/bin/node"
cp "$artifact_root/build/$node_top/bin/node" "$artifact_root/node-$machine.bin"
tar -C "$artifact_root/build" -cJf "$artifact_root/node-$machine.tar.xz" "$node_top"
rm -rf -- "$artifact_root/build/$node_top"
bun_top="bun-linux-$bun_arch"
mkdir -p "$artifact_root/build/$bun_top"
printf '#!/usr/bin/env bash\n# fixture %s\nprintf "1.4.0\\n"\n' "$machine" \
> "$artifact_root/build/$bun_top/bun"
chmod +x "$artifact_root/build/$bun_top/bun"
cp "$artifact_root/build/$bun_top/bun" "$artifact_root/bun-$machine.bin"
(cd "$artifact_root/build" && zip -q "$artifact_root/bun-$machine.zip" \
"$bun_top/" "$bun_top/bun")
rm -rf -- "$artifact_root/build/$bun_top"
mkdir -p "$artifact_root/build/bin" "$artifact_root/build/codex-path" \
"$artifact_root/build/codex-resources/zsh/bin"
printf '#!/usr/bin/env bash\n# fixture %s\nprintf "codex-cli 0.150.1\\n"\n' "$machine" \
> "$artifact_root/build/bin/codex"
cp "$artifact_root/build/bin/codex" "$artifact_root/codex-$machine.bin"
printf '#!/usr/bin/env bash\nprintf "code mode host\\n"\n' \
> "$artifact_root/build/bin/codex-code-mode-host"
printf '{"target":"%s"}\n' "$machine" > "$artifact_root/build/codex-package.json"
for package_binary in codex-path/rg codex-resources/bwrap codex-resources/zsh/bin/zsh; do
printf '#!/usr/bin/env bash\nprintf "package resource\\n"\n' \
> "$artifact_root/build/$package_binary"
done
chmod +x "$artifact_root/build/bin/codex" \
"$artifact_root/build/bin/codex-code-mode-host" \
"$artifact_root/build/codex-path/rg" "$artifact_root/build/codex-resources/bwrap" \
"$artifact_root/build/codex-resources/zsh/bin/zsh"
tar -C "$artifact_root/build" --no-recursion -czf "$artifact_root/codex-$machine.tar.gz" \
bin/ bin/codex bin/codex-code-mode-host codex-package.json \
codex-path/ codex-path/rg codex-resources/ codex-resources/bwrap \
codex-resources/zsh/ codex-resources/zsh/bin/ codex-resources/zsh/bin/zsh
rm -rf -- "$artifact_root/build/bin" "$artifact_root/build/codex-path" \
"$artifact_root/build/codex-resources"
rm -f -- "$artifact_root/build/codex-package.json"
done
python3 - "$artifact_root/bun-symlink.zip" "$artifact_root/bun-special.zip" \
"$artifact_root/bun-directory.zip" <<'PY'
import stat
import sys
import zipfile
for destination, directory_type, entry_type, content in (
(sys.argv[1], stat.S_IFDIR, stat.S_IFLNK, b"../../outside-bun"),
(sys.argv[2], stat.S_IFDIR, stat.S_IFSOCK, b'#!/usr/bin/env bash\nprintf "1.4.0\\n"\n'),
(sys.argv[3], stat.S_IFREG, stat.S_IFREG, b'#!/usr/bin/env bash\nprintf "1.4.0\\n"\n'),
):
with zipfile.ZipFile(destination, "w") as archive:
directory = zipfile.ZipInfo("bun-linux-x64/")
directory.create_system = 3
directory.external_attr = (directory_type | 0o755) << 16
archive.writestr(directory, b"")
binary = zipfile.ZipInfo("bun-linux-x64/bun")
binary.create_system = 3
binary.external_attr = (entry_type | 0o755) << 16
archive.writestr(binary, content)
PY
mkdir -p "$artifact_root/build/wrong-node/bin" "$artifact_root/build/wrong-codex"
printf '#!/usr/bin/env bash\nprintf "v24.20.0\\n"\n' > "$artifact_root/build/wrong-node/bin/node"
printf '#!/usr/bin/env bash\nprintf "codex-cli 0.150.1\\n"\n' > "$artifact_root/build/wrong-codex/codex"
chmod +x "$artifact_root/build/wrong-node/bin/node" "$artifact_root/build/wrong-codex/codex"
tar -C "$artifact_root/build" -cJf "$artifact_root/node-bad.tar.xz" wrong-node
tar -C "$artifact_root/build" -czf "$artifact_root/codex-bad.tar.gz" wrong-codex/codex
mkdir -p "$artifact_root/build/bun-linux-x64"
printf '#!/usr/bin/env bash\nprintf "1.4.0\\n"\n' > "$artifact_root/build/bun-linux-x64/bun"
printf 'unexpected\n' > "$artifact_root/build/bun-linux-x64/extra"
chmod +x "$artifact_root/build/bun-linux-x64/bun"
(cd "$artifact_root/build" && zip -q "$artifact_root/bun-bad.zip" \
bun-linux-x64/ bun-linux-x64/bun bun-linux-x64/extra)
cp "$test_tmp/unsigned-fixture.rpm" "$artifact_root/rustdesk.rpm"
printf '#!/usr/bin/env bash\nprintf "executed\\n" > "$OUTSIDE_EXECUTED"\nprintf "v24.20.0\\n"\n' \
> "$artifact_root/outside-node"
printf '#!/usr/bin/env bash\nprintf "executed\\n" > "$OUTSIDE_EXECUTED"\nprintf "codex-cli 0.150.1\\n"\n' \
> "$artifact_root/outside-codex"
chmod +x "$artifact_root/outside-node" "$artifact_root/outside-codex"
node_escape_top='node-v24.20.0-linux-x64'
mkdir -p "$artifact_root/build/$node_escape_top/bin"
ln -s "$artifact_root/outside-node" "$artifact_root/build/$node_escape_top/bin/node"
tar -C "$artifact_root/build" -cJf "$artifact_root/node-symlink-escape.tar.xz" "$node_escape_top"
rm -rf -- "$artifact_root/build"
make_stub_commands() {
local case_root="$1"
mkdir -p "$case_root/bin" "$case_root/home" "$case_root/tmp" \
"$case_root/etc/profile.d" "$case_root/etc/yum.repos.d" \
"$case_root/etc/pki/rpm-gpg" "$case_root/flatpak-repo"
cat > "$case_root/etc/profile.d/nvm.sh" <<'STUB'
nvm() {
printf 'nvm:%s\n' "$*" >> "$COMMAND_LOG"
}
STUB
cat > "$case_root/bin/uname" <<'STUB'
#!/usr/bin/env bash
printf '%s\n' "${STUB_ARCH:-x86_64}"
STUB
cat > "$case_root/bin/tar" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
/usr/bin/tar "$@"
if [[ "${STUB_SIGNAL_PHASE:-}" == extract && "$*" == *'-x'* ]]; then
printf 'signal:extract\n' >> "$COMMAND_LOG"
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
kill -TERM -- "-$pgid"
sleep 2
fi
if [[ "${STUB_NODE_ESCAPE:-}" == hardlink && "$*" == *'-xJf'* ]]; then
destination=''
while (($#)); do
if [[ "$1" == -C ]]; then
destination="$2"
break
fi
shift
done
[[ -n "$destination" ]] || exit 71
node="$destination/node-v24.20.0-linux-x64/bin/node"
rm -f -- "$node"
ln "$ARTIFACT_ROOT/outside-node" "$node"
fi
if [[ -n "${STUB_TRAVERSAL_ERROR:-}" && "$*" == *'-x'* ]]; then
destination=''
arguments=("$@")
for ((index = 0; index < ${#arguments[@]}; index++)); do
if [[ "${arguments[index]}" == -C ]]; then
destination="${arguments[index + 1]}"
break
fi
done
[[ -n "$destination" ]] || exit 73
case "$STUB_TRAVERSAL_ERROR" in
Node)
selected="$destination/node-v24.20.0-linux-x64/bin/node"
outside="$ARTIFACT_ROOT/outside-node"
mkdir "$destination/node-v24.20.0-linux-x64/.unreadable"
chmod 000 "$destination/node-v24.20.0-linux-x64/.unreadable"
;;
Codex)
selected="$destination/bin/codex"
outside="$ARTIFACT_ROOT/outside-codex"
;;
esac
rm -f -- "$selected"
ln -s -- "$outside" "$selected"
fi
STUB
cat > "$case_root/bin/find" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${STUB_TRAVERSAL_ERROR:-}" && "${1:-}" == *'.stage.'* ]]; then
printf 'find:traversal-error:%s\n' "$STUB_TRAVERSAL_ERROR" >> "$COMMAND_LOG"
if [[ "$STUB_TRAVERSAL_ERROR" == Node ]]; then
root="$1"
shift
exec /usr/bin/find "$root/.unreadable" "$@"
fi
printf 'malformed traversal output'
exit 74
fi
exec /usr/bin/find "$@"
STUB
cat > "$case_root/bin/unzip" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
if [[ -n "${STUB_BUN_ATTRIBUTE:-}" && "${1:-}" == -q ]]; then
destination=''
while (($#)); do
if [[ "$1" == -d ]]; then
destination="$2"
break
fi
shift
done
[[ -n "$destination" ]] || exit 72
mkdir -p "$destination/bun-linux-x64"
cp "$ARTIFACT_ROOT/bun-x86_64.bin" "$destination/bun-linux-x64/bun"
chmod +x "$destination/bun-linux-x64/bun"
exit 0
fi
exec /usr/bin/unzip "$@"
STUB
cat > "$case_root/bin/mv" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
destination="${!#}"
if [[ "${STUB_SIGNAL_PHASE:-}" == activation ]]; then
case "$destination" in
*/.nvm/versions/node/v24.20.0|*/.bun/versions/1.4.0|*/.local/lib/panama/codex/0.150.1)
printf 'signal:activation\n' >> "$COMMAND_LOG"
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
kill -TERM -- "-$pgid"
sleep 2
;;
esac
elif [[ "${STUB_SIGNAL_PHASE:-}" == link ]]; then
case "$destination" in
*/.bun/bin/bun|*/.local/bin/codex)
printf 'signal:link\n' >> "$COMMAND_LOG"
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
kill -TERM -- "-$pgid"
sleep 2
;;
esac
fi
case "${STUB_LATE_COLLISION:-}:$destination" in
Node:*/.nvm/versions/node/v24.20.0|\
Bun:*/.bun/versions/1.4.0|\
Codex:*/.local/lib/panama/codex/0.150.1)
if [[ ! -e "$destination" ]]; then
mkdir -p -- "$destination"
printf 'preserved collision\n' > "$destination/collision-marker"
printf 'mv:late-collision:%s\n' "$STUB_LATE_COLLISION" >> "$COMMAND_LOG"
fi
;;
esac
exec /usr/bin/mv "$@"
STUB
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
package="${!#}"
printf 'rpm:query:%s\n' "$package" >> "$COMMAND_LOG"
case "$package" in
terra-release) [[ "${STUB_TERRA_INSTALLED:-0}" == 1 ]] ;;
claude-desktop-extra) [[ "${STUB_CLAUDE_DESKTOP_INSTALLED:-0}" == 1 ]] ;;
rustdesk)
[[ -n "${STUB_RUSTDESK_VERSION:-}" ]] || exit 1
[[ "$*" != *--queryformat* ]] || printf '%s' "$STUB_RUSTDESK_VERSION"
;;
*) 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"
if [[ "${STUB_SIGNAL_PHASE:-}" == download ]]; then
printf 'partial' > "$output"
printf 'signal:download\n' >> "$COMMAND_LOG"
pgid="$(ps -o pgid= -p $$ | tr -d ' ')"
kill -TERM -- "-$pgid"
sleep 2
fi
if [[ "${STUB_DOWNLOAD_INTERRUPT:-}" == 1 ]]; then
printf 'partial' > "$output"
exit 42
fi
case "$url" in
*rpmfusion-free*) cp "$SIGNED_RPM" "$output" ;;
*rpmfusion-nonfree*) cp "$SIGNED_RPM" "$output" ;;
*flathub.flatpakrepo) cp "$FLATHUB_DESCRIPTOR" "$output" ;;
*node-v24.20.0-linux-x64.tar.xz)
if [[ "${STUB_NODE_ESCAPE:-}" == symlink ]]; then
cp "$ARTIFACT_ROOT/node-symlink-escape.tar.xz" "$output"
else
cp "$ARTIFACT_ROOT/${STUB_BAD_LAYOUT:+node-bad.tar.xz}" "$output" 2>/dev/null \
|| cp "$ARTIFACT_ROOT/node-x86_64.tar.xz" "$output"
fi
;;
*node-v24.20.0-linux-arm64.tar.xz) cp "$ARTIFACT_ROOT/node-aarch64.tar.xz" "$output" ;;
*bun-linux-x64.zip)
cp "$ARTIFACT_ROOT/${STUB_BUN_ATTRIBUTE:+bun-$STUB_BUN_ATTRIBUTE.zip}" "$output" 2>/dev/null \
|| cp "$ARTIFACT_ROOT/${STUB_BAD_LAYOUT:+bun-bad.zip}" "$output" 2>/dev/null \
|| cp "$ARTIFACT_ROOT/bun-x86_64.zip" "$output"
;;
*bun-linux-aarch64.zip) cp "$ARTIFACT_ROOT/bun-aarch64.zip" "$output" ;;
*codex-package-x86_64-unknown-linux-musl.tar.gz)
cp "$ARTIFACT_ROOT/${STUB_BAD_LAYOUT:+codex-bad.tar.gz}" "$output" 2>/dev/null \
|| cp "$ARTIFACT_ROOT/codex-x86_64.tar.gz" "$output"
;;
*codex-package-aarch64-unknown-linux-musl.tar.gz) cp "$ARTIFACT_ROOT/codex-aarch64.tar.gz" "$output" ;;
*rustdesk-1.4.9-0.x86_64.rpm) cp "$ARTIFACT_ROOT/rustdesk.rpm" "$output" ;;
*) exit 66 ;;
esac
STUB
cat > "$case_root/bin/sha256sum" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
file="${!#}"
if [[ "${STUB_DIGEST_MISMATCH:-}" == 1 ]]; then
printf '%064d %s\n' 0 "$file"
exit 0
fi
if [[ "${STUB_TRAVERSAL_ERROR:-}" == Node ]] \
&& cmp -s "$file" "$ARTIFACT_ROOT/outside-node"; then
printf '%s %s\n' 89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7 "$file"
exit 0
elif [[ "${STUB_TRAVERSAL_ERROR:-}" == Codex ]] \
&& cmp -s "$file" "$ARTIFACT_ROOT/outside-codex"; then
printf '%s %s\n' abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386 "$file"
exit 0
fi
for spec in \
'node-x86_64.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
'node-aarch64.tar.xz 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7' \
'node-bad.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
'node-symlink-escape.tar.xz 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2' \
'bun-x86_64.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
'bun-aarch64.zip 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e' \
'bun-bad.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
'bun-symlink.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
'bun-special.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
'bun-directory.zip 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452' \
'codex-x86_64.tar.gz 00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17' \
'codex-aarch64.tar.gz 1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639' \
'codex-bad.tar.gz 00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17' \
'rustdesk.rpm eb1b053ac5b2f774f2271f7fbbfd2ea475899f7a55135c5e172bc54b9388f108'; do
read -r fixture digest <<<"$spec"
if cmp -s "$file" "$ARTIFACT_ROOT/$fixture"; then
if [[ "$fixture" == rustdesk.rpm ]]; then
stat -c '%d:%i' "$file" > "$VERIFIED_RUSTDESK_INODE"
fi
printf '%s %s\n' "$digest" "$file"
exit 0
fi
done
for binary_spec in \
'node-x86_64.bin 89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7' \
'node-aarch64.bin 23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477' \
'bun-x86_64.bin 33d56b070be6a9e3da0ab013038b43d1645d0534ca811ecdba4472599117eb4b' \
'bun-aarch64.bin 086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9' \
'codex-x86_64.bin abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386' \
'codex-aarch64.bin 7a49aabe11fd95a1c968d79e16b5f1b17c3219002c5f7129d467f415f8460feb'; do
read -r fixture digest <<<"$binary_spec"
if cmp -s "$file" "$ARTIFACT_ROOT/$fixture"; then
printf '%s %s\n' "$digest" "$file"
exit 0
fi
done
/usr/bin/sha256sum "$@"
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 || "${2:-}" == 644 ) ]] || 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"
count=0
[[ ! -f "$STUB_INSTALL_COUNTER" ]] || read -r count < "$STUB_INSTALL_COUNTER"
count=$((count + 1))
printf '%s\n' "$count" > "$STUB_INSTALL_COUNTER"
if [[ -n "${STUB_INSTALL_FAIL_AT:-}" && "$count" == "$STUB_INSTALL_FAIL_AT" ]]; then
exit 67
fi
exit 0
fi
if [[ "${1:-}" == rm && "${2:-}" == -f && "${3:-}" == -- ]]; then
destination="$4"
printf 'sudo:rm:%s\n' "$destination" >> "$COMMAND_LOG"
rm -f -- "$STUB_ETC${destination#/etc}"
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) ;;
rustdesk.rpm) logged+=(RUSTDESK_LOCAL) ;;
flathub-key.asc) logged+=(FLATHUB_KEY) ;;
*) logged+=("$argument") ;;
esac
done
printf 'sudo:%s\n' "${logged[*]}" >> "$COMMAND_LOG"
if [[ "$original" == *'/rustdesk.rpm'* ]]; then
[[ "$original" == *'--setopt=localpkg_gpgcheck=0'* \
&& "$original" != *'--setopt=localpkg_gpgcheck=1'* ]] || exit 70
rustdesk_path=''
for argument in "$@"; do
[[ "$(basename -- "$argument")" != rustdesk.rpm ]] || rustdesk_path="$argument"
done
[[ "$rustdesk_path" == "$TMPDIR"/tmp.*/rustdesk.rpm \
&& -s "$VERIFIED_RUSTDESK_INODE"
&& "$(stat -c '%d:%i' "$rustdesk_path")" == "$(<"$VERIFIED_RUSTDESK_INODE")" ]] \
&& cmp -s "$rustdesk_path" "$UNSIGNED_RPM" || exit 72
/usr/bin/rpm -qp --queryformat '%{NAME}\n' "$rustdesk_path" >/dev/null || exit 73
signature_status="$(/usr/bin/rpmkeys --checksig --verbose "$rustdesk_path")" || exit 74
[[ "$signature_status" == *'Header SHA256 digest: OK'* \
&& "$signature_status" == *'Payload SHA256 digest: OK'* \
&& "${signature_status,,}" != *signature* ]] || exit 75
fi
if [[ "$original" == *' pnpm' || "$original" == *' claude-code' ]]; then
case "$original" in
'dnf install -y --repo=fedora --repo=updates pnpm'|\
'dnf install -y --repo=claude-code --repo=fedora --repo=updates --from-repo=claude-code claude-code') ;;
*) exit 71 ;;
esac
fi
if [[ -n "${STUB_DNF_FAIL_MATCH:-}" && "$original" == *"$STUB_DNF_FAIL_MATCH"* ]]; then
exit 68
fi
if [[ "${1:-}" == flatpak && "${2:-}" == remote-add ]]; then
if [[ ! -f "$STUB_FLATPAK_REPO/config" ]] \
|| ! grep -q '^\[remote "flathub"\]$' "$STUB_FLATPAK_REPO/config"; then
key=''
url="${!#}"
for argument in "$@"; do
[[ "$argument" != --gpg-import=* ]] || key="${argument#--gpg-import=}"
done
[[ -n "$key" ]] || exit 69
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=%s\ngpg-verify=true\ngpg-verify-summary=true\n' \
"$url" > "$STUB_FLATPAK_REPO/config"
cp "$key" "$STUB_FLATPAK_REPO/flathub.trustedkeys.gpg"
printf 'mutated\n' > "$STUB_FLATPAK_STATE"
fi
fi
STUB
cat > "$case_root/bin/dnf" <<'STUB'
#!/usr/bin/env bash
set -euo pipefail
query=''
if [[ "$*" == '--quiet --no-plugins --dump-repo-config=terra*' ]]; then
query=filtered
printf 'dnf:dump-terra\n' >> "$COMMAND_LOG"
elif [[ "$*" == '--quiet --no-plugins --dump-repo-config=*' ]]; then
query=full
printf 'dnf:dump-all:locale=%s\n' "${LC_ALL:-unset}" >> "$COMMAND_LOG"
fi
if [[ -n "$query" ]]; then
mode="${STUB_TERRA_EFFECTIVE_MODE:-auto}"
if [[ "$mode" == auto ]]; then
case "${STUB_TERRA_REPO_MODE:-absent}" in
trusted|wrong-key) mode=trusted ;;
nogpg) mode=legacy ;;
wrong-url) mode=override-url ;;
stock|stock-wrong-key) mode=stock ;;
esac
# Adoption rewrites the repository file. Once it is the pinned form the
# dump has to say so, or the re-verification adoption performs on itself
# could never pass.
if [[ "$mode" == stock && -f "$STUB_ETC/yum.repos.d/terra.repo" ]] \
&& grep -q '^gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama$' \
"$STUB_ETC/yum.repos.d/terra.repo"; then
mode=trusted
fi
if [[ "$mode" == auto && -f "$STUB_ETC/yum.repos.d/terra.repo" ]] \
&& grep -q '^baseurl=https://repos.fyralabs.com/terra44$' "$STUB_ETC/yum.repos.d/terra.repo"; then
mode=trusted
elif [[ "$mode" == auto ]]; then
mode=absent
fi
fi
if [[ "$query" == full ]]; then
printf '======== "fedora" repository configuration: ========\n'
printf 'baseurl = \nenabled = 1\ngpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-44-primary\n'
printf 'metalink = https://mirrors.fedoraproject.org/metalink\nmirrorlist\n'
printf 'pkg_gpgcheck = 0\nrepo_gpgcheck = 0\n'
fi
case "$mode" in
absent) exit 0 ;;
trusted)
printf '======== "terra" repository configuration: ========\n'
printf 'baseurl = https://repos.fyralabs.com/terra44\n'
printf 'enabled = 1\n'
printf 'gpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
;;
legacy)
printf '======== "terra" repository configuration: ========\n'
printf 'baseurl = \nenabled = 1\ngpgcheck = 0\n'
printf 'gpgkey = https://repos.fyralabs.com/terra44.key\n'
printf 'metalink = https://tetsudou.fyralabs.com/terra44\nmirrorlist = \n'
printf 'pkg_gpgcheck = 0\nrepo_gpgcheck = 0\n'
;;
stock)
printf '======== "terra" repository configuration: ========\n'
printf 'baseurl = \nenabled = 1\ngpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44\n'
printf 'metalink = https://tetsudou.fyralabs.com/metalink?repo=terra44&arch=x86_64\n'
printf 'mirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
;;
override-url)
printf '======== "terra" repository configuration: ========\n'
printf 'baseurl = https://evil.invalid/terra44\nenabled = 1\ngpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
;;
override-gpg)
printf '======== "terra" repository configuration: ========\n'
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 0\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 0\nrepo_gpgcheck = 0\n'
;;
duplicate)
for id in terra terra; do
printf '======== "%s" repository configuration: ========\n' "$id"
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
done
;;
alternate)
for id in terra terra-legacy; do
printf '======== "%s" repository configuration: ========\n' "$id"
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
done
;;
locale-unsafe)
if [[ "${LC_ALL:-}" == C ]]; then
printf '======== "terra" repository configuration: ========\n'
else
printf '======== "terra" Repository-Konfiguration: ========\n'
fi
printf 'baseurl = \nenabled = 1\ngpgcheck = 0\n'
printf 'gpgkey = https://repos.fyralabs.com/terra44.key\n'
printf 'metalink = https://tetsudou.fyralabs.com/terra44\nmirrorlist = \n'
printf 'pkg_gpgcheck = 0\nrepo_gpgcheck = 0\n'
;;
localized-output)
printf '======== "terra" Repository-Konfiguration: ========\n'
printf 'baseurl = \nenabled = 1\ngpgcheck = 0\n'
;;
uppercase|mixed-case)
[[ "$query" == full ]] || exit 0
[[ "$mode" == uppercase ]] && id=TERRA || id=TeRrA
printf '======== "%s" repository configuration: ========\n' "$id"
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
;;
mixed-alternate)
ids=(terra)
[[ "$query" == filtered ]] || ids+=(TeRrA-legacy)
for id in "${ids[@]}"; do
printf '======== "%s" repository configuration: ========\n' "$id"
printf 'baseurl = https://repos.fyralabs.com/terra44\nenabled = 1\ngpgcheck = 1\n'
printf 'gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n'
printf 'metalink = \nmirrorlist = \npkg_gpgcheck = 1\nrepo_gpgcheck = 1\n'
done
;;
esac
exit 0
fi
exit 69
STUB
cat > "$case_root/bin/flatpak" <<'STUB'
#!/usr/bin/env bash
exit 69
STUB
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"
if [[ "${STUB_REUSE_CASE:-}" != 1 ]]; then
rm -rf -- "$case_root"
fi
make_stub_commands "$case_root"
: > "$case_root/commands.log"
printf '0\n' > "$case_root/install-counter"
: > "$case_root/verified-rustdesk-inode"
printf 'preserved\n' > "$case_root/flatpak-state"
: > "$case_root/softly-failed"
case "${STUB_SEED_OLD:-}" in
Node)
mkdir -p "$case_root/home/.nvm/versions/node/v23.0.0/bin"
printf '#!/usr/bin/env bash\nprintf "v23.0.0\\n"\n' \
> "$case_root/home/.nvm/versions/node/v23.0.0/bin/node"
chmod +x "$case_root/home/.nvm/versions/node/v23.0.0/bin/node"
;;
Bun)
mkdir -p "$case_root/home/.bun/versions/1.3.0/bin" "$case_root/home/.bun/bin"
printf '#!/usr/bin/env bash\nprintf "1.3.0\\n"\n' \
> "$case_root/home/.bun/versions/1.3.0/bin/bun"
chmod +x "$case_root/home/.bun/versions/1.3.0/bin/bun"
ln -s "$case_root/home/.bun/versions/1.3.0/bin/bun" "$case_root/home/.bun/bin/bun"
;;
Codex)
mkdir -p "$case_root/home/.local/lib/panama/codex/0.149.0" "$case_root/home/.local/bin"
printf '#!/usr/bin/env bash\nprintf "codex-cli 0.149.0\\n"\n' \
> "$case_root/home/.local/lib/panama/codex/0.149.0/codex"
chmod +x "$case_root/home/.local/lib/panama/codex/0.149.0/codex"
ln -s "$case_root/home/.local/lib/panama/codex/0.149.0/codex" \
"$case_root/home/.local/bin/codex"
;;
esac
case "${STUB_SEED_LEGACY:-}" in
Node)
mkdir -p "$case_root/home/.nvm/versions/node/v24.20.0/bin"
printf '#!/usr/bin/env bash\nprintf "v24.20.0\\n"\n' \
> "$case_root/home/.nvm/versions/node/v24.20.0/bin/node"
chmod +x "$case_root/home/.nvm/versions/node/v24.20.0/bin/node"
;;
Bun)
mkdir -p "$case_root/home/.bun/versions/1.4.0/bin" "$case_root/home/.bun/bin"
printf '#!/usr/bin/env bash\nprintf "1.4.0\\n"\n' \
> "$case_root/home/.bun/versions/1.4.0/bin/bun"
chmod +x "$case_root/home/.bun/versions/1.4.0/bin/bun"
ln -s "$case_root/home/.bun/versions/1.4.0/bin/bun" "$case_root/home/.bun/bin/bun"
;;
Codex)
mkdir -p "$case_root/home/.local/lib/panama/codex/0.150.1" "$case_root/home/.local/bin"
printf '#!/usr/bin/env bash\nprintf "codex-cli 0.150.1\\n"\n' \
> "$case_root/home/.local/lib/panama/codex/0.150.1/codex"
chmod +x "$case_root/home/.local/lib/panama/codex/0.150.1/codex"
ln -s "$case_root/home/.local/lib/panama/codex/0.150.1/codex" \
"$case_root/home/.local/bin/codex"
;;
esac
case "${STUB_SEED_COLLISION:-}" in
Node)
mkdir -p "$case_root/home/.nvm/versions/node/v24.20.0/bin"
printf '#!/usr/bin/env bash\nprintf "v0.0.0\\n"\n' \
> "$case_root/home/.nvm/versions/node/v24.20.0/bin/node"
chmod +x "$case_root/home/.nvm/versions/node/v24.20.0/bin/node"
;;
Bun)
mkdir -p "$case_root/home/.bun/versions/1.4.0/bin"
printf '#!/usr/bin/env bash\nprintf "0.0.0\\n"\n' \
> "$case_root/home/.bun/versions/1.4.0/bin/bun"
chmod +x "$case_root/home/.bun/versions/1.4.0/bin/bun"
;;
Codex)
mkdir -p "$case_root/home/.local/lib/panama/codex/0.150.1"
printf '#!/usr/bin/env bash\nprintf "codex-cli 10.150.10\\n"\n' \
> "$case_root/home/.local/lib/panama/codex/0.150.1/codex"
chmod +x "$case_root/home/.local/lib/panama/codex/0.150.1/codex"
;;
esac
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 \
|| "${STUB_PAIR_PRIOR:-}" == present && "${STUB_PAIR_NAME:-}" == 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
if [[ "${STUB_PAIR_PRIOR:-}" == present && "${STUB_PAIR_NAME:-}" == claude-code ]]; then
printf 'known key\n' > "$case_root/etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama"
printf 'known repo\n' > "$case_root/etc/yum.repos.d/claude-code.repo"
fi
case "${STUB_TERRA_REPO_MODE:-absent}" in
trusted)
cp "$installer_fixture/setup/provenance/keys/terra44.asc" \
"$case_root/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama"
printf '[terra]\nbaseurl=https://repos.fyralabs.com/terra44\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n' \
> "$case_root/etc/yum.repos.d/terra.repo"
;;
nogpg)
printf '[terra]\nbaseurl=https://repos.fyralabs.com/terra44\nenabled=1\ngpgcheck=0\nrepo_gpgcheck=0\ngpgkey=https://repos.fyralabs.com/terra44.key\n' \
> "$case_root/etc/yum.repos.d/terra.repo"
;;
wrong-url)
cp "$installer_fixture/setup/provenance/keys/terra44.asc" \
"$case_root/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama"
printf '[terra]\nbaseurl=https://evil.invalid/terra44\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n' \
> "$case_root/etc/yum.repos.d/terra.repo"
;;
stock|stock-wrong-key)
if [[ "${STUB_TERRA_REPO_MODE}" == stock ]]; then
cp "$installer_fixture/setup/provenance/keys/terra44.asc" \
"$case_root/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44"
else
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
"$case_root/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44"
fi
printf '[terra]\nmetalink=https://tetsudou.fyralabs.com/metalink?repo=terra44&arch=$basearch\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44\n' \
> "$case_root/etc/yum.repos.d/terra.repo"
;;
wrong-key)
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
"$case_root/etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama"
printf '[terra]\nbaseurl=https://repos.fyralabs.com/terra44\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama\n' \
> "$case_root/etc/yum.repos.d/terra.repo"
;;
esac
case "${STUB_FLATPAK_REMOTE_MODE:-absent}" in
trusted)
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://dl.flathub.org/repo/\ngpg-verify=true\ngpg-verify-summary=true\n' \
> "$case_root/flatpak-repo/config"
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
;;
wrong-url)
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://evil.invalid/repo/\ngpg-verify=true\ngpg-verify-summary=true\n' \
> "$case_root/flatpak-repo/config"
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
;;
wrong-key)
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://dl.flathub.org/repo/\ngpg-verify=true\ngpg-verify-summary=true\n' \
> "$case_root/flatpak-repo/config"
cp "$installer_fixture/setup/provenance/keys/terra44.asc" \
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
;;
no-gpg)
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://dl.flathub.org/repo/\ngpg-verify=false\ngpg-verify-summary=false\n' \
> "$case_root/flatpak-repo/config"
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
;;
alternate-key)
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://dl.flathub.org/repo/\ngpg-verify=true\ngpg-verify-summary=true\ngpgkeypath=/unreviewed/keyring.gpg\n' \
> "$case_root/flatpak-repo/config"
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
;;
empty-alternate-key|duplicate-alternate-key|malformed-alternate-key)
printf '[core]\nrepo_version=1\n\n[remote "flathub"]\nurl=https://dl.flathub.org/repo/\ngpg-verify=true\ngpg-verify-summary=true\n' \
> "$case_root/flatpak-repo/config"
case "$STUB_FLATPAK_REMOTE_MODE" in
empty-alternate-key) printf 'gpgkeypath=\n' ;;
duplicate-alternate-key) printf 'gpgkeypath=\ngpgkeypath=/unreviewed/keyring.gpg\n' ;;
malformed-alternate-key) printf 'gpgkeypath /unreviewed/keyring.gpg\n' ;;
esac >> "$case_root/flatpak-repo/config"
cp "$installer_fixture/setup/provenance/keys/flathub.asc" \
"$case_root/flatpak-repo/flathub.trustedkeys.gpg"
;;
esac
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" \
SOFT_LOG="$case_root/softly-failed" \
ARTIFACT_ROOT="$artifact_root" \
OUTSIDE_EXECUTED="$case_root/outside-executed" \
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" \
STUB_FLATPAK_REPO="$case_root/flatpak-repo" \
STUB_INSTALL_COUNTER="$case_root/install-counter" \
VERIFIED_RUSTDESK_INODE="$case_root/verified-rustdesk-inode" \
UNSIGNED_RPM="$test_tmp/unsigned-fixture.rpm" \
LC_ALL="${STUB_CALLER_LOCALE:-C}" \
HOME="$case_root/home" \
NVM_DIR="$case_root/home/.nvm" \
TMPDIR="$case_root/tmp" \
PANAMA_PATH="$installer_fixture" \
PATH="$case_root/bin:/usr/bin:/bin" \
setsid bash -c 'source "$PANAMA_PATH/setup/scripts/install-packages"; PANAMA_SYSTEM_ETC="$STUB_ETC"; PANAMA_SYSTEM_FLATPAK_REPO="$STUB_FLATPAK_REPO"; declare -F "$1" >/dev/null; status=0; "$1" || status=$?; (( ${#softly_failed[@]} == 0 )) || printf "%s\n" "${softly_failed[@]}" > "$SOFT_LOG"; exit "$status"' \
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
}
}
assert_soft_failure() {
local name="$1" component="$2"
[[ "$(<"$test_tmp/cases/$name/softly-failed")" == "$component" ]] \
|| fail "$name did not record exactly one $component soft failure"
}
assert_no_download() {
local name="$1"
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'curl:'* ]] \
|| fail "$name reached curl"
}
assert_no_runtime_staging() {
local name="$1"
[[ -z "$(find "$test_tmp/cases/$name/home" \
\( -name '*.part.*' -o -name '*.stage.*' -o -name '*.link.*' \) -print -quit)" \
&& -z "$(find "$test_tmp/cases/$name/tmp" -mindepth 1 -print -quit)" ]] \
|| fail "$name left private runtime staging behind"
}
assert_old_runtime_preserved() {
local name="$1" component="$2" home="$test_tmp/cases/$name/home"
case "$component" in
Node)
[[ "$($home/.nvm/versions/node/v23.0.0/bin/node --version)" == v23.0.0 ]] \
|| fail "$name changed the known-good Node"
;;
Bun)
[[ "$(readlink "$home/.bun/bin/bun")" == \
"$home/.bun/versions/1.3.0/bin/bun" ]] \
|| fail "$name changed the active Bun link"
[[ "$($home/.bun/bin/bun --version)" == 1.3.0 ]] \
|| fail "$name changed the known-good Bun"
;;
Codex)
[[ "$(readlink "$home/.local/bin/codex")" == \
"$home/.local/lib/panama/codex/0.149.0/codex" ]] \
|| fail "$name changed the active Codex link"
[[ "$($home/.local/bin/codex --version)" == 'codex-cli 0.149.0' ]] \
|| fail "$name changed the known-good Codex"
;;
esac
}
# Each supported architecture selects its own reviewed URL, digest and archive
# layout. Successful activation leaves no private download or extraction tree.
for runtime_case in \
'node-x86_64 x86_64 install_node Node https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-x64.tar.xz 67108864' \
'node-aarch64 aarch64 install_node Node https://nodejs.org/dist/v24.20.0/node-v24.20.0-linux-arm64.tar.xz 67108864' \
'bun-x86_64 x86_64 install_bun Bun https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-x64.zip 67108864' \
'bun-aarch64 aarch64 install_bun Bun https://github.com/oven-sh/bun/releases/download/bun-v1.4.0/bun-linux-aarch64.zip 67108864' \
'codex-x86_64 x86_64 install_codex Codex https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-x86_64-unknown-linux-musl.tar.gz 134217728' \
'codex-aarch64 aarch64 install_codex Codex https://github.com/openai/codex/releases/download/rust-v0.150.1/codex-package-aarch64-unknown-linux-musl.tar.gz 134217728'; do
read -r name arch function_name component url max_bytes <<<"$runtime_case"
reset_installer_fixture
if ! STUB_ARCH="$arch" run_installer_function "$name" "$function_name"; then
tail -n 120 "$test_tmp/cases/$name/output" >&2
fail "expected successful $name activation"
fi
grep -qFx "curl:$url:max=$max_bytes:output=artifact" \
"$test_tmp/cases/$name/commands.log" \
|| { sed -n '1,80p' "$test_tmp/cases/$name/output" >&2; sed -n '1,80p' "$test_tmp/cases/$name/commands.log" >&2; fail "$name did not select its reviewed artifact"; }
[[ ! -s "$test_tmp/cases/$name/softly-failed" ]] \
|| fail "$name recorded a soft failure after successful activation"
assert_no_runtime_staging "$name"
done
for receipt_spec in \
'node-x86_64 .nvm/versions/node/v24.20.0 2f2c0da162318f0de47665410c7c8c2ed3d36c8f3105de4bbc61176c70a7cbf2 89af8424dd53e560b1933f87ba650d8bf57c83ca5a04600eefb31f416aabbae7' \
'node-aarch64 .nvm/versions/node/v24.20.0 5f4ddab610c1ab2016b3c227cebdbf6d9495161487e4739c7b90090595f465f7 23a5637c2470fde09fcc1acc77c1b92e04e3d7e3e6e80ff7df6f5831958d1477' \
'bun-x86_64 .bun/versions/1.4.0 2d03fb5fb83ac8b567aca0a281b2ce1a1a19d488f56c2968d88c3f25e92fe452 33d56b070be6a9e3da0ab013038b43d1645d0534ca811ecdba4472599117eb4b' \
'bun-aarch64 .bun/versions/1.4.0 4b1a332ee861983eb93bcfe6f770fff94e3e31b2c388bdaea3c8ed35e58eed0e 086c4121c8738a8e0f5ed730e8a461bc3973b4444e372ddb77aef9a747fa2ae9' \
'codex-x86_64 .local/lib/panama/codex/0.150.1 00aba704f029f6dc0d948be407a756e0c97cc840132fd691353b2c6b0a505b17 abf1bb1643a79f73aa78ee627e111e02d4f8c98f25813a0cf6ce277709664386' \
'codex-aarch64 .local/lib/panama/codex/0.150.1 1ecac3f87823efb98153233b076ea3d6e34a7a8cebe43c5285dc5f79e1514639 7a49aabe11fd95a1c968d79e16b5f1b17c3219002c5f7129d467f415f8460feb'; do
read -r name target_relative artifact_digest binary_digest <<<"$receipt_spec"
assert_file_bytes "$test_tmp/cases/$name/home/$target_relative/.panama-provenance" \
"$(printf 'schema=1\nartifact_sha256=%s\nbinary_sha256=%s\n' \
"$artifact_digest" "$binary_digest")"
done
# No-op trust comes only from directories produced and attested by a successful
# installer run, never from a handcrafted executable that prints the version.
for exact_spec in \
'node-x86_64 x86_64 install_node' \
'node-aarch64 aarch64 install_node' \
'bun-x86_64 x86_64 install_bun' \
'bun-aarch64 aarch64 install_bun' \
'codex-x86_64 x86_64 install_codex' \
'codex-aarch64 aarch64 install_codex'; do
read -r name arch function_name <<<"$exact_spec"
reset_installer_fixture
STUB_REUSE_CASE=1 STUB_ARCH="$arch" expect_success \
run_installer_function "$name" "$function_name"
assert_no_download "$name"
[[ ! -s "$test_tmp/cases/$name/softly-failed" ]] \
|| fail "$name rejected its installer-produced provenance receipt"
done
[[ "$($test_tmp/cases/node-x86_64/home/.nvm/versions/node/v24.20.0/bin/node --version)" \
== v24.20.0 ]] || fail 'x86_64 Node activation has the wrong version'
grep -qFx 'nvm:alias default 24.20.0' "$test_tmp/cases/node-x86_64/commands.log" \
|| fail 'Node did not set the exact nvm default alias'
[[ "$($test_tmp/cases/bun-x86_64/home/.bun/bin/bun --version)" == 1.4.0 ]] \
|| fail 'x86_64 Bun activation has the wrong version'
[[ "$(readlink "$test_tmp/cases/bun-x86_64/home/.bun/bin/bun")" == \
"$test_tmp/cases/bun-x86_64/home/.bun/versions/1.4.0/bin/bun" ]] \
|| fail 'Bun did not atomically activate the reviewed version path'
[[ "$($test_tmp/cases/codex-x86_64/home/.local/bin/codex --version)" == \
'codex-cli 0.150.1' ]] || fail 'x86_64 Codex activation has the wrong version'
[[ "$(readlink "$test_tmp/cases/codex-x86_64/home/.local/bin/codex")" == \
"$test_tmp/cases/codex-x86_64/home/.local/lib/panama/codex/0.150.1/codex" ]] \
|| fail 'Codex did not atomically activate the reviewed version path'
# Unsupported CPUs stop before curl. RustDesk's reviewed RPM is x86_64-only,
# so aarch64 is also an intentional, recorded soft failure without a download.
for unsupported_case in \
'node-unsupported install_node Node riscv64' \
'bun-unsupported install_bun Bun riscv64' \
'codex-unsupported install_codex Codex riscv64' \
'rustdesk-unsupported install_rustdesk RustDesk riscv64' \
'rustdesk-aarch64 install_rustdesk RustDesk aarch64'; do
read -r name function_name component arch <<<"$unsupported_case"
reset_installer_fixture
STUB_ARCH="$arch" expect_failure run_installer_function "$name" "$function_name"
assert_no_download "$name"
assert_soft_failure "$name" "$component"
done
# A target can appear after the initial absence check. Activation must not
# replace it or move the staged directory inside it, and active older tools
# must stay selected.
for component_spec in \
'Node install_node .nvm/versions/node/v24.20.0' \
'Bun install_bun .bun/versions/1.4.0' \
'Codex install_codex .local/lib/panama/codex/0.150.1'; do
read -r component function_name target_relative <<<"$component_spec"
name="${component,,}-late-collision"
reset_installer_fixture
STUB_SEED_OLD="$component" STUB_LATE_COLLISION="$component" \
expect_failure run_installer_function "$name" "$function_name"
target="$test_tmp/cases/$name/home/$target_relative"
assert_file_bytes "$target/collision-marker" 'preserved collision'
[[ -z "$(find "$target" -mindepth 1 ! -name collision-marker -print -quit)" ]] \
|| fail "$name nested verified staging into the late collision"
assert_old_runtime_preserved "$name" "$component"
assert_soft_failure "$name" "$component"
assert_no_runtime_staging "$name"
done
# A staged Node must prove that links stay inside the verified tree before its
# executable can run. The hard-link adapter changes the extracted inode at the
# filesystem boundary, which catches checks that inspect tar names only.
for escape_kind in symlink hardlink; do
name="node-$escape_kind-escape"
reset_installer_fixture
STUB_NODE_ESCAPE="$escape_kind" expect_failure \
run_installer_function "$name" install_node
[[ ! -e "$test_tmp/cases/$name/outside-executed" ]] \
|| fail "$name executed a Node target outside the staged tree"
assert_soft_failure "$name" Node
assert_no_runtime_staging "$name"
done
# A traversal command that cannot read/describe the staged tree is itself a
# trust failure. Node traverses an unreadable subtree and Codex emits malformed
# output before failing; scanners that lose the status execute the outside tool.
for traversal_spec in 'Node install_node' 'Codex install_codex'; do
read -r component function_name <<<"$traversal_spec"
name="${component,,}-traversal-error"
reset_installer_fixture
STUB_TRAVERSAL_ERROR="$component" expect_failure \
run_installer_function "$name" "$function_name"
grep -qFx "find:traversal-error:$component" \
"$test_tmp/cases/$name/commands.log" \
|| fail "$name did not exercise the traversal error"
[[ ! -e "$test_tmp/cases/$name/outside-executed" ]] \
|| fail "$name executed a target hidden by a failed traversal"
assert_soft_failure "$name" "$component"
assert_no_runtime_staging "$name"
done
# A bad digest or interrupted transfer cannot replace the previously active
# tool and cannot leave reusable bytes behind.
for failure_mode in digest interrupted; do
for component_spec in \
'Node install_node' \
'Bun install_bun' \
'Codex install_codex'; do
read -r component function_name <<<"$component_spec"
name="${component,,}-$failure_mode"
reset_installer_fixture
if [[ "$failure_mode" == digest ]]; then
STUB_SEED_OLD="$component" STUB_DIGEST_MISMATCH=1 \
expect_failure run_installer_function "$name" "$function_name"
else
STUB_SEED_OLD="$component" STUB_DOWNLOAD_INTERRUPT=1 \
expect_failure run_installer_function "$name" "$function_name"
fi
assert_soft_failure "$name" "$component"
assert_old_runtime_preserved "$name" "$component"
assert_no_runtime_staging "$name"
done
name="rustdesk-$failure_mode"
reset_installer_fixture
if [[ "$failure_mode" == digest ]]; then
STUB_RUSTDESK_VERSION=1.4.8 STUB_DIGEST_MISMATCH=1 \
expect_failure run_installer_function "$name" install_rustdesk
else
STUB_RUSTDESK_VERSION=1.4.8 STUB_DOWNLOAD_INTERRUPT=1 \
expect_failure run_installer_function "$name" install_rustdesk
fi
assert_soft_failure "$name" RustDesk
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:'* ]] \
|| fail "$name reached DNF with an unverified RPM"
assert_no_runtime_staging "$name"
done
# Deliver a real signal to each isolated installer process group while private
# state exists. Cleanup must run for download, extraction, directory activation,
# and active-link replacement without changing an older selected runtime.
for signal_spec in \
'node-signal-extract install_node Node extract' \
'bun-signal-activation install_bun Bun activation' \
'codex-signal-download install_codex Codex download' \
'rustdesk-signal-download install_rustdesk RustDesk download' \
'bun-signal-link install_bun Bun link' \
'codex-signal-link install_codex Codex link'; do
read -r name function_name component phase <<<"$signal_spec"
reset_installer_fixture
if [[ "$component" == RustDesk ]]; then
STUB_RUSTDESK_VERSION=1.4.8 STUB_SIGNAL_PHASE="$phase" \
expect_failure run_installer_function "$name" "$function_name" 2>/dev/null
else
STUB_SEED_OLD="$component" STUB_SIGNAL_PHASE="$phase" \
expect_failure run_installer_function "$name" "$function_name" 2>/dev/null
assert_old_runtime_preserved "$name" "$component"
fi
grep -qFx "signal:$phase" "$test_tmp/cases/$name/commands.log" \
|| fail "$name did not deliver its real process-group signal"
assert_no_runtime_staging "$name"
done
# Successful updates keep the old version directory and switch only the active
# symlink after the replacement binary has passed its version check.
for component_spec in 'Bun install_bun .bun/bin/bun .bun/versions/1.4.0/bin/bun' \
'Codex install_codex .local/bin/codex .local/lib/panama/codex/0.150.1/codex'; do
read -r component function_name active_relative target_relative <<<"$component_spec"
name="${component,,}-atomic-update"
reset_installer_fixture
STUB_SEED_OLD="$component" expect_success run_installer_function "$name" "$function_name"
[[ "$(readlink "$test_tmp/cases/$name/home/$active_relative")" == \
"$test_tmp/cases/$name/home/$target_relative" ]] \
|| fail "$name did not atomically replace the active symlink"
if [[ "$component" == Bun ]]; then
[[ "$($test_tmp/cases/$name/home/.bun/versions/1.3.0/bin/bun --version)" == 1.3.0 ]] \
|| fail "$name removed the prior version directory"
else
[[ "$($test_tmp/cases/$name/home/.local/lib/panama/codex/0.149.0/codex --version)" \
== 'codex-cli 0.149.0' ]] || fail "$name removed the prior version directory"
fi
done
# Receipt or executable tampering preserves the directory and leaves an older
# active link unchanged. A matching version string is not an attestation.
for tamper_spec in \
'Node install_node .nvm/versions/node/v24.20.0 bin/node v24.20.0' \
'Bun install_bun .bun/versions/1.4.0 bin/bun 1.4.0' \
'Codex install_codex .local/lib/panama/codex/0.150.1 codex codex-cli_0.150.1'; do
read -r component function_name target_relative binary_relative version_text <<<"$tamper_spec"
for tamper_kind in receipt binary; do
name="${component,,}-$tamper_kind-tamper"
reset_installer_fixture
STUB_SEED_OLD="$component" expect_success \
run_installer_function "$name" "$function_name"
home="$test_tmp/cases/$name/home"
target="$home/$target_relative"
if [[ "$component" == Bun ]]; then
ln -sfn "$home/.bun/versions/1.3.0/bin/bun" "$home/.bun/bin/bun"
elif [[ "$component" == Codex ]]; then
ln -sfn "$home/.local/lib/panama/codex/0.149.0/codex" "$home/.local/bin/codex"
fi
if [[ "$tamper_kind" == receipt ]]; then
sed -i 's/^artifact_sha256=.*/artifact_sha256=0000000000000000000000000000000000000000000000000000000000000000/' \
"$target/.panama-provenance"
else
version_text="${version_text//_/ }"
printf '#!/usr/bin/env bash\nprintf "%s\\n"\n' "$version_text" \
> "$target/$binary_relative"
chmod +x "$target/$binary_relative"
fi
STUB_REUSE_CASE=1 expect_failure \
run_installer_function "$name" "$function_name"
assert_no_download "$name"
assert_soft_failure "$name" "$component"
if [[ "$component" != Node ]]; then
assert_old_runtime_preserved "$name" "$component"
fi
assert_no_runtime_staging "$name"
done
done
# A reviewed digest does not excuse a malformed archive. Reject the wrong top
# level or any extra member before a version path or active link appears.
for layout_case in \
'node-layout install_node Node .nvm/versions/node/v24.20.0' \
'bun-layout install_bun Bun .bun/versions/1.4.0' \
'codex-layout install_codex Codex .local/lib/panama/codex/0.150.1'; do
read -r name function_name component relative_target <<<"$layout_case"
reset_installer_fixture
STUB_BAD_LAYOUT=1 expect_failure run_installer_function "$name" "$function_name"
assert_soft_failure "$name" "$component"
[[ ! -e "$test_tmp/cases/$name/home/$relative_target" ]] \
|| fail "$name activated an archive with an unexpected layout"
assert_no_runtime_staging "$name"
done
# ZIP names alone do not establish entry type. The extraction adapter
# deliberately materializes all crafted entries as regular executable files,
# so only central-directory attribute validation can reject them pre-extraction.
for attribute_kind in symlink special directory; do
name="bun-$attribute_kind-attribute"
reset_installer_fixture
STUB_BUN_ATTRIBUTE="$attribute_kind" expect_failure \
run_installer_function "$name" install_bun
assert_soft_failure "$name" Bun
[[ ! -e "$test_tmp/cases/$name/home/.bun/versions/1.4.0" ]] \
|| fail "$name activated a ZIP entry with non-regular metadata"
assert_no_runtime_staging "$name"
done
# Legacy version-only and mismatched collisions are preserved and reported;
# neither can be deleted and recreated or treated as installer provenance.
for collision_mode in legacy collision; do
for component_spec in \
'Node install_node' \
'Bun install_bun' \
'Codex install_codex'; do
read -r component function_name <<<"$component_spec"
name="${component,,}-$collision_mode"
reset_installer_fixture
if [[ "$collision_mode" == legacy ]]; then
STUB_SEED_LEGACY="$component" expect_failure \
run_installer_function "$name" "$function_name"
assert_soft_failure "$name" "$component"
else
STUB_SEED_COLLISION="$component" expect_failure \
run_installer_function "$name" "$function_name"
assert_soft_failure "$name" "$component"
fi
assert_no_download "$name"
done
done
reset_installer_fixture
STUB_ARCH=x86_64 STUB_RUSTDESK_VERSION=1.4.8 \
expect_success run_installer_function rustdesk-x86_64 install_rustdesk
assert_log rustdesk-x86_64 "$(cat <<'EXPECTED'
rpm:query:rustdesk
curl:https://github.com/rustdesk/rustdesk/releases/download/1.4.9/rustdesk-1.4.9-0.x86_64.rpm:max=134217728:output=rustdesk.rpm
sudo:dnf install -y --setopt=localpkg_gpgcheck=0 RUSTDESK_LOCAL
EXPECTED
)"
assert_no_runtime_staging rustdesk-x86_64
reset_installer_fixture
STUB_ARCH=x86_64 STUB_RUSTDESK_VERSION=1.4.9 \
expect_success run_installer_function rustdesk-exact install_rustdesk
assert_log rustdesk-exact 'rpm:query:rustdesk'
reset_installer_fixture
expect_success run_installer_function pnpm install_pnpm
assert_log pnpm "$(cat <<'EXPECTED'
rpm:release
sudo:dnf install -y --repo=fedora --repo=updates pnpm
EXPECTED
)"
reset_installer_fixture
STUB_DNF_FAIL_MATCH=pnpm expect_failure run_installer_function pnpm-failure install_pnpm
assert_soft_failure pnpm-failure pnpm
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
dnf:dump-all:locale=C
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
sudo:install:terra.repo:/etc/yum.repos.d/terra.repo
dnf:dump-all:locale=C
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
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'
assert_file_bytes "$test_tmp/cases/terra/etc/yum.repos.d/terra.repo" "$(cat <<'EXPECTED'
[terra]
name=Panama reviewed Terra 44
baseurl=https://repos.fyralabs.com/terra44
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama
EXPECTED
)"
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/
gpg:fingerprint:6E5C05D979C76DAF93C081354184DD4D907A7CAE
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 --repo=claude-code --repo=fedora --repo=updates --from-repo=claude-code 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
)"
# Existing repository state is part of the trust boundary. Idempotency is only
# success when the already-active repository matches the reviewed policy.
reset_installer_fixture
STUB_FLATPAK_REMOTE_MODE=trusted \
expect_success run_installer_function flathub-existing-trusted ensure_flathub_remote
assert_log flathub-existing-trusted "$(cat <<'EXPECTED'
rpm:release
gpg:fingerprint:6E5C05D979C76DAF93C081354184DD4D907A7CAE
EXPECTED
)"
assert_file_bytes "$test_tmp/cases/flathub-existing-trusted/flatpak-state" 'preserved'
for mode in wrong-url wrong-key no-gpg alternate-key empty-alternate-key \
duplicate-alternate-key malformed-alternate-key; do
reset_installer_fixture
name="flathub-existing-$mode"
STUB_FLATPAK_REMOTE_MODE="$mode" \
expect_failure run_installer_function "$name" ensure_flathub_remote
assert_file_bytes "$test_tmp/cases/$name/flatpak-state" 'preserved'
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'curl:'* \
&& "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:'* ]] \
|| fail "untrusted existing Flathub $mode state was changed"
done
reset_installer_fixture
STUB_TERRA_INSTALLED=1 STUB_TERRA_REPO_MODE=trusted \
expect_success run_installer_function terra-existing-trusted install_terra_repository
assert_log terra-existing-trusted "$(cat <<'EXPECTED'
rpm:release
dnf:dump-all:locale=C
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
EXPECTED
)"
for mode in nogpg wrong-url wrong-key absent; do
reset_installer_fixture
name="terra-existing-$mode"
STUB_TERRA_INSTALLED=1 STUB_TERRA_REPO_MODE="$mode" \
expect_failure run_installer_function "$name" install_terra_repository
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:'* ]] \
|| fail "untrusted existing Terra $mode state reached a mutation"
done
# A machine that installed Terra the way Terra documents it. The repository file
# is terra-release's own -- a metalink, and the key at its stock path -- so it is
# not Panama's pinned form, but it IS the fingerprint this repository reviewed,
# with every signature check on. That is an adoption, not a compromise.
#
# Refusing it was a gate with no door: the ordinary Fedora desktop could never
# reach the pinned state, and status 78 then stopped every stage of every run,
# including the ones that never open DNF.
reset_installer_fixture
STUB_TERRA_INSTALLED=1 STUB_TERRA_REPO_MODE=stock \
expect_success run_installer_function terra-stock-preflight preflight_terra_trust
reset_installer_fixture
STUB_TERRA_INSTALLED=1 STUB_TERRA_REPO_MODE=stock \
expect_success run_installer_function terra-stock-adopt install_terra_repository
terra_adopted="$test_tmp/cases/terra-stock-adopt/etc/yum.repos.d/terra.repo"
grep -qx 'baseurl=https://repos.fyralabs.com/terra44' "$terra_adopted" \
|| fail 'adoption left Terra off the reviewed baseurl'
grep -qx 'gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama' "$terra_adopted" \
|| fail 'adoption left Terra off the reviewed key path'
grep -q 'metalink' "$terra_adopted" \
&& fail 'adoption kept the metalink it was supposed to replace'
[[ "$(<"$test_tmp/cases/terra-stock-adopt/commands.log")" != *'dnf:install'* ]] \
|| fail 'adoption opened a DNF transaction it does not need'
# Adoption is anchored on the fingerprint, not the URL. The same stock shape
# verifying against a key that is not Terra's is still a hard refusal.
reset_installer_fixture
STUB_TERRA_INSTALLED=1 STUB_TERRA_REPO_MODE=stock-wrong-key \
expect_failure run_installer_function terra-stock-wrong-key install_terra_repository
[[ "$(<"$test_tmp/cases/terra-stock-wrong-key/commands.log")" != *'sudo:'* ]] \
|| fail 'a stock Terra signed by an unreviewed key reached a mutation'
# An optional security field may be absent, but duplicates are malformed even
# when one copy looks safe. These cases catch the absent/duplicate conflation.
for duplicate_case in \
$'duplicate-no-gpg NoGPGVerify=false\nNoGPGVerify=true' \
$'duplicate-gpg-verify GPGVerify=true\nGPGVerify=false' \
'alternate-gpg-key-path GPGKeyPath=/unreviewed/keyring.gpg' \
'empty-gpg-key-path GPGKeyPath=' \
$'duplicate-gpg-key-path GPGKeyPath=\nGPGKeyPath=/unreviewed/keyring.gpg' \
'malformed-gpg-key-path GPGKeyPath /unreviewed/keyring.gpg'; do
name="${duplicate_case%% *}"
lines="${duplicate_case#* }"
reset_installer_fixture
STUB_FLATHUB_VERIFY_LINE="$lines" \
expect_failure run_installer_function "$name" ensure_flathub_remote
assert_file_bytes "$test_tmp/cases/$name/flatpak-state" 'preserved'
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:'* ]] \
|| fail "$name descriptor reached remote activation"
done
# DNF's non-networking effective configuration dump, rather than any one repo
# file, decides whether Terra is absent, trusted, overridden, or duplicated.
reset_installer_fixture
STUB_TERRA_EFFECTIVE_MODE=absent \
expect_success run_installer_function terra-effective-absent preflight_terra_trust
assert_log terra-effective-absent 'dnf:dump-all:locale=C'
reset_installer_fixture
STUB_TERRA_EFFECTIVE_MODE=trusted STUB_TERRA_REPO_MODE=trusted \
expect_success run_installer_function terra-effective-trusted preflight_terra_trust
assert_log terra-effective-trusted "$(cat <<'EXPECTED'
dnf:dump-all:locale=C
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F
EXPECTED
)"
for mode in legacy override-url override-gpg duplicate alternate; do
reset_installer_fixture
name="terra-effective-$mode"
status=0
STUB_TERRA_EFFECTIVE_MODE="$mode" STUB_TERRA_REPO_MODE=trusted \
run_installer_function "$name" preflight_terra_trust || status=$?
[[ "$status" -eq 78 ]] \
|| fail "effective Terra $mode returned $status instead of hard trust status 78"
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:'* ]] \
|| fail "effective Terra $mode reached a transaction"
done
# The full effective configuration must reveal case variants and mixed-case
# alternate identities that a lowercase terra* selector omits.
for identity_case in uppercase mixed-case mixed-alternate; do
reset_installer_fixture
name="terra-effective-$identity_case"
status=0
STUB_TERRA_EFFECTIVE_MODE="$identity_case" STUB_TERRA_REPO_MODE=trusted \
run_installer_function "$name" preflight_terra_trust || status=$?
[[ "$status" -eq 78 ]] \
|| fail "effective Terra $identity_case returned $status instead of hard trust status 78"
done
# DNF output must be locale-stable, and nonempty output that does not match the
# machine format is unsafe rather than equivalent to a fresh host.
for locale_case in locale-unsafe localized-output; do
reset_installer_fixture
name="terra-effective-$locale_case"
status=0
STUB_CALLER_LOCALE=C.UTF-8 STUB_TERRA_EFFECTIVE_MODE="$locale_case" \
run_installer_function "$name" preflight_terra_trust || status=$?
[[ "$status" -eq 78 ]] \
|| fail "effective Terra $locale_case returned $status instead of hard trust status 78"
assert_log "$name" 'dnf:dump-all:locale=C'
done
assert_pair_rollback() {
local name="$1" pair="$2" prior="$3" key repo
case "$pair" in
hyprland)
key="$test_tmp/cases/$name/etc/pki/rpm-gpg/RPM-GPG-KEY-panama-hyprland"
repo="$test_tmp/cases/$name/etc/yum.repos.d/panama-hyprland.repo"
;;
claude-code)
key="$test_tmp/cases/$name/etc/pki/rpm-gpg/RPM-GPG-KEY-claude-code-panama"
repo="$test_tmp/cases/$name/etc/yum.repos.d/claude-code.repo"
;;
esac
if [[ "$prior" == present ]]; then
assert_file_bytes "$key" 'known key'
assert_file_bytes "$repo" 'known repo'
else
[[ ! -e "$key" && ! -e "$repo" ]] \
|| fail "$pair activation failure left part of an absent pair"
fi
[[ "$(<"$test_tmp/cases/$name/commands.log")" != *'sudo:dnf'* ]] \
|| fail "$pair activation failure reached DNF"
}
# Both activation writes can fail after changing their target. Each repository
# must restore known-good pairs and return prior-absent pairs to full absence.
for pair_spec in \
'hyprland configure_hyprland_repository' \
'claude-code install_claude_code'; do
read -r pair function_name <<<"$pair_spec"
for prior in absent present; do
for fail_at in 1 2; do
reset_installer_fixture
name="$pair-$prior-activation-$fail_at"
STUB_PAIR_NAME="$pair" STUB_PAIR_PRIOR="$prior" STUB_INSTALL_FAIL_AT="$fail_at" \
expect_failure run_installer_function "$name" "$function_name"
assert_pair_rollback "$name" "$pair" "$prior"
done
done
done
# 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_pnpm 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'
if [[ "$function_name" == install_claude_code ]]; then
assert_soft_failure "$name" 'Claude Code'
elif [[ "$function_name" == install_pnpm ]]; then
assert_soft_failure "$name" pnpm
fi
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_system_flathub_key" == "$(snapshot_file_state /var/lib/flatpak/repo/flathub.trustedkeys.gpg)" ]] \
|| fail 'repository cases changed the system Flathub trusted key'
[[ "$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'