798 lines
34 KiB
Bash
798 lines
34 KiB
Bash
#!/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 ~ /<Task-[0-9]+-[^>]+>/) 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'
|