#!/usr/bin/env bash # Source-safe TDD coverage for the standalone K3s recovery LUKS header proof. set -Eeuo pipefail readonly K3SLRH_TEST_DIRECTORY="${BASH_SOURCE[0]%/*}" readonly K3SLRH_REPOSITORY_ROOT="$(cd -- "${K3SLRH_TEST_DIRECTORY}/../.." && pwd -P)" readonly K3SLRH_SHARED_LIBRARY="${K3SLRH_REPOSITORY_ROOT}/scripts/lib/k3s-local-recovery.sh" readonly K3SLRH_A1_LIBRARY="${K3SLRH_REPOSITORY_ROOT}/scripts/lib/k3s-local-recovery-a1.sh" readonly K3SLRH_A1_HELPER="${K3SLRH_REPOSITORY_ROOT}/scripts/libexec/k3s-local-recovery-a1-check.sh" readonly K3SLRH_PROOF_LIBRARY="${K3SLRH_REPOSITORY_ROOT}/scripts/lib/k3s-local-recovery-header-proof.sh" readonly K3SLRH_WRAPPER="${K3SLRH_REPOSITORY_ROOT}/scripts/bootstrap/prove-k3s-local-recovery-header.sh" readonly K3SLRH_CONTRACT="${K3SLRH_REPOSITORY_ROOT}/infrastructure/security/k3s/local-recovery.env" readonly K3SLRH_VALIDATOR="${K3SLRH_REPOSITORY_ROOT}/scripts/validate/k3s-local-recovery.sh" readonly K3SLRH_AUTHORITY_DESIGN='/home/donghyeon/workspace/docs/platform/plans/2026-08-09-k3s-luks-header-proof-design.md' readonly K3SLRH_AUTHORITY_PLAN='/home/donghyeon/workspace/docs/platform/plans/2026-08-09-k3s-luks-header-proof-implementation.md' readonly K3SLRH_AUTHORITY_DESIGN_SHA256='0d1546795da59490956ba2e260bf12df210083514ae55c0551de632d54e278ec' readonly K3SLRH_AUTHORITY_PLAN_SHA256='126f7fa43fb857dd9ff753aac4ce6528f7a227d7151d688f8cce2b6e0ff8fbc9' readonly K3SLRH_EXPECTED_SHARED_SHA256='a206d81539e6c37fa085457ba01cff9de57de50f7235e3a4085989780e99585d' readonly K3SLRH_EXPECTED_A1_SHA256='8b39fc46229ebd4c77ff20040f942c4ef7b756d564d344a8ecdd11a7428e2968' readonly K3SLRH_EXPECTED_A1_HELPER_SHA256='06831fa35943a328ecd183d3c6f721b24c4607b2f349abf6201a12e690d08ebe' readonly K3SLRH_EXPECTED_CONTRACT_SHA256='c045f42c2dc3eda09c37a123bcc05d88d94f275e61504a2d5816948237e516df' readonly K3SLRH_EXPECTED_VALIDATOR_SHA256='6b869032fc325759ee25b219fad4ea6190a3389a561c496f0664a51b802f73e6' k3slrh_test_fail() { printf 'TEST FAILURE: %s\n' "$*" >&2 exit 1 } # This RED gate intentionally precedes authority hashing, source scanning, # sourcing, mktemp, command doubles, and every capability or live path. [[ -f "$K3SLRH_PROOF_LIBRARY" && ! -L "$K3SLRH_PROOF_LIBRARY" && -r "$K3SLRH_PROOF_LIBRARY" ]] || k3slrh_test_fail 'header-proof library missing' [[ -f "$K3SLRH_WRAPPER" && ! -L "$K3SLRH_WRAPPER" && -r "$K3SLRH_WRAPPER" ]] || k3slrh_test_fail 'header-proof wrapper missing' [[ "${K3SLRH_FOCUS:-entrypoint}" == entrypoint ]] || k3slrh_test_fail 'unsupported header-proof focus' k3slrh_sha_is_exact() { local path="${1-}" expected="${2-}" record (( $# == 2 )) || return 97 record="$(/usr/bin/sha256sum -- "$path")" || return 1 [[ "$record" == "${expected} ${path}" ]] } for k3slrh_checkpoint in \ "$K3SLRH_AUTHORITY_DESIGN:$K3SLRH_AUTHORITY_DESIGN_SHA256" \ "$K3SLRH_AUTHORITY_PLAN:$K3SLRH_AUTHORITY_PLAN_SHA256" \ "$K3SLRH_SHARED_LIBRARY:$K3SLRH_EXPECTED_SHARED_SHA256" \ "$K3SLRH_A1_LIBRARY:$K3SLRH_EXPECTED_A1_SHA256" \ "$K3SLRH_A1_HELPER:$K3SLRH_EXPECTED_A1_HELPER_SHA256" \ "$K3SLRH_CONTRACT:$K3SLRH_EXPECTED_CONTRACT_SHA256" \ "$K3SLRH_VALIDATOR:$K3SLRH_EXPECTED_VALIDATOR_SHA256"; do k3slrh_checkpoint_path="${k3slrh_checkpoint%:*}" k3slrh_checkpoint_hash="${k3slrh_checkpoint##*:}" k3slrh_sha_is_exact "$k3slrh_checkpoint_path" "$k3slrh_checkpoint_hash" || k3slrh_test_fail 'authority or frozen dependency drift' done unset k3slrh_checkpoint k3slrh_checkpoint_path k3slrh_checkpoint_hash k3slrh_library_static_is_safe() { local source_path="${1-}" (( $# == 1 )) || return 97 [[ -f "$source_path" && ! -L "$source_path" ]] || return 1 /usr/bin/awk ' function trim(value) { sub(/^[[:space:]]+/, "", value) sub(/[[:space:]]+$/, "", value) return value } function external_path(value) { return value ~ /\/(usr\/)?(s?bin)\/[A-Za-z0-9._+:-]+/ } function sensitive_bare(value) { return value ~ /(^|(&&|\|\||;)[[:space:]]*)(sudo|mount|umount|losetup|cryptsetup|keepassxc-cli|sqlite3|systemctl|timeout|k3s|findmnt|lsblk|blockdev|dmsetup)([[:space:]]|$)/ } { raw = $0 value = trim(raw) if (value == "" || value ~ /^#/) next if (value ~ /]+>/) bad = 1 if (value ~ /_k3slr_collect_prepackage_snapshot|_k3slr_lifecycle_dispatch|_k3slr_prove_header_restore/) bad = 1 if (in_function) { if (raw == "}") { in_function = 0; function_name = ""; next } if (value ~ /`/ || value ~ /(^|[;[:space:]])(eval|command|exec)[[:space:]]/) bad = 1 if (sensitive_bare(value) && value !~ /_k3slrh_command[[:space:]]/) bad = 1 if (external_path(value) && value !~ /_k3slrh_command[[:space:]]/ && value !~ /_k3slr[a-z0-9_]+[[:space:]]/) bad = 1 next } if (value ~ /^[A-Za-z_][A-Za-z0-9_]*\(\)[[:space:]]*\{$/) { in_function = 1 function_name = value sub(/\(\)[[:space:]]*\{$/, "", function_name) next } if (value ~ /^readonly[[:space:]]+[A-Za-z_][A-Za-z0-9_]*=(\047[^\047]*\047|[A-Za-z0-9_.:+\/-]+)$/) next bad = 1 } END { if (in_function || bad) exit 1 } ' "$source_path" } k3slrh_wrapper_static_is_safe() { local source_path="${1-}" (( $# == 1 )) || return 97 [[ -f "$source_path" && ! -L "$source_path" ]] || return 1 /usr/bin/awk ' { if ($0 ~ /_k3slr_collect_prepackage_snapshot|_k3slr_lifecycle_dispatch|_k3slr_prove_header_restore/) bad = 1 if ($0 ~ /(^|[[:space:]])(sudo|mount|umount|losetup|cryptsetup|keepassxc-cli|sqlite3)([[:space:]]|$)/) bad = 1 if ($0 ~ /scripts\/lib\/k3s-local-recovery\.sh/) shared = NR if ($0 ~ /scripts\/lib\/k3s-local-recovery-a1\.sh/) a1 = NR if ($0 ~ /scripts\/lib\/k3s-local-recovery-header-proof\.sh/) proof = NR if ($0 ~ /^if \[\[ "\$\{BASH_SOURCE\[0\]\}" == "\$0" \]\]; then$/) direct_guard++ if ($0 ~ /_k3slrh_header_proof_main "\$@"/) direct_main++ if ($0 ~ /_k3slrh_wrapper_initial_guard/) initial_guard++ if ($0 ~ /unset -f[[:space:]]+_k3slrh_/) bad = 1 if ($0 ~ /_k3slrh_wrapper_environment_is_clean/) inherited_guard = NR } END { if (bad || shared == 0 || a1 == 0 || proof == 0 || !(shared < a1 && a1 < proof) || direct_guard != 1 || direct_main != 1 || initial_guard < 2 || inherited_guard == 0 || inherited_guard >= shared) exit 1 } ' "$source_path" } # No production source is evaluated until both conservative scans pass. k3slrh_library_static_is_safe "$K3SLRH_PROOF_LIBRARY" || k3slrh_test_fail 'header-proof library static preflight failed' k3slrh_wrapper_static_is_safe "$K3SLRH_WRAPPER" || k3slrh_test_fail 'header-proof wrapper static preflight failed' # shellcheck source=/dev/null source "$K3SLRH_SHARED_LIBRARY" || k3slrh_test_fail 'shared library source failed' # shellcheck source=/dev/null source "$K3SLRH_A1_LIBRARY" || k3slrh_test_fail 'A1 library source failed' # shellcheck source=/dev/null source "$K3SLRH_PROOF_LIBRARY" || k3slrh_test_fail 'header-proof library source failed' _k3slr_load_contract "$K3SLRH_CONTRACT" || k3slrh_test_fail 'strict contract load failed' k3slrh_require_function() { local name="${1-}" (( $# == 1 )) || return 97 declare -F "$name" >/dev/null || k3slrh_test_fail "header-proof API missing: ${name}" } for k3slrh_api in \ _k3slrh_parse_cli \ _k3slrh_derive_targets \ _k3slrh_run_closed_validator \ _k3slrh_k3s_health \ _k3slrh_pre_mount_capability \ _k3slrh_header_proof_main \ _k3slrh_target_state_is_consistent; do k3slrh_require_function "$k3slrh_api" done unset k3slrh_api k3slrh_assert_readonly_literal() { local name="${1-}" expected="${2-}" declaration (( $# == 2 )) || return 97 declaration="$(declare -p "$name" 2>/dev/null)" || return 1 [[ "$declaration" == 'declare -r '* || "$declaration" == 'declare -rx '* ]] || return 1 [[ "${!name}" == "$expected" ]] } k3slrh_assert_readonly_literal K3SLRH_PROOF_RELATIVE \ 'HyeonworksRecovery/containers/k3s-recovery.header-proof.luks' || k3slrh_test_fail 'proof relative name is not immutable' k3slrh_assert_readonly_literal K3SLRH_PRE_KDBX_PREFIX \ 'hyeonworks-recovery.header-proof-pre.' || k3slrh_test_fail 'pre KDBX prefix is not immutable' k3slrh_assert_readonly_literal K3SLRH_POST_KDBX_PREFIX \ 'hyeonworks-recovery.header-proof-post.' || k3slrh_test_fail 'post KDBX prefix is not immutable' k3slrh_assert_readonly_literal K3SLRH_HEADER_BACKUP_NAME 'header-original.bin' || k3slrh_test_fail 'header backup name is not immutable' k3slrh_assert_readonly_literal K3SLRH_HEADER_EXPORT_NAME 'header-exported.bin' || k3slrh_test_fail 'header export name is not immutable' k3slrh_assert_readonly_literal K3SLRH_SQLITE_PACKAGE_VERSION '3.45.1-1ubuntu2.7' || k3slrh_test_fail 'sqlite package version is not immutable' k3slrh_assert_readonly_literal K3SLRH_SHARED_LIBRARY_SHA256 "$K3SLRH_EXPECTED_SHARED_SHA256" || k3slrh_test_fail 'shared source checkpoint placeholder or drift' k3slrh_assert_readonly_literal K3SLRH_A1_LIBRARY_SHA256 "$K3SLRH_EXPECTED_A1_SHA256" || k3slrh_test_fail 'A1 source checkpoint placeholder or drift' k3slrh_assert_readonly_literal K3SLRH_A1_HELPER_SHA256 "$K3SLRH_EXPECTED_A1_HELPER_SHA256" || k3slrh_test_fail 'A1 helper checkpoint placeholder or drift' k3slrh_assert_readonly_literal K3SLRH_CONTRACT_SHA256 "$K3SLRH_EXPECTED_CONTRACT_SHA256" || k3slrh_test_fail 'contract checkpoint placeholder or drift' k3slrh_assert_readonly_literal K3SLRH_VALIDATOR_SHA256 "$K3SLRH_EXPECTED_VALIDATOR_SHA256" || k3slrh_test_fail 'validator checkpoint placeholder or drift' readonly K3SLRH_EXPECTED_UTC='20260809T120304Z' readonly K3SLRH_RAW_IDENTIFIER_SENTINEL='RAW_IDENTIFIER_SENTINEL_DO_NOT_PRINT' readonly K3SLRH_EXPECTED_CLOSED_RECORD=$'Recovery device: match\nRecovery state: closed\nLineage: match\nLatest bundle: not_checked\n' readonly K3SLRH_EXPECTED_DRY_RUN=$'Header proof state: closed\nHeader proof capability: pass\nExecute-time checks: pending\nDRY RUN PASS: no changes\n' k3slrh_fixture_root="$(/usr/bin/mktemp -d /tmp/k3slrh-entrypoint-test.XXXXXXXX)" || k3slrh_test_fail 'cannot allocate fixture root' readonly k3slrh_fixture_root readonly k3slrh_stdout="${k3slrh_fixture_root}/stdout" readonly k3slrh_stderr="${k3slrh_fixture_root}/stderr" readonly k3slrh_command_log="${k3slrh_fixture_root}/commands" readonly k3slrh_mutation_log="${k3slrh_fixture_root}/mutations" readonly k3slrh_validator_call_log="${k3slrh_fixture_root}/validator-calls" readonly k3slrh_validator_hash_log="${k3slrh_fixture_root}/validator-hashes" readonly k3slrh_sudo_validate_log="${k3slrh_fixture_root}/sudo-validates" readonly k3slrh_readyz_log="${k3slrh_fixture_root}/readyz-calls" readonly k3slrh_validator_file_log="${k3slrh_fixture_root}/validator-file-checks" readonly k3slrh_validator_symlink_log="${k3slrh_fixture_root}/validator-symlink-checks" readonly k3slrh_validator_stat_log="${k3slrh_fixture_root}/validator-stat-checks" readonly k3slrh_binary_stat_log="${k3slrh_fixture_root}/binary-stat-checks" k3slrh_cleanup() { case "$k3slrh_fixture_root" in /tmp/k3slrh-entrypoint-test.[A-Za-z0-9]*) /usr/bin/rm -rf -- "$k3slrh_fixture_root" ;; *) return 1 ;; esac } trap k3slrh_cleanup EXIT k3slrh_assert_eq() { local expected="${1-}" actual="${2-}" label="${3-}" (( $# == 3 )) || k3slrh_test_fail 'invalid assert_eq invocation' [[ "$actual" == "$expected" ]] || k3slrh_test_fail "$label" } k3slrh_assert_rc() { local expected="${1-}" label="${2-}" rc=0 shift 2 || k3slrh_test_fail 'invalid assert_rc invocation' if "$@"; then rc=0; else rc=$?; fi [[ "$rc" == "$expected" ]] || k3slrh_test_fail "${label}: rc=${rc}, expected=${expected}" } k3slrh_assert_succeeds() { local label="${1-}" shift || k3slrh_test_fail 'invalid assert_succeeds invocation' "$@" || k3slrh_test_fail "$label" } k3slrh_wrapper_clean_composition() { local rc=0 : >"$k3slrh_stdout" : >"$k3slrh_stderr" if /usr/bin/env -i \ PATH=/usr/sbin:/usr/bin:/sbin:/bin LC_ALL=C \ /usr/bin/bash --noprofile --norc -c ' set -Eeuo pipefail source "$1" declare -F _k3slrh_header_proof_main >/dev/null declare -F _k3slrh_wrapper_initial_guard >/dev/null if _k3slrh_wrapper_initial_guard 0 hB; then exit 41; fi if _k3slrh_wrapper_initial_guard 1000 hxB; then exit 42; fi ' k3slrh-wrapper-probe "$K3SLRH_WRAPPER" \ >"$k3slrh_stdout" 2>"$k3slrh_stderr"; then rc=0 else rc=$? fi [[ "$rc" == 0 && ! -s "$k3slrh_stdout" && ! -s "$k3slrh_stderr" ]] } k3slrh_wrapper_rejects_inherited_route() { local rc=0 marker="${k3slrh_fixture_root}/inherited-route-marker" : >"$k3slrh_stdout" : >"$k3slrh_stderr" if /usr/bin/env -i \ PATH=/usr/sbin:/usr/bin:/sbin:/bin LC_ALL=C \ /usr/bin/bash --noprofile --norc -c ' set -Eeuo pipefail K3SLRH_PROOF_RELATIVE=attacker-controlled test_marker=$2 _k3slrh_header_proof_main() { : >"$test_marker"; } set +e source "$1" >/dev/null 2>&1 wrapper_rc=$? set -e [[ "$wrapper_rc" == 1 && ! -e "$test_marker" ]] ' k3slrh-wrapper-probe "$K3SLRH_WRAPPER" "$marker" \ >"$k3slrh_stdout" 2>"$k3slrh_stderr"; then rc=0 else rc=$? fi [[ "$rc" == 0 && ! -e "$marker" && ! -s "$k3slrh_stdout" && ! -s "$k3slrh_stderr" ]] } k3slrh_run_parse() { local parsed='' rc=0 if _k3slrh_parse_cli parsed "$@"; then rc=0; else rc=$?; fi (( rc == 0 )) || return "$rc" printf '%s' "$parsed" } k3slrh_run_timestamp() ( _k3slrh_derive_targets "$@" ) k3slrh_targets_are_exact() ( _k3slrh_derive_targets "$K3SLRH_EXPECTED_UTC" || return 1 [[ "${K3SLRH_RUN_UTC:-}" == "$K3SLRH_EXPECTED_UTC" && "${K3SLRH_PRE_KDBX_RELATIVE:-}" == \ "HyeonworksRecovery/vault/backups/${K3SLRH_PRE_KDBX_PREFIX}${K3SLRH_EXPECTED_UTC}.kdbx" && "${K3SLRH_POST_KDBX_RELATIVE:-}" == \ "HyeonworksRecovery/vault/backups/${K3SLRH_POST_KDBX_PREFIX}${K3SLRH_EXPECTED_UTC}.kdbx" ]] || return 1 _k3slrh_target_state_is_consistent \ "$K3SLRH_RUN_UTC" "$K3SLRH_PRE_KDBX_RELATIVE" "$K3SLRH_POST_KDBX_RELATIVE" ) k3slrh_run_targets_same_second_collision() ( _k3slrh_derive_targets "$K3SLRH_EXPECTED_UTC" || return 1 _k3slrh_derive_targets "$K3SLRH_EXPECTED_UTC" ) k3slrh_run_targets_pre_post_timestamp_drift() ( local drifted_post _k3slrh_derive_targets "$K3SLRH_EXPECTED_UTC" || return 1 drifted_post="HyeonworksRecovery/vault/backups/${K3SLRH_POST_KDBX_PREFIX}20260809T120305Z.kdbx" _k3slrh_target_state_is_consistent \ "$K3SLRH_RUN_UTC" "$K3SLRH_PRE_KDBX_RELATIVE" "$drifted_post" ) k3slrh_model_case=valid k3slrh_validator_count=0 k3slrh_mutation_count=0 k3slrh_log_command() { local argument for argument in "$@"; do printf '%s\0' "$argument" >>"$k3slrh_command_log"; done printf '\0' >>"$k3slrh_command_log" } k3slrh_path_is_known_binary() { local candidate for candidate in \ /usr/bin/awk /usr/bin/bash /usr/bin/chmod /usr/bin/chown /usr/bin/cmp \ /usr/bin/date /usr/bin/dd /usr/bin/dpkg-query /usr/bin/env /usr/bin/find \ /usr/bin/findmnt /usr/bin/grep /usr/bin/keepassxc-cli /usr/bin/lsblk \ /usr/bin/mkdir /usr/bin/mktemp /usr/bin/mount /usr/bin/ntfsinfo \ /usr/bin/od /usr/bin/readlink /usr/bin/rm /usr/bin/rmdir /usr/bin/sha256sum \ /usr/bin/sqlite3 /usr/bin/stat /usr/bin/sudo /usr/bin/sync /usr/bin/systemctl \ /usr/bin/test /usr/bin/timeout /usr/bin/umount /usr/bin/unlink /usr/bin/wc \ /usr/local/bin/k3s /usr/sbin/blockdev /usr/sbin/cryptsetup /usr/sbin/dmsetup \ /usr/sbin/losetup; do [[ "${1-}" != "$candidate" ]] || return 0 done return 1 } _k3slrh_command() { local package='' path='' expected_backing='' hash_count=0 k3slrh_log_command "$@" case "${1-}" in /usr/bin/sudo) if (( $# == 3 )) && [[ "$2" == --non-interactive && "$3" == --validate ]]; then printf x >>"$k3slrh_sudo_validate_log" if [[ "$k3slrh_model_case" == uncached_sudo ]]; then printf 'SUDO_PROMPT_SENTINEL\n' >&2 return 1 fi return 0 fi if (( $# == 15 )) && [[ "$2" == --non-interactive && "$3" == -- && "$4" == /usr/bin/env && "$5" == -i && "$6" == 'PATH=/usr/sbin:/usr/bin:/sbin:/bin' && "$7" == LC_ALL=C && "$8" == /usr/bin/timeout && "$9" == --signal=TERM && "${10}" == --kill-after=1s && "${11}" == 30s && "${12}" == /usr/local/bin/k3s && "${13}" == kubectl && "${14}" == get && "${15}" == --raw=/readyz ]]; then printf x >>"$k3slrh_readyz_log" case "$k3slrh_model_case" in readyz_extra) printf 'ok\nextra\n' ;; readyz_missing_lf) printf 'ok' ;; readyz_cr) printf 'ok\r\n' ;; readyz_nul) printf 'ok\0\n' ;; readyz_empty) : ;; readyz_rc1) return 1 ;; readyz_rc124) return 124 ;; readyz_rc137) return 137 ;; readyz_stderr) printf 'READYZ_STDERR_SENTINEL\n' >&2; printf 'ok\n' ;; *) printf 'ok\n' ;; esac return 0 fi printf 'mutation:sudo\n' >>"$k3slrh_mutation_log" return 97 ;; /usr/bin/systemctl) [[ "$#" == 4 && "$2" == is-active && "$3" == --quiet && "$4" == k3s ]] || return 97 [[ "$k3slrh_model_case" != k3s_inactive ]] || return 3 ;; /usr/bin/timeout) # Direct non-root readyz is forbidden by the authority contract. return 97 ;; /usr/bin/env) [[ "$#" == 10 && "$2" == -i && "$3" == 'PATH=/usr/sbin:/usr/bin:/sbin:/bin' && "$4" == LC_ALL=C && "$5" == /usr/bin/bash && "$6" == --noprofile && "$7" == --norc && "$8" == -- && "$9" == "$K3SLRH_VALIDATOR" && "${10}" == --expect-closed ]] || return 97 k3slrh_validator_count=$((k3slrh_validator_count + 1)) printf x >>"$k3slrh_validator_call_log" case "$k3slrh_model_case" in validator_cr) printf '%s\r\n' 'Recovery device: match' 'Recovery state: closed' 'Lineage: match' 'Latest bundle: not_checked' ;; validator_nul) printf 'Recovery device: match\0Recovery state: closed\nLineage: match\nLatest bundle: not_checked\n' ;; validator_missing_lf) printf '%s' "${K3SLRH_EXPECTED_CLOSED_RECORD%$'\n'}" ;; validator_extra) printf '%sextra\n' "$K3SLRH_EXPECTED_CLOSED_RECORD" ;; validator_raw_id) printf '%s%s\n' "$K3SLRH_EXPECTED_CLOSED_RECORD" "$K3SLRH_RAW_IDENTIFIER_SENTINEL" ;; validator_rc1) return 1 ;; validator_rc124) return 124 ;; validator_rc137) return 137 ;; validator_stderr) printf 'x' >&2; printf '%s' "$K3SLRH_EXPECTED_CLOSED_RECORD" ;; *) printf '%s' "$K3SLRH_EXPECTED_CLOSED_RECORD" ;; esac ;; /usr/bin/dpkg-query) [[ "$#" == 4 && "$2" == --show && "$3" == '--showformat=${Status}|${Version}\n' ]] || return 97 package="${!#}" case "$package" in keepassxc) path='2.7.6+dfsg.1-1build3'; [[ "$k3slrh_model_case" != keepass_package_drift ]] || path='0.invalid' ;; cryptsetup-bin) path='2:2.7.0-1ubuntu4.2'; [[ "$k3slrh_model_case" != cryptsetup_package_drift ]] || path='0.invalid' ;; sqlite3) path='3.45.1-1ubuntu2.7'; [[ "$k3slrh_model_case" != sqlite_package_drift ]] || path='0.invalid' ;; *) return 97 ;; esac printf 'install ok installed|%s\n' "$path" ;; /usr/bin/date) [[ "$#" == 3 && "$2" == --utc && "$3" == +%Y%m%dT%H%M%SZ ]] || return 97 printf '%s\n' "$K3SLRH_EXPECTED_UTC" ;; /usr/bin/findmnt) [[ "$#" == 6 && "$2" == -rn && "$3" == -M && "$5" == -o && "$6" == TARGET ]] || return 97 case "$4|$k3slrh_model_case" in "$K3SLR_OUTER_MOUNT|outer_mount_collision"|"$K3SLR_INNER_MOUNT|inner_mount_collision") printf '%s\n' "$4" return 0 ;; "$K3SLR_OUTER_MOUNT|"*|"$K3SLR_INNER_MOUNT|"*) return 1 ;; *) return 97 ;; esac ;; /usr/sbin/losetup) [[ "$#" == 5 && "$2" == --list && "$3" == --noheadings && "$4" == --output && "$5" == NAME,BACK-FILE ]] || { printf 'mutation:losetup\n' >>"$k3slrh_mutation_log" return 97 } case "$k3slrh_model_case" in main_loop_residue) expected_backing="${K3SLR_OUTER_MOUNT}/${K3SLR_CONTAINER_RELATIVE}" printf '/dev/loop91 %s\n' "$expected_backing" ;; proof_loop_residue) expected_backing="${K3SLR_OUTER_MOUNT}/${K3SLRH_PROOF_RELATIVE}" printf '/dev/loop92 %s\n' "$expected_backing" ;; esac ;; /usr/sbin/dmsetup) return 97 ;; /usr/bin/find) if (( $# == 8 )) && [[ "$2" == "$K3SLR_OUTER_MOUNT" && "$3" == -mindepth && "$4" == 1 && "$5" == -maxdepth && "$6" == 1 && "$7" == -print && "$8" == -quit ]]; then [[ "$k3slrh_model_case" != outer_mountpoint_not_empty ]] || printf '%s/unexpected\n' "$K3SLR_OUTER_MOUNT" return 0 fi if (( $# == 10 )) && [[ "$2" == /run && "$3" == -mindepth && "$4" == 1 && "$5" == -maxdepth && "$6" == 1 && "$7" == -name && "$8" == 'hyeonworks-k3s-recovery.*' && "$9" == -print && "${10}" == -quit ]]; then [[ "$k3slrh_model_case" != tmpfs_residue ]] || printf '/run/hyeonworks-k3s-recovery.ABCDEFGH\n' return 0 fi return 97 ;; /usr/bin/test) if (( $# == 3 )) && [[ "$2" == ! && "$3" == -L ]]; then return 97; fi if (( $# == 4 )) && [[ "$2" == ! && "$3" == -e ]]; then case "$4" in "/dev/mapper/${K3SLR_MAPPING_NAME}") [[ "$k3slrh_model_case" != main_mapping_residue ]] return ;; "/dev/mapper/${K3SLR_PROOF_MAPPING_NAME}") [[ "$k3slrh_model_case" != proof_mapping_residue ]] return ;; *) return 97 ;; esac fi if (( $# == 3 )); then case "$2" in -d) [[ "$3" == "$K3SLR_OUTER_MOUNT" ]] || return 97 ;; -f|-x|-r) case "$3" in "$K3SLRH_SHARED_LIBRARY"|"$K3SLRH_A1_LIBRARY"|"$K3SLRH_A1_HELPER"|"$K3SLRH_CONTRACT"|"$K3SLRH_VALIDATOR") ;; *) k3slrh_path_is_known_binary "$3" || return 97 ;; esac if [[ "$2" == -f && "$3" == "$K3SLRH_VALIDATOR" ]]; then printf x >>"$k3slrh_validator_file_log" fi ;; *) return 97 ;; esac return 0 fi if (( $# == 4 )) && [[ "$2" == ! && "$3" == -L ]]; then case "$4" in "/dev/mapper/${K3SLR_MAPPING_NAME}") [[ "$k3slrh_model_case" != main_mapping_dangling ]] return ;; "/dev/mapper/${K3SLR_PROOF_MAPPING_NAME}") [[ "$k3slrh_model_case" != proof_mapping_dangling ]] return ;; "$K3SLRH_VALIDATOR") printf x >>"$k3slrh_validator_symlink_log"; return 0 ;; "$K3SLR_OUTER_MOUNT"|"$K3SLRH_SHARED_LIBRARY"|"$K3SLRH_A1_LIBRARY"|"$K3SLRH_A1_HELPER"|"$K3SLRH_CONTRACT") return 0 ;; *) k3slrh_path_is_known_binary "$4" || return 97; return 0 ;; esac fi return 97 ;; /usr/bin/stat) path="${!#}" if (( $# == 4 )) && [[ "$2" == "--format=%u|%a|%F" && "$3" == -- ]]; then k3slrh_path_is_known_binary "$path" || return 97 printf '%s\n' "$path" >>"$k3slrh_binary_stat_log" if [[ "$k3slrh_model_case" == binary_untrusted && "$path" == /usr/bin/keepassxc-cli ]]; then printf '0|775|regular file\n' else printf '0|755|regular file\n' fi return 0 fi if (( $# == 4 )) && [[ "$2" == "--format=%F|%u|%g|%a" && "$3" == -- ]]; then case "$path" in "$K3SLR_OUTER_MOUNT") printf 'directory|0|0|755\n' ;; "$K3SLRH_SHARED_LIBRARY"|"$K3SLRH_A1_LIBRARY"|"$K3SLRH_A1_HELPER"|"$K3SLRH_CONTRACT"|"$K3SLRH_VALIDATOR") [[ "$path" != "$K3SLRH_VALIDATOR" ]] || printf x >>"$k3slrh_validator_stat_log" printf 'regular file|1000|1000|664\n' ;; *) return 97 ;; esac return 0 fi return 97 ;; /usr/bin/readlink) [[ "$#" == 4 && "$2" == -e && "$3" == -- ]] || return 97 path="$4" case "$path" in "$K3SLR_OUTER_MOUNT"|"$K3SLRH_SHARED_LIBRARY"|"$K3SLRH_A1_LIBRARY"|"$K3SLRH_A1_HELPER"|"$K3SLRH_CONTRACT"|"$K3SLRH_VALIDATOR") printf '%s\n' "$path" ;; *) return 97 ;; esac ;; /usr/bin/sha256sum) [[ "$#" == 3 && "$2" == -- ]] || return 97 path="${!#}" case "$path" in "$K3SLRH_SHARED_LIBRARY") printf '%s %s\n' "$K3SLRH_EXPECTED_SHARED_SHA256" "$path" ;; "$K3SLRH_A1_LIBRARY") printf '%s %s\n' "$K3SLRH_EXPECTED_A1_SHA256" "$path" ;; "$K3SLRH_A1_HELPER") printf '%s %s\n' "$K3SLRH_EXPECTED_A1_HELPER_SHA256" "$path" ;; "$K3SLRH_CONTRACT") printf '%s %s\n' "$K3SLRH_EXPECTED_CONTRACT_SHA256" "$path" ;; "$K3SLRH_VALIDATOR") printf x >>"$k3slrh_validator_hash_log" hash_count="$(/usr/bin/wc -c <"$k3slrh_validator_hash_log")" || return 97 if [[ "$k3slrh_model_case" == validator_hash_pre_drift || ( "$k3slrh_model_case" == validator_hash_post_drift && "$hash_count" -ge 2 ) ]]; then printf '%064d %s\n' 0 "$path" else printf '%s %s\n' "$K3SLRH_EXPECTED_VALIDATOR_SHA256" "$path" fi ;; *) return 97 ;; esac ;; /usr/bin/mount|/usr/bin/umount|/usr/sbin/cryptsetup) k3slrh_mutation_count=$((k3slrh_mutation_count + 1)) printf 'mutation\n' >>"$k3slrh_mutation_log" return 97 ;; *) return 97 ;; esac } _k3slr_command() { _k3slrh_command "$@"; } _k3slr_collect_prepackage_snapshot() { printf 'legacy-collector\n' >>"$k3slrh_mutation_log"; return 97; } _k3slr_prove_header_restore() { printf 'legacy-marker\n' >>"$k3slrh_mutation_log"; return 97; } _k3slr_lifecycle_dispatch() { printf 'prepare-dispatch\n' >>"$k3slrh_mutation_log"; return 97; } k3slrh_reset_model_logs() { : >"$k3slrh_command_log" : >"$k3slrh_mutation_log" : >"$k3slrh_validator_call_log" : >"$k3slrh_validator_hash_log" : >"$k3slrh_sudo_validate_log" : >"$k3slrh_readyz_log" : >"$k3slrh_validator_file_log" : >"$k3slrh_validator_symlink_log" : >"$k3slrh_validator_stat_log" : >"$k3slrh_binary_stat_log" } k3slrh_capture_call() { : >"$k3slrh_stdout" : >"$k3slrh_stderr" "$@" >"$k3slrh_stdout" 2>"$k3slrh_stderr" } k3slrh_validator_case() { local case_name="${1-}" expected_rc="${2-}" expected_calls="${3-}" local expected_hashes="${4-}" rc=0 expected_physical=2 (( $# == 4 )) || return 97 k3slrh_reset_model_logs k3slrh_model_case="$case_name" [[ "$case_name" != validator_hash_pre_drift ]] || expected_physical=1 if k3slrh_capture_call _k3slrh_run_closed_validator; then rc=0; else rc=$?; fi [[ "$rc" == "$expected_rc" && "$(/usr/bin/wc -c <"$k3slrh_validator_call_log")" == "$expected_calls" && "$(/usr/bin/wc -c <"$k3slrh_validator_hash_log")" == "$expected_hashes" && "$(/usr/bin/wc -c <"$k3slrh_validator_file_log")" == "$expected_physical" && "$(/usr/bin/wc -c <"$k3slrh_validator_symlink_log")" == "$expected_physical" && "$(/usr/bin/wc -c <"$k3slrh_validator_stat_log")" == "$expected_physical" ]] || return 1 [[ ! -s "$k3slrh_stdout" && ! -s "$k3slrh_stderr" ]] || return 1 ! /usr/bin/grep -aFq -- "$K3SLRH_RAW_IDENTIFIER_SENTINEL" "$k3slrh_stdout" "$k3slrh_stderr" } k3slrh_health_case() { local case_name="${1-}" expected_rc="${2-}" expected_readyz="${3-}" rc=0 (( $# == 3 )) || return 97 k3slrh_reset_model_logs k3slrh_model_case="$case_name" if k3slrh_capture_call _k3slrh_k3s_health; then rc=0; else rc=$?; fi [[ "$rc" == "$expected_rc" && ! -s "$k3slrh_stdout" && ! -s "$k3slrh_stderr" && "$(/usr/bin/wc -c <"$k3slrh_readyz_log")" == "$expected_readyz" ]] } k3slrh_capability_case() { local case_name="${1-}" expected_rc="${2-}" rc=0 k3slrh_reset_model_logs k3slrh_model_case="$case_name" if k3slrh_capture_call _k3slrh_pre_mount_capability; then rc=0; else rc=$?; fi [[ "$rc" == "$expected_rc" ]] || return 1 if [[ "$expected_rc" == 0 ]]; then [[ "$(<"$k3slrh_stdout")" == 'Header proof capability: pass' && ! -s "$k3slrh_stderr" ]] || return 1 else [[ ! -s "$k3slrh_stdout" && ! -s "$k3slrh_stderr" ]] || return 1 fi [[ ! -s "$k3slrh_mutation_log" ]] || return 1 if [[ "$case_name" == uncached_sudo ]]; then [[ "$(/usr/bin/wc -c <"$k3slrh_sudo_validate_log")" == 1 && ! -s "$k3slrh_readyz_log" && ! -s "$k3slrh_validator_call_log" && ! -s "$k3slrh_validator_hash_log" ]] fi } k3slrh_command_model_is_closed() { local rc=0 k3slrh_bad_command local -a k3slrh_bad_argv=() k3slrh_reset_model_logs for k3slrh_bad_command in \ '/usr/bin/timeout --signal=TERM 30s /usr/local/bin/k3s kubectl get --raw=/readyz' \ '/usr/bin/findmnt --target /fixture' \ '/usr/sbin/losetup --find --show /fixture' \ '/usr/bin/stat --format=%F -- /dev/loop0' \ '/usr/bin/test -e /dev/mapper/k3s-recovery'; do read -r -a k3slrh_bad_argv <<<"$k3slrh_bad_command" if _k3slrh_command "${k3slrh_bad_argv[@]}" >/dev/null 2>&1; then rc=0; else rc=$?; fi [[ "$rc" == 97 ]] || return 1 done [[ -s "$k3slrh_mutation_log" ]] } k3slrh_invalid_main() { local rc=0 : >"$k3slrh_stdout" : >"$k3slrh_stderr" if _k3slrh_header_proof_main "$@" >"$k3slrh_stdout" 2>"$k3slrh_stderr"; then rc=0 else rc=$? fi ! /usr/bin/grep -aFq -- "$K3SLRH_RAW_IDENTIFIER_SENTINEL" \ "$k3slrh_stdout" "$k3slrh_stderr" || return 97 return "$rc" } k3slrh_execute_without_tty() ( local rc=0 _k3slrh_tty_capability() { return 1; } _k3slr_require_execute_tty() { return 1; } : >"$k3slrh_stdout" : >"$k3slrh_stderr" if _k3slrh_header_proof_main --execute >"$k3slrh_stdout" 2>"$k3slrh_stderr"; then rc=0 else rc=$? fi [[ ! -s "$k3slrh_stdout" && ! -s "$k3slrh_stderr" ]] || return 97 return "$rc" ) k3slrh_dry_run_closed() { local rc=0 validator_calls validator_hashes sudo_validates readyz_calls local validator_files validator_symlinks validator_stats binary_count k3slrh_required_binary k3slrh_model_case=valid k3slrh_validator_count=0 k3slrh_mutation_count=0 k3slrh_reset_model_logs if k3slrh_capture_call _k3slrh_header_proof_main; then rc=0; else rc=$?; fi validator_calls="$(/usr/bin/wc -c <"$k3slrh_validator_call_log")" || return 1 validator_hashes="$(/usr/bin/wc -c <"$k3slrh_validator_hash_log")" || return 1 sudo_validates="$(/usr/bin/wc -c <"$k3slrh_sudo_validate_log")" || return 1 readyz_calls="$(/usr/bin/wc -c <"$k3slrh_readyz_log")" || return 1 validator_files="$(/usr/bin/wc -c <"$k3slrh_validator_file_log")" || return 1 validator_symlinks="$(/usr/bin/wc -c <"$k3slrh_validator_symlink_log")" || return 1 validator_stats="$(/usr/bin/wc -c <"$k3slrh_validator_stat_log")" || return 1 for k3slrh_required_binary in /usr/bin/keepassxc-cli /usr/sbin/cryptsetup /usr/bin/sqlite3; do binary_count="$(/usr/bin/grep -Fxc -- "$k3slrh_required_binary" "$k3slrh_binary_stat_log")" || return 1 [[ "$binary_count" == 1 ]] || return 1 done [[ "$rc" == 0 && "$(<"$k3slrh_stdout")"$'\n' == "$K3SLRH_EXPECTED_DRY_RUN" && ! -s "$k3slrh_stderr" && ! -s "$k3slrh_mutation_log" && "$validator_calls" == 1 && "$validator_hashes" == 2 && "$validator_files" == 2 && "$validator_symlinks" == 2 && "$validator_stats" == 2 && "$sudo_validates" == 1 && "$readyz_calls" == 1 && "$k3slrh_mutation_count" == 0 ]] } k3slrh_assert_eq dry-run "$(k3slrh_run_parse)" 'no argument must select dry-run' k3slrh_assert_eq execute "$(k3slrh_run_parse --execute)" 'one execute must be accepted' k3slrh_assert_rc 1 'duplicate parser execute must fail safely' k3slrh_run_parse --execute --execute k3slrh_assert_rc 1 'caller parser path must fail safely' k3slrh_run_parse --container /fixture k3slrh_assert_rc 2 'duplicate main execute must be usage rc2' k3slrh_invalid_main --execute --execute k3slrh_assert_rc 2 'caller main path must be usage rc2' k3slrh_invalid_main --container /fixture k3slrh_assert_succeeds 'actual clean wrapper composition and root/xtrace guards must pass' \ k3slrh_wrapper_clean_composition k3slrh_assert_succeeds 'actual wrapper must reject inherited route and constants' \ k3slrh_wrapper_rejects_inherited_route k3slrh_assert_rc 1 'execute without TTY must be safety rc1' k3slrh_execute_without_tty k3slrh_assert_rc 1 'malformed timestamp must be safety rc1' k3slrh_run_timestamp malformed k3slrh_assert_succeeds 'valid target names must derive exactly once' k3slrh_targets_are_exact k3slrh_assert_rc 1 'same-second target reservation must be safety rc1' \ k3slrh_run_targets_same_second_collision k3slrh_assert_rc 1 'pre/post timestamp drift must be safety rc1' \ k3slrh_run_targets_pre_post_timestamp_drift k3slrh_assert_succeeds 'closed external command model must reject unknown argv' \ k3slrh_command_model_is_closed for k3slrh_bad_health in k3s_inactive readyz_extra readyz_missing_lf readyz_cr \ readyz_nul readyz_empty readyz_rc1 readyz_rc124 readyz_rc137 readyz_stderr; do if [[ "$k3slrh_bad_health" == k3s_inactive ]]; then k3slrh_expected_readyz=0; else k3slrh_expected_readyz=1; fi k3slrh_assert_succeeds "K3s health must reject ${k3slrh_bad_health}" \ k3slrh_health_case "$k3slrh_bad_health" 1 "$k3slrh_expected_readyz" done unset k3slrh_bad_health k3slrh_expected_readyz k3slrh_assert_succeeds 'exact cached-sudo readyz must pass silently' \ k3slrh_health_case valid 0 1 for k3slrh_bad_validator in validator_cr validator_nul validator_missing_lf \ validator_extra validator_raw_id validator_rc1 validator_rc124 validator_rc137 \ validator_stderr; do k3slrh_assert_succeeds "closed validator must reject ${k3slrh_bad_validator}" \ k3slrh_validator_case "$k3slrh_bad_validator" 1 1 2 done unset k3slrh_bad_validator k3slrh_assert_succeeds 'validator pre-call hash drift must stop before dispatch' \ k3slrh_validator_case validator_hash_pre_drift 1 0 1 k3slrh_assert_succeeds 'validator post-call hash drift must fail closed' \ k3slrh_validator_case validator_hash_post_drift 1 1 2 k3slrh_assert_succeeds 'exact pinned closed validator record must pass' \ k3slrh_validator_case valid 0 1 2 for k3slrh_bad_capability in uncached_sudo k3s_inactive readyz_extra \ keepass_package_drift cryptsetup_package_drift sqlite_package_drift \ binary_untrusted \ outer_mount_collision inner_mount_collision outer_mountpoint_not_empty tmpfs_residue \ main_mapping_residue proof_mapping_residue main_mapping_dangling proof_mapping_dangling \ main_loop_residue proof_loop_residue; do k3slrh_assert_succeeds "capability must reject ${k3slrh_bad_capability}" \ k3slrh_capability_case "$k3slrh_bad_capability" 1 done unset k3slrh_bad_capability k3slrh_assert_succeeds 'closed dry-run must pass without mutation' k3slrh_dry_run_closed printf 'PASS: header-proof entrypoint and closed capability fixtures\n'