diff --git a/install b/install index 513278a..2cb4dd4 100755 --- a/install +++ b/install @@ -105,6 +105,23 @@ record_packages_hash() { hash_packages >"$PACKAGES_HASH" } +# Repository trust is checked before the installer can reach its bootstrap DNF. +# Status 78 is reserved for a trust-root failure and is propagated unchanged so +# no later stage, especially install-hardware, can invoke DNF with that repo. +TERRA_TRUST_FAILURE_STATUS=78 +trust_preflight="$PANAMA_PATH/setup/scripts/install-packages" +if [[ ! -x "$trust_preflight" ]]; then + printf 'install: package repository trust preflight is unavailable\n' >&2 + exit "$TERRA_TRUST_FAILURE_STATUS" +fi +if "$trust_preflight" --trust-preflight; then + : +else + trust_status=$? + printf 'install: package repository trust preflight failed\n' >&2 + exit "$trust_status" +fi + # ── The interview ──────────────────────────────────────────────────────────── # # Everything Panama needs to be told is asked here, before a single package is @@ -294,11 +311,18 @@ for stage in "${STAGES[@]}"; do echo "Run with --packages to install them anyway." continue fi - if ! "$script"; then + if "$script"; then + if [[ "$stage" == install-packages ]]; then + record_packages_hash + fi + else + stage_status=$? + if [[ "$stage" == install-packages && "$stage_status" -eq "$TERRA_TRUST_FAILURE_STATUS" ]]; then + printf '!!! %s stopped on an untrusted package repository\n' "$stage" >&2 + exit "$stage_status" + fi failed+=("$stage") printf '!!! %s failed\n' "$stage" >&2 - elif [[ "$stage" == install-packages ]]; then - record_packages_hash fi done diff --git a/setup/scripts/install-packages b/setup/scripts/install-packages index 31c0816..b74cb0f 100755 --- a/setup/scripts/install-packages +++ b/setup/scripts/install-packages @@ -294,6 +294,36 @@ _ini_section_count() { ' "$file" } +_ini_key_occurrence_count() { + local file="$1" wanted_section="$2" wanted_key="$3" + awk -v wanted_section="$wanted_section" -v wanted_key="$wanted_key" ' + function trim(value) { + sub(/^[[:space:]]+/, "", value) + sub(/[[:space:]]+$/, "", value) + return value + } + { + sub(/\r$/, "") + line = trim($0) + if (line == "" || line ~ /^[#;]/) next + if (line ~ /^\[[^]]+\]$/) { + section = substr(line, 2, length(line) - 2) + next + } + if (tolower(section) != tolower(wanted_section)) next + equals = index(line, "=") + if (equals > 0) { + key = trim(substr(line, 1, equals - 1)) + } else { + split(line, words, /[[:space:]]+/) + key = words[1] + } + if (tolower(key) == tolower(wanted_key)) count++ + } + END { print count + 0 } + ' "$file" +} + _restore_repository_file() { local existed="$1" backup="$2" mode="$3" destination="$4" if (( existed )); then @@ -346,29 +376,107 @@ _publish_repository_pair() { return "$status" } -_terra_repository_is_trusted() { - local repo_file baseurl enabled gpgcheck repo_gpgcheck gpgkey local_key - repo_file="$PANAMA_SYSTEM_ETC/yum.repos.d/terra.repo" - [[ -f "$repo_file" ]] || return 1 - baseurl="$(_ini_value "$repo_file" terra baseurl)" || return 1 - enabled="$(_ini_value "$repo_file" terra enabled)" || return 1 - gpgcheck="$(_ini_value "$repo_file" terra gpgcheck)" || return 1 - repo_gpgcheck="$(_ini_value "$repo_file" terra repo_gpgcheck)" || return 1 - gpgkey="$(_ini_value "$repo_file" terra gpgkey)" || return 1 - [[ "$baseurl" == "${INSTALLER_PROVENANCE[TERRA_BASEURL]}" \ - && "$enabled" == 1 && "$gpgcheck" == 1 && "$repo_gpgcheck" == 1 \ - && "$gpgkey" == 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama' ]] || return 1 +_effective_terra_key() { + awk -v reviewed_baseurl="${INSTALLER_PROVENANCE[TERRA_BASEURL]}" ' + function reset_block() { + delete values + delete seen + in_block = 0 + id = "" + } + function finish_block( key) { + if (!in_block) return + if (seen["enabled"] != 1) { + bad = 1 + return + } + if (values["enabled"] != "1") return + enabled_count++ + if (id != "terra") bad = 1 + for (key in required) { + if (seen[key] != 1) bad = 1 + } + if (values["baseurl"] != reviewed_baseurl || values["metalink"] != "" \ + || values["mirrorlist"] != "" || values["gpgcheck"] != "1" \ + || values["pkg_gpgcheck"] != "1" || values["repo_gpgcheck"] != "1" \ + || values["gpgkey"] != "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama") bad = 1 + trusted_key = values["gpgkey"] + } + BEGIN { + split("enabled baseurl metalink mirrorlist gpgcheck pkg_gpgcheck repo_gpgcheck gpgkey", fields) + for (field_index in fields) required[fields[field_index]] = 1 + reset_block() + } + /^======== ".*" repository configuration: ========$/ { + finish_block() + reset_block() + id = $0 + sub(/^======== "/, "", id) + sub(/" repository configuration: ========$/, "", id) + if (id == "" || id !~ /^terra/) bad = 1 + in_block = 1 + next + } + { + if (!in_block || $0 == "") next + separator = index($0, " = ") + if (separator > 0) { + key = substr($0, 1, separator - 1) + if (key in required) { + seen[key]++ + values[key] = substr($0, separator + 3) + } + } else if ($0 == "mirrorlist") { + seen["mirrorlist"]++ + values["mirrorlist"] = "" + } + } + END { + finish_block() + if (bad || enabled_count > 1) exit 2 + if (enabled_count == 0) exit 1 + print trusted_key + } + ' +} + +# Status 0 is one trusted effective Terra identity, 1 is no enabled Terra +# identity, and 2 is an unsafe, duplicated, or unreadable effective state. +_terra_effective_status() { + local dump gpgkey parse_status=0 local_key + dump="$(dnf --quiet --no-plugins --dump-repo-config='terra*')" || return 2 + gpgkey="$(printf '%s\n' "$dump" | _effective_terra_key)" || parse_status=$? + (( parse_status == 0 )) || return "$parse_status" + [[ "$gpgkey" == 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama' ]] || return 2 local_key="$PANAMA_SYSTEM_ETC/pki/rpm-gpg/RPM-GPG-KEY-terra44-panama" - [[ -f "$local_key" ]] || return 1 + [[ -f "$local_key" ]] || return 2 key_fingerprint_matches "$PANAMA_PATH/setup/provenance/keys/terra44.asc" \ "${INSTALLER_PROVENANCE[TERRA_FINGERPRINT]}" \ && key_fingerprint_matches "$local_key" \ - "${INSTALLER_PROVENANCE[TERRA_FINGERPRINT]}" + "${INSTALLER_PROVENANCE[TERRA_FINGERPRINT]}" \ + || return 2 +} + +TERRA_TRUST_FAILURE_STATUS=78 + +preflight_terra_trust() { + local status=0 + _require_policy_value TERRA_BASEURL 'https://repos.fyralabs.com/terra44' \ + || return "$TERRA_TRUST_FAILURE_STATUS" + _require_policy_value TERRA_FINGERPRINT AE09157A4DE88B497EA1D5D300CDAB43DE226D6F \ + || return "$TERRA_TRUST_FAILURE_STATUS" + _terra_effective_status || status=$? + if (( status == 0 || status == 1 )); then + return 0 + fi + log "Effective Terra repository configuration is not trusted; refusing all package work" + return "$TERRA_TRUST_FAILURE_STATUS" } # Status 0 is trusted, 1 is absent, and 2 is present but untrusted or malformed. _flathub_remote_status() { local config section_count url gpg_verify summary_verify disabled disabled_status + local alternate_key_count config="$PANAMA_SYSTEM_FLATPAK_REPO/config" [[ -f "$config" ]] || return 1 section_count="$(_ini_section_count "$config" 'remote "flathub"')" || return 2 @@ -387,6 +495,11 @@ _flathub_remote_status() { elif (( disabled_status != 1 )); then return 2 fi + alternate_key_count="$(_ini_key_occurrence_count "$config" 'remote "flathub"' gpgkeypath)" \ + || return 2 + # The reviewed default keyring is the only permitted trust source. Empty, + # duplicate, malformed, and nonempty alternate paths all fail closed. + (( alternate_key_count == 0 )) || return 2 [[ -f "$PANAMA_SYSTEM_FLATPAK_REPO/flathub.trustedkeys.gpg" ]] || return 2 key_fingerprint_matches "$PANAMA_SYSTEM_FLATPAK_REPO/flathub.trustedkeys.gpg" \ "${INSTALLER_PROVENANCE[FLATHUB_FINGERPRINT]}" || return 2 @@ -428,17 +541,21 @@ install_rpmfusion_repositories() { } install_terra_repository() { - local work staged_key staged_repo status + local work staged_key staged_repo status effective_status=0 require_reviewed_fedora_release || return 1 _require_policy_value TERRA_BASEURL 'https://repos.fyralabs.com/terra44' || return 1 _require_policy_value TERRA_FINGERPRINT AE09157A4DE88B497EA1D5D300CDAB43DE226D6F || return 1 + _terra_effective_status || effective_status=$? + if (( effective_status == 0 )); then + log "Terra repository already configured and verified" + return 0 + elif (( effective_status != 1 )); then + log "Effective Terra repository configuration is not trusted" + return "$TERRA_TRUST_FAILURE_STATUS" + fi if rpm -q terra-release >/dev/null 2>&1; then - if _terra_repository_is_trusted; then - log "Terra repository already installed and verified" - return 0 - fi - log "Installed Terra repository does not match Panama's reviewed trust policy" - return 1 + log "terra-release is installed without one trusted enabled Terra repository" + return "$TERRA_TRUST_FAILURE_STATUS" fi work="$(mktemp -d)" || return 1 chmod 0700 "$work" @@ -472,8 +589,10 @@ install_terra_repository() { if (( status == 0 )); then sudo install -m 0644 "$staged_repo" /etc/yum.repos.d/terra.repo || status=$? fi - if (( status == 0 )) && ! _terra_repository_is_trusted; then - status=1 + if (( status == 0 )); then + effective_status=0 + _terra_effective_status || effective_status=$? + (( effective_status == 0 )) || status="$TERRA_TRUST_FAILURE_STATUS" fi rm -rf -- "$work" return "$status" @@ -513,7 +632,7 @@ configure_hyprland_repository() { ensure_flathub_remote() { local work descriptor encoded key_file url no_gpg_verify gpg_verify - local status remote_status no_gpg_status gpg_status + local alternate_key_count status remote_status no_gpg_status gpg_status require_reviewed_fedora_release || return 1 _require_policy_value FLATHUB_DESCRIPTOR_URL 'https://flathub.org/repo/flathub.flatpakrepo' || return 1 _require_policy_value FLATHUB_DESCRIPTOR_MAX_BYTES 1048576 || return 1 @@ -556,6 +675,12 @@ ensure_flathub_remote() { rm -rf -- "$work" return 1 fi + alternate_key_count="$(_ini_key_occurrence_count "$descriptor" 'Flatpak Repo' GPGKeyPath)" \ + || alternate_key_count=1 + if (( alternate_key_count != 0 )); then + rm -rf -- "$work" + return 1 + fi if ! key_fingerprint_matches "$key_file" "${INSTALLER_PROVENANCE[FLATHUB_FINGERPRINT]}"; then rm -rf -- "$work" return 1 @@ -648,6 +773,20 @@ install_claude_desktop_if_trusted() { # RPM Fusion, no Terra, no COPR, no multimedia, no flatpaks: those exist for a # desktop, and every one of them is a network dependency and a failure mode a # headless machine has no reason to carry. +if [[ "${1:-}" == --trust-preflight ]]; then + if preflight_terra_trust; then + exit 0 + else + exit $? + fi +fi + +# Repeat the enclosing installer's early preflight at the package boundary so +# a repository change made after startup cannot reach this stage's first DNF. +if ! preflight_terra_trust; then + exit "$TERRA_TRUST_FAILURE_STATUS" +fi + if [[ "$ROLE" == server ]]; then echo -e "\n--- Installing packages (server) ---" log "Updating all packages. This may take a while" diff --git a/tests/setup/package-provenance-contract b/tests/setup/package-provenance-contract index 39fcbe3..a0902f2 100755 --- a/tests/setup/package-provenance-contract +++ b/tests/setup/package-provenance-contract @@ -445,6 +445,71 @@ STUB cat > "$case_root/bin/dnf" <<'STUB' #!/usr/bin/env bash +set -euo pipefail +if [[ "$*" == '--quiet --no-plugins --dump-repo-config=terra*' ]]; then + printf 'dnf:dump-terra\n' >> "$COMMAND_LOG" + 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 + 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 + ;; + esac + exit 0 +fi exit 69 STUB @@ -537,6 +602,23 @@ run_installer_function() { 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) @@ -602,11 +684,13 @@ reset_installer_fixture expect_success run_installer_function terra install_terra_repository assert_log terra "$(cat <<'EXPECTED' rpm:release +dnf:dump-terra 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-terra gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F EXPECTED @@ -713,7 +797,8 @@ EXPECTED )" assert_file_bytes "$test_tmp/cases/flathub-existing-trusted/flatpak-state" 'preserved' -for mode in wrong-url wrong-key no-gpg; do +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" \ @@ -729,7 +814,7 @@ 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 -rpm:query:terra-release +dnf:dump-terra gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F gpg:fingerprint:AE09157A4DE88B497EA1D5D300CDAB43DE226D6F EXPECTED @@ -748,7 +833,11 @@ done # 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'; do + $'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 @@ -759,6 +848,35 @@ for duplicate_case in \ || 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-terra' + +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-terra +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 + assert_pair_rollback() { local name="$1" pair="$2" prior="$3" key repo case "$pair" in diff --git a/tests/setup/update-command-contract b/tests/setup/update-command-contract index baa8f77..5d781fd 100755 --- a/tests/setup/update-command-contract +++ b/tests/setup/update-command-contract @@ -43,7 +43,7 @@ STAGE_NAMES=(install-packages link-dotfiles link-skills link-user change-setting # A PANAMA_PATH that looks enough like the real one for install to run, and # records what it was asked to do instead of doing it. build_fixture() { - local root="$1" packages_rc="${2:-0}" + local root="$1" packages_rc="${2:-0}" trust_rc="${3:-0}" rm -rf "$root" mkdir -p "$root/bin" "$root/setup/scripts" "$root/setup/packages" \ "$root/config/dot/quickshell/scripts" @@ -63,6 +63,10 @@ EOF # The one stage whose exit code the caller wants to control. cat >"$root/setup/scripts/install-packages" <>"\$PANAMA_RAN" + exit $trust_rc +fi printf 'install-packages\n' >>"\$PANAMA_RAN" exit $packages_rc EOF @@ -103,6 +107,14 @@ EOF #!/usr/bin/env bash exit 0 EOF + cat >"$root/shim/dnf" <<'EOF' +#!/usr/bin/env bash +printf 'dnf-transaction\n' >>"$PANAMA_RAN" +exit 0 +EOF + for prerequisite in gum lspci mokutil fwupdmgr; do + ln -s gsettings "$root/shim/$prerequisite" + done chmod +x "$root/shim"/* } @@ -215,6 +227,44 @@ run_install "$tmp/c" --upgrade >/dev/null || install_status=$? if [[ -r "$tmp/c/state/panama/packages-hash" ]]; then note 'install-packages failed but its hash was recorded, so it will never be retried' fi +grep -qx 'link-dotfiles' "$tmp/c/ran" \ + || note 'an ordinary package-stage failure no longer allows later safe stages' + +# An invalid enabled Terra root is not an ordinary package failure. It must +# stop before the installer's bootstrap DNF and before every stage. +build_fixture "$tmp/terra-preflight-hard" 0 78 +install_status=0 +run_install "$tmp/terra-preflight-hard" >/dev/null || install_status=$? +[[ "$install_status" -eq 78 ]] \ + || note "initial Terra trust failure returned $install_status instead of 78" +asserted_preflight="$(<"$tmp/terra-preflight-hard/ran")" +[[ "$asserted_preflight" == trust-preflight ]] \ + || note "initial Terra trust failure allowed later work: ${asserted_preflight//$'\n'/,}" + +# The trust verifier is itself mandatory. Losing its executable adapter must +# fail closed before interview, bootstrap, or stage work. +build_fixture "$tmp/terra-preflight-missing" +rm "$tmp/terra-preflight-missing/setup/scripts/install-packages" +install_status=0 +run_install "$tmp/terra-preflight-missing" >/dev/null || install_status=$? +[[ "$install_status" -eq 78 ]] \ + || note "missing Terra trust verifier returned $install_status instead of 78" +[[ ! -s "$tmp/terra-preflight-missing/ran" ]] \ + || note 'missing Terra trust verifier allowed later work' + +# The package stage repeats the preflight to close a configuration-change race. +# Its hard status must also stop link stages and install-hardware immediately. +build_fixture "$tmp/terra-stage-hard" 78 0 +install_status=0 +run_install "$tmp/terra-stage-hard" >/dev/null || install_status=$? +[[ "$install_status" -eq 78 ]] \ + || note "stage-time Terra trust failure returned $install_status instead of 78" +grep -qx 'install-packages' "$tmp/terra-stage-hard/ran" \ + || note 'stage-time Terra trust fixture never reached install-packages' +for suppressed in link-dotfiles link-skills link-user change-settings install-hardware dnf-transaction; do + grep -qx "$suppressed" "$tmp/terra-stage-hard/ran" \ + && note "stage-time Terra trust failure still ran $suppressed" +done # A full install always runs the stage, whatever any recorded hash says. build_fixture "$tmp/d"