#!/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" 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/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 ;; esac 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' ;; 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" ;; 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 # 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'