Files
platform-core/scripts/validate/test-k3s-local-recovery.sh

8669 lines
379 KiB
Bash

#!/usr/bin/env bash
# Regression coverage for the source-safe local recovery hardware contract.
set -Eeuo pipefail
readonly REPOSITORY_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd -P)"
readonly LIBRARY_PATH="${REPOSITORY_ROOT}/scripts/lib/k3s-local-recovery.sh"
readonly CONTRACT_PATH="${REPOSITORY_ROOT}/infrastructure/security/k3s/local-recovery.env"
readonly VALIDATOR_PATH="${REPOSITORY_ROOT}/scripts/validate/k3s-local-recovery.sh"
readonly FEASIBILITY_PATH="${REPOSITORY_ROOT}/scripts/validate/k3s-local-recovery-feasibility.sh"
readonly PREPARE_WRAPPER_PATH="${REPOSITORY_ROOT}/scripts/bootstrap/prepare-k3s-local-recovery.sh"
readonly OPEN_WRAPPER_PATH="${REPOSITORY_ROOT}/scripts/bootstrap/open-k3s-local-recovery.sh"
readonly CLOSE_WRAPPER_PATH="${REPOSITORY_ROOT}/scripts/bootstrap/close-k3s-local-recovery.sh"
fail() {
printf 'TEST FAILURE: %s\n' "$*" >&2
exit 1
}
assert_eq() {
local expected="$1" actual="$2" description="$3"
[[ "$actual" == "$expected" ]] || fail "$description (expected ${expected}, got ${actual})"
}
assert_succeeds() {
"$@" || fail "expected success: $*"
}
assert_fails() {
if "$@"; then
fail "expected failure: $*"
fi
}
task5b_focus_selector_guard() {
local focus="${1-}"
(( $# == 1 )) || return 2
case "$focus" in
''|gate0|api_state|tty_seam|action1) return 0 ;;
*)
printf 'TEST FAILURE: unsupported TASK5B_FOCUS: %s\n' "$focus" >&2
return 2
;;
esac
}
# A nonempty Task 5B selector is closed before fixture creation, sourcing, or
# any Task 5A/older/full-suite assertion can run.
task5b_focus_selector_guard "${TASK5B_FOCUS:-}" || exit $?
task4_review2_focus_selector_guard() {
local focus="${1-}"
(( $# == 1 )) || return 2
case "$focus" in
''|critical|capture|atomic|show_protected|feas_show_protected|feas_attachment_listing) return 0 ;;
*)
printf 'TEST FAILURE: unsupported TASK4_REVIEW2_FOCUS: %s\n' "$focus" >&2
return 2
;;
esac
}
# A Task 1 selector is closed before any optional full-suite branch can run.
task4_review2_focus_selector_guard "${TASK4_REVIEW2_FOCUS:-}" || exit $?
fixture_root="$(mktemp -d "${TMPDIR:-/tmp}/k3s-local-recovery-test.XXXXXX")"
cleanup() {
case "$fixture_root" in
/tmp/k3s-local-recovery-test.*|"${TMPDIR:-/tmp}"/k3s-local-recovery-test.*)
rm -rf -- "$fixture_root"
;;
*)
fail 'refusing to remove an unexpected fixture directory'
;;
esac
}
trap cleanup EXIT
write_fixture() {
local name="$1" destination
destination="${fixture_root}/${name}"
cp -- "$CONTRACT_PATH" "$destination"
case "$name" in
missing_key.env)
sed -i '/^K3SLR_OWNER_GID=/d' "$destination"
;;
duplicate_key.env)
printf '%s\n' 'K3SLR_OWNER_GID=1000' >>"$destination"
;;
unknown_key.env)
printf '%s\n' 'K3SLR_UNSUPPORTED=value' >>"$destination"
;;
command_substitution.env)
printf 'K3SLR_OUTER_MOUNT=$(touch %s)\n' "${fixture_root}/evaluated" >"$destination.tmp"
sed '/^K3SLR_OUTER_MOUNT=/d' "$destination" >>"$destination.tmp"
mv -- "$destination.tmp" "$destination"
;;
relative_device.env)
sed -i 's|^K3SLR_RECOVERY_DISK_BY_ID=.*|K3SLR_RECOVERY_DISK_BY_ID=dev/disk/by-id/wwn-0x500a0751e6aa6254|' "$destination"
;;
parent_path.env)
sed -i 's|^K3SLR_DATABASE_RELATIVE=.*|K3SLR_DATABASE_RELATIVE=HyeonworksRecovery/..|' "$destination"
;;
*)
fail "unknown fixture: $name"
;;
esac
printf '%s\n' "$destination"
}
load_fixture() {
_k3slr_load_contract "$(write_fixture "$1")"
}
# The production change this catches is a missing or unsafe source-only contract
# library. The RED run before implementation asserted that source failed.
# shellcheck source=/dev/null
source "$LIBRARY_PATH"
assert_succeeds _k3slr_load_contract "$CONTRACT_PATH"
assert_eq /dev/disk/by-id/wwn-0x500a0751e6aa6254 \
"$( _k3slr_config_value "$CONTRACT_PATH" K3SLR_RECOVERY_DISK_BY_ID )" \
'approved recovery disk identity is available without sourcing config'
assert_eq 'K3s Recovery LUKS' "$K3SLR_KEEPASS_ENTRY" 'KeePass entry is library-owned'
assert_eq 'luks-header-backup.bin' "$K3SLR_KEEPASS_HEADER_ATTACHMENT" 'header attachment is library-owned'
assert_fails _k3slr_config_value "$CONTRACT_PATH" K3SLR_UNSUPPORTED
assert_fails load_fixture missing_key.env
assert_fails load_fixture duplicate_key.env
assert_fails load_fixture unknown_key.env
assert_fails load_fixture command_substitution.env
[[ ! -e "${fixture_root}/evaluated" ]] || fail 'contract parser evaluated command substitution'
assert_fails load_fixture relative_device.env
assert_fails load_fixture parent_path.env
assert_eq 12884901891 "$( _k3slr_required_bytes 858993460 1 )" '25 percent padding and two phases'
assert_fails _k3slr_required_bytes -1 0
assert_fails _k3slr_required_bytes 1 9223372036854775807
assert_fails _k3slr_required_bytes 1844674407370955161 0
assert_succeeds _k3slr_inner_capacity_fits 12884901891 1 858993460
assert_fails _k3slr_inner_capacity_fits 12884901890 1 858993460
assert_fails _k3slr_inner_capacity_fits 12884901891 1x 858993460
assert_succeeds _k3slr_outer_capacity_fits 1000000000000 400000000000 34359738368
assert_fails _k3slr_outer_capacity_fits 1000000000000 250000000000 34359738368
assert_fails _k3slr_outer_capacity_fits 1000 1001 1
validator_device_evidence() {
local fixture="$1"
local recovery_partition=/dev/fixture-recovery-partition
local recovery_partition_parent=/dev/fixture-recovery-disk
local recovery_fs_uuid=4EA0196C0C5FA27E recovery_partuuid=4670aa9f-9045-4bce-930d-9e84dfec9f38
local recovery_model=CT1000MX500SSD1 recovery_serial=2306E6AA6254
local recovery_wwn=0x500a0751e6aa6254 k3s_major_minor=8:32
local smart_health=PASSED smart_reallocated=0 smart_pending=0 smart_uncorrectable=0 ntfs_probe=pass
case "$fixture" in
wrong_by_id_target) recovery_partition_parent=/dev/fixture-wrong-disk ;;
wrong_fs_uuid) recovery_fs_uuid=AAAAAAAAAAAAAAAA ;;
wrong_partuuid) recovery_partuuid=aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa ;;
wrong_model) recovery_model=WRONGMODEL ;;
wrong_serial) recovery_serial=WRONGSERIAL ;;
wrong_wwn) recovery_wwn=0xaaaaaaaaaaaaaaaa ;;
same_parent_disk) k3s_major_minor=8:0 ;;
smart_health_failed) smart_health=FAILED ;;
smart_reallocated_nonzero) smart_reallocated=1 ;;
smart_pending_nonzero) smart_pending=1 ;;
smart_uncorrectable_nonzero) smart_uncorrectable=1 ;;
ntfs_dirty_or_hibernated) ntfs_probe=fail ;;
esac
cat <<EOF
recovery_partition=$recovery_partition
recovery_disk=/dev/fixture-recovery-disk
recovery_partition_parent=$recovery_partition_parent
recovery_fs_uuid=$recovery_fs_uuid
recovery_partuuid=$recovery_partuuid
recovery_type=ntfs
recovery_model=$recovery_model
recovery_serial=$recovery_serial
recovery_wwn=$recovery_wwn
recovery_major_minor=8:0
k3s_partition=/dev/fixture-k3s-partition
k3s_disk=/dev/fixture-k3s-disk
k3s_partition_parent=/dev/fixture-k3s-disk
k3s_fs_uuid=b86086ef-2b3c-4638-abcf-fc7f137dcb97
k3s_partuuid=b081b955-d6bc-442c-ac0f-db76560a5245
k3s_type=ext4
k3s_model=CT250MX500SSD1
k3s_serial=21132DF28BD1
k3s_wwn=0x500a07512df28bd1
k3s_major_minor=$k3s_major_minor
smart_health=$smart_health
smart_reallocated=$smart_reallocated
smart_pending=$smart_pending
smart_uncorrectable=$smart_uncorrectable
ntfs_probe=$ntfs_probe
EOF
}
validator_state_evidence() {
local fixture="$1" mode="$2"
if [[ "$mode" == open ]]; then
local outer_source=/dev/fixture-recovery-partition
local outer_options=rw,nodev,nosuid,noexec,uid=1000,gid=1000,umask=077
local mapping_type=LUKS2 mapping_device=/dev/fixture-loop inner_label=K3S_RECOVERY
case "$fixture" in
open_wrong_outer_source) outer_source=/dev/fixture-other-partition ;;
open_plain_mapping) mapping_type=plain ;;
open_wrong_backing_file) mapping_device=/dev/fixture-other-loop ;;
open_wrong_inner_label) inner_label=WRONG_LABEL ;;
open_wrong_mount_options) outer_options=rw,nodev,nosuid,uid=1000,gid=1000,umask=077 ;;
esac
cat <<EOF
outer_mounted=1
outer_source=$outer_source
outer_fstype=ntfs3
outer_options=$outer_options
inner_mounted=1
inner_source=/dev/mapper/k3s-recovery
inner_fstype=ext4
inner_options=rw,nodev,nosuid,noexec
container_kind=regular
container_symlink=0
container_uid=1000
container_gid=1000
container_mode=600
container_size=34359738368
container_allocated=34359738368
loop_count=1
loop_device=/dev/fixture-loop
mapping_present=1
mapping_type=$mapping_type
mapping_device=$mapping_device
proof_mapping_present=0
inner_type=ext4
inner_label=$inner_label
inner_root_uid=0
inner_root_gid=0
inner_root_mode=700
inner_root_kind=directory
container_chain_pinned=1
container_inode=4242
container_device=8:3
loop_back_inode=4242
loop_back_major_minor=8:3
outer_major_minor=8:3
loop_major_minor=7:0
mapping_loop_major_minor=7:0
mapping_major_minor=253:0
inner_major_minor=253:0
loop_offset=0
loop_sizelimit=0
snapshot_stable=1
EOF
else
local loop_count=0 mapping_present=0
[[ "$fixture" == unexpected_loop ]] && loop_count=1
[[ "$fixture" == unexpected_mapping ]] && mapping_present=1
cat <<'EOF'
outer_mounted=0
inner_mounted=0
EOF
printf 'loop_count=%s\nmapping_present=%s\n' "$loop_count" "$mapping_present"
cat <<'EOF'
proof_mapping_present=0
EOF
fi
}
validator_outputs_are_sanitized() {
local stdout_file="$1" stderr_file="$2" protected
for protected in \
/dev/disk/by-id/wwn-0x500a0751e6aa6254 /dev/disk/by-id/wwn-0x500a0751e6aa6254-part3 \
/dev/disk/by-id/wwn-0x500a07512df28bd1 /dev/disk/by-id/wwn-0x500a07512df28bd1-part1 \
4EA0196C0C5FA27E 4670aa9f-9045-4bce-930d-9e84dfec9f38 CT1000MX500SSD1 2306E6AA6254 0x500a0751e6aa6254 \
b86086ef-2b3c-4638-abcf-fc7f137dcb97 b081b955-d6bc-442c-ac0f-db76560a5245 CT250MX500SSD1 21132DF28BD1 0x500a07512df28bd1 \
/dev/fixture-recovery-partition /dev/fixture-recovery-disk /dev/fixture-wrong-disk /dev/fixture-other-partition \
/dev/fixture-k3s-partition /dev/fixture-k3s-disk /dev/fixture-loop /dev/fixture-other-loop \
/dev/mapper/k3s-recovery /dev/mapper/k3s-recovery-proof \
/mnt/k3s-recovery-ssd /srv/recovery/k3s \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault/hyeonworks-recovery.kdbx \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/volume.env \
/srv/recovery/k3s/.latest-post-bundle.env \
/srv/recovery/k3s/k3s-secrets-encryption-20260801T000000Z/post \
HyeonworksRecovery HyeonworksRecovery/vault/hyeonworks-recovery.kdbx \
HyeonworksRecovery/containers/k3s-recovery.luks HyeonworksRecovery/volume.env \
hyeonworks-recovery.kdbx k3s-recovery.luks k3s-recovery-proof k3s-recovery K3S_RECOVERY \
.latest-post-bundle.env bundle.env verification.manifest k3s-secrets-encryption- \
AAAAAAAAAAAAAAAA aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa WRONGMODEL WRONGSERIAL \
0xaaaaaaaaaaaaaaaa WRONG_LABEL 8:0 8:32 8:3 8:9 7:0 7:9 253:0 34359738368; do
if /usr/bin/grep -Fq -- "$protected" "$stdout_file"; then
return 1
fi
if /usr/bin/grep -Fq -- "$protected" "$stderr_file"; then
return 1
fi
done
return 0
}
assert_validator_case() {
local fixture="$1" expected_rc="$2" expected_state="$3"
shift 3
local stdout_file="${fixture_root}/${fixture}.stdout" stderr_file="${fixture_root}/${fixture}.stderr"
local log_file="${fixture_root}/${fixture}.argv" output rc
set +e
run_validator_command_fixture "$fixture" "$stdout_file" "$stderr_file" "$log_file" "$@"
rc=$?
set -e
[[ "$rc" -eq "$expected_rc" ]] || fail "$fixture exit code (expected $expected_rc, got $rc)"
if (( expected_rc == 0 )); then
[[ ! -s "$stderr_file" ]] || fail "$fixture wrote unexpected stderr"
output="$(<"$stdout_file")"
assert_eq "Recovery device: match
Recovery state: ${expected_state}
Lineage: match
Latest bundle: $([[ "$*" == *--check-latest-bundle* ]] && printf verified || printf not_checked)" \
"$output" "$fixture success output"
fi
validator_outputs_are_sanitized "$stdout_file" "$stderr_file" || fail "$fixture leaked protected material"
return 0
}
review_focus="${K3SLR_REVIEW_FOCUS:-}"
probe_review1_ntfs_probe_argv() (
local argv_log="${fixture_root}/review1-ntfs-probe.argv"
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
_k3slrv_one_line() {
case "${*: -1}" in
"$K3SLR_RECOVERY_PARTITION_BY_ID") printf '%s\n' /dev/fixture-recovery-partition ;;
"$K3SLR_RECOVERY_DISK_BY_ID") printf '%s\n' /dev/fixture-recovery-disk ;;
"$K3SLR_K3S_PARTITION_BY_ID") printf '%s\n' /dev/fixture-k3s-partition ;;
"$K3SLR_K3S_DISK_BY_ID") printf '%s\n' /dev/fixture-k3s-disk ;;
*) return 1 ;;
esac
}
_k3slrv_lsblk() { printf '%s\n' fixture; }
_k3slrv_blkid() { printf '%s\n' fixture; }
_k3slrv_smart_evidence() {
printf '%s\n' smart_health=PASSED smart_reallocated=0 smart_pending=0 smart_uncorrectable=0
}
_k3slrv_findmnt_optional() { :; }
_k3slrv_findmnt_source_optional() { :; }
_k3slrv_root() { printf '%s\n' "$*" >"$argv_log"; }
_k3slrv_collect_device_evidence device_ready >/dev/null
[[ "$(<"$argv_log")" == '/usr/bin/ntfs-3g.probe --readwrite /dev/fixture-recovery-partition' ]]
)
probe_review1_blkid_argv() (
local argv_log="${fixture_root}/review1-blkid.argv"
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
_k3slrv_one_line_root() {
printf '%s\n' "$*" >"$argv_log"
printf '%s\n' fixture
}
_k3slrv_blkid UUID /dev/fixture-recovery-partition >/dev/null
[[ "$(<"$argv_log")" == '/usr/sbin/blkid --output value --match-tag UUID -- /dev/fixture-recovery-partition' ]]
)
probe_review1_ntfs3_effective_masks() (
local device_raw state_raw
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
device_raw="$(validator_device_evidence open_exact_lineage)"
state_raw="$(validator_state_evidence open_exact_lineage open)"
state_raw="${state_raw/umask=077/dmask=0077,fmask=0077}"
_k3slrv_validate_state open "$state_raw" "$device_raw"
)
probe_review1_initial_guard() (
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
declare -F _k3slrv_initial_guard >/dev/null || return 1
_k3slrv_initial_guard 1000 hB || return 1
! _k3slrv_initial_guard 0 hB
)
probe_review1_bash_x_rejected() (
local stdout_file="${fixture_root}/review1-xtrace.stdout"
local stderr_file="${fixture_root}/review1-xtrace.stderr" rc
set +e
/usr/bin/bash -x "$VALIDATOR_PATH" --help >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
[[ "$rc" -eq 1 && ! -s "$stdout_file" ]] || return 1
[[ "$(<"$stderr_file")" == *'Recovery validation refused'* ]] || return 1
[[ "$(<"$stderr_file")" != *'K3SLR_RECOVERY_FS_UUID'* ]]
)
probe_review1_manifest_symlink_escape() (
local root="${fixture_root}/review1-manifest-symlink" bundle outside hash
root="$(mktemp -d "${root}.XXXXXX")"
bundle="${root}/bundle"
outside="${root}/outside"
mkdir -p "$bundle/data" "$outside/parent"
chmod 0700 "$bundle" "$bundle/data" "$outside" "$outside/parent"
printf '%s\n' payload >"$bundle/data/payload"
printf '%s\n' escaped >"$outside/leaf"
printf '%s\n' escaped-parent >"$outside/parent/payload"
hash="$(/usr/bin/sha256sum "$bundle/data/payload")"; hash="${hash%% *}"
printf '%s %s\n' "$hash" './data/payload' >"$bundle/verification.manifest"
chmod 0600 "$bundle/verification.manifest" "$bundle/data/payload" "$outside/leaf" "$outside/parent/payload"
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
declare -F _k3slrv_verify_manifest_targets >/dev/null || return 1
_k3slrv_root() { "$@"; }
_k3slrv_verify_manifest_targets "$bundle" "$bundle/verification.manifest" || return 1
ln -s "$outside/leaf" "$bundle/leaf-link"
hash="$(/usr/bin/sha256sum "$outside/leaf")"; hash="${hash%% *}"
printf '%s %s\n' "$hash" './leaf-link' >"$bundle/verification.manifest"
! _k3slrv_verify_manifest_targets "$bundle" "$bundle/verification.manifest" || return 1
ln -s "$outside/parent" "$bundle/parent-link"
hash="$(/usr/bin/sha256sum "$outside/parent/payload")"; hash="${hash%% *}"
printf '%s %s\n' "$hash" './parent-link/payload' >"$bundle/verification.manifest"
! _k3slrv_verify_manifest_targets "$bundle" "$bundle/verification.manifest"
)
probe_review1_partition_mounted_elsewhere() (
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
_k3slrv_one_line() {
case "${*: -1}" in
"$K3SLR_RECOVERY_PARTITION_BY_ID") printf '%s\n' /dev/fixture-recovery-partition ;;
"$K3SLR_RECOVERY_DISK_BY_ID") printf '%s\n' /dev/fixture-recovery-disk ;;
"$K3SLR_K3S_PARTITION_BY_ID") printf '%s\n' /dev/fixture-k3s-partition ;;
"$K3SLR_K3S_DISK_BY_ID") printf '%s\n' /dev/fixture-k3s-disk ;;
*) return 1 ;;
esac
}
_k3slrv_lsblk() { printf '%s\n' fixture; }
_k3slrv_blkid() { printf '%s\n' fixture; }
_k3slrv_smart_evidence() {
printf '%s\n' smart_health=PASSED smart_reallocated=0 smart_pending=0 smart_uncorrectable=0
}
_k3slrv_findmnt_optional() { :; }
_k3slrv_findmnt_source_optional() { printf '%s\n' /mnt/fixture-unapproved; }
_k3slrv_root() { :; }
! _k3slrv_collect_device_evidence device_ready >/dev/null
)
probe_review1_open_lineage_snapshot() (
local good mutation key current line candidate device_raw state_raw
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
declare -F _k3slrv_validate_open_snapshot >/dev/null || return 1
good=$'container_chain_pinned=1\ncontainer_inode=4242\ncontainer_device=8:3\nloop_back_inode=4242\nloop_back_major_minor=8:3\nouter_major_minor=8:3\nloop_major_minor=7:0\nmapping_loop_major_minor=7:0\nmapping_major_minor=253:0\ninner_major_minor=253:0\nloop_offset=0\nloop_sizelimit=0\nsnapshot_stable=1'
_k3slrv_validate_open_snapshot "$good" || { printf 'review1 snapshot good failed\n' >&2; return 1; }
for mutation in \
'container_chain_pinned=0' \
'container_device=8:9' \
'loop_back_inode=9999' \
'loop_back_major_minor=8:9' \
'mapping_loop_major_minor=7:9' \
'mapping_major_minor=253:9' \
'loop_offset=1' \
'loop_sizelimit=1' \
'snapshot_stable=0'; do
key="${mutation%%=*}"
current=''
while IFS= read -r line; do [[ "$line" == "$key="* ]] && current="$line"; done <<<"$good"
[[ -n "$current" ]] || return 1
candidate="${good/"$current"/"$mutation"}"
! _k3slrv_validate_open_snapshot "$candidate" || { printf 'review1 snapshot mutation accepted: %s\n' "$mutation" >&2; return 1; }
done
device_raw="$(validator_device_evidence open_exact_lineage)"
state_raw="$(validator_state_evidence open_exact_lineage open)"
_k3slrv_validate_state open "$state_raw" "$device_raw" || { printf 'review1 state snapshot good failed\n' >&2; return 1; }
! _k3slrv_validate_state open "${state_raw/snapshot_stable=1/snapshot_stable=0}" "$device_raw" || {
printf 'review1 state snapshot drift accepted\n' >&2
return 1
}
)
probe_review1_predefined_parser_cannot_bypass() (
local marker="${fixture_root}/review1-parser-attacked" stdout_file="${fixture_root}/review1-parser.stdout"
local stderr_file="${fixture_root}/review1-parser.stderr" rc
export -f validator_device_evidence validator_state_evidence
set +e
/usr/bin/bash -c '
source "$1"
_k3slr_load_contract() { printf attacked >"$2"; return 1; }
_k3slrv_verify_trusted_binaries() { :; }
_k3slrv_require_cached_sudo() { :; }
_k3slrv_collect_device_evidence() { validator_device_evidence exact_device_ready; }
_k3slrv_collect_state_evidence() { validator_state_evidence closed_no_residue device_ready; }
k3slr_local_recovery_main --expect-device-ready
' review1-parser "$VALIDATOR_PATH" "$marker" >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
[[ "$rc" -eq 0 && ! -e "$marker" && ! -s "$stderr_file" ]] || return 1
[[ "$(<"$stdout_file")" == *'Recovery state: device_ready'* ]]
)
probe_review2_exported_source_cannot_intercept() (
local marker="${fixture_root}/review2-source-attacked"
local stdout_file="${fixture_root}/review2-source.stdout"
local stderr_file="${fixture_root}/review2-source.stderr" rc
export -f validator_device_evidence validator_state_evidence
source() {
printf '%s' attacked >"$K3SLR_SOURCE_ATTACK_MARKER"
return 1
}
export -f source
set +e
K3SLR_SOURCE_ATTACK_MARKER="$marker" /usr/bin/bash -c '
builtin source -- "$1"
_k3slrv_verify_trusted_binaries() { :; }
_k3slrv_require_cached_sudo() { :; }
_k3slrv_collect_device_evidence() { validator_device_evidence exact_device_ready; }
_k3slrv_collect_state_evidence() { validator_state_evidence closed_no_residue device_ready; }
k3slr_local_recovery_main --expect-device-ready
' review2-source "$VALIDATOR_PATH" >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
[[ "$rc" -eq 0 && ! -e "$marker" && ! -s "$stderr_file" ]] || return 1
[[ "$(<"$stdout_file")" == $'Recovery device: match\nRecovery state: device_ready\nLineage: match\nLatest bundle: not_checked' ]]
)
validator_command_fake() {
local scope="$1" command="$2" argument field='' device='' tag='' format='' target='' mapping value occurrence
local stat_mode=600 stat_size=34359738368 stat_blocks=67108864 stat_device=8:3
local smart_health=PASSED smart_reallocated=0 smart_pending=0 smart_uncorrectable=0
shift 2
{
printf '%s\t%s' "$scope" "$command"
for argument in "$@"; do printf '\t%s' "$argument"; done
printf '\n'
} >>"$K3SLR_FAKE_LOG"
case "$command" in
/usr/bin/test)
if [[ "${1-}" == -L ]]; then return 1; fi
return 0
;;
/usr/bin/stat)
while (( $# > 0 )); do
case "$1" in
--format=*) format="${1#--format=}"; shift ;;
--) shift; target="$1"; shift ;;
*) target="$1"; shift ;;
esac
done
case "$format|$target" in
'%u|%a|%F|'*) printf '%s\n' '0|755|regular file' ;;
'%d:%i|%F|/mnt/k3s-recovery-ssd') printf '%s\n' '2051:100|directory' ;;
'%d:%i|%F|/mnt/k3s-recovery-ssd/HyeonworksRecovery') printf '%s\n' '2051:101|directory' ;;
'%d:%i|%F|/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers') printf '%s\n' '2051:102|directory' ;;
'%d:%i|%F|/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks') printf '%s\n' '2051:4242|regular file' ;;
'%F|%u|%g|%a|%s|%b|%i|/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks'|\
'%F|%u|%g|%a|%s|%b|%i|%Hd:%Ld|/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks')
occurrence="$(/usr/bin/grep -Fc -- $'\t/usr/bin/stat\t--format=%F|%u|%g|%a|%s|%b|%i' "$K3SLR_FAKE_LOG")"
if (( occurrence >= 2 )); then
case "$K3SLR_FAKE_CASE" in
open_drift_container_mode) stat_mode=640 ;;
open_drift_container_size) stat_size=34359734272 ;;
open_drift_container_allocated) stat_blocks=67108856 ;;
esac
fi
if [[ "$format" == *'%Hd:%Ld' ]]; then
printf 'regular file|1000|1000|%s|%s|%s|4242|%s\n' \
"$stat_mode" "$stat_size" "$stat_blocks" "$stat_device"
else
printf 'regular file|1000|1000|%s|%s|%s|4242\n' \
"$stat_mode" "$stat_size" "$stat_blocks"
fi
;;
'%u|%g|%a|%F|/srv/recovery/k3s')
occurrence="$(/usr/bin/grep -Fc -- $'\t/usr/bin/stat\t--format=%u|%g|%a|%F\t--\t/srv/recovery/k3s' "$K3SLR_FAKE_LOG")"
if [[ "$K3SLR_FAKE_CASE" == open_drift_inner_root && "$occurrence" -ge 2 ]]; then
printf '%s\n' '0|0|750|directory'
else
printf '%s\n' '0|0|700|directory'
fi
;;
*) return 1 ;;
esac
;;
/usr/bin/sudo)
[[ "$*" == '--non-interactive --validate' ]]
;;
/usr/bin/readlink)
device="${*: -1}"
case "$device" in
/dev/disk/by-id/wwn-0x500a0751e6aa6254-part3) printf '%s\n' /dev/fixture-recovery-partition ;;
/dev/disk/by-id/wwn-0x500a0751e6aa6254) printf '%s\n' /dev/fixture-recovery-disk ;;
/dev/disk/by-id/wwn-0x500a07512df28bd1-part1) printf '%s\n' /dev/fixture-k3s-partition ;;
/dev/disk/by-id/wwn-0x500a07512df28bd1) printf '%s\n' /dev/fixture-k3s-disk ;;
*) printf '%s\n' "$device" ;;
esac
;;
/usr/bin/lsblk)
while (( $# > 0 )); do
if [[ "$1" == --output ]]; then field="$2"; shift 2; else device="$1"; shift; fi
done
case "$field|$device" in
'PKNAME|/dev/fixture-recovery-partition')
if [[ "$K3SLR_FAKE_CASE" == wrong_by_id_target ]]; then printf '%s\n' /dev/fixture-wrong-disk; else printf '%s\n' /dev/fixture-recovery-disk; fi
;;
'PKNAME|/dev/fixture-k3s-partition') printf '%s\n' /dev/fixture-k3s-disk ;;
'MODEL|/dev/fixture-recovery-disk')
if [[ "$K3SLR_FAKE_CASE" == wrong_model ]]; then printf '%s\n' WRONGMODEL; else printf '%s\n' CT1000MX500SSD1; fi
;;
'SERIAL|/dev/fixture-recovery-disk')
if [[ "$K3SLR_FAKE_CASE" == wrong_serial ]]; then printf '%s\n' WRONGSERIAL; else printf '%s\n' 2306E6AA6254; fi
;;
'WWN|/dev/fixture-recovery-disk')
if [[ "$K3SLR_FAKE_CASE" == wrong_wwn ]]; then printf '%s\n' 0xaaaaaaaaaaaaaaaa; else printf '%s\n' 0x500a0751e6aa6254; fi
;;
'MAJ:MIN|/dev/fixture-recovery-disk') printf '%s\n' 8:0 ;;
'MODEL|/dev/fixture-k3s-disk') printf '%s\n' CT250MX500SSD1 ;;
'SERIAL|/dev/fixture-k3s-disk') printf '%s\n' 21132DF28BD1 ;;
'WWN|/dev/fixture-k3s-disk') printf '%s\n' 0x500a07512df28bd1 ;;
'MAJ:MIN|/dev/fixture-k3s-disk')
if [[ "$K3SLR_FAKE_CASE" == same_parent_disk ]]; then printf '%s\n' 8:0; else printf '%s\n' 8:32; fi
;;
'MAJ:MIN|/dev/fixture-loop') printf '%s\n' 7:0 ;;
'MAJ:MIN|/dev/fixture-other-loop') printf '%s\n' 7:9 ;;
'MAJ:MIN|/dev/mapper/k3s-recovery') printf '%s\n' 253:0 ;;
*) return 1 ;;
esac
;;
/usr/sbin/blkid)
while (( $# > 0 )); do
if [[ "$1" == --match-tag ]]; then tag="$2"; shift 2; else device="$1"; shift; fi
done
case "$tag|$device" in
'UUID|/dev/fixture-recovery-partition')
if [[ "$K3SLR_FAKE_CASE" == wrong_fs_uuid ]]; then printf '%s\n' AAAAAAAAAAAAAAAA; else printf '%s\n' 4EA0196C0C5FA27E; fi
;;
'PARTUUID|/dev/fixture-recovery-partition')
if [[ "$K3SLR_FAKE_CASE" == wrong_partuuid ]]; then printf '%s\n' aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa; else printf '%s\n' 4670aa9f-9045-4bce-930d-9e84dfec9f38; fi
;;
'TYPE|/dev/fixture-recovery-partition') printf '%s\n' ntfs ;;
'UUID|/dev/fixture-k3s-partition') printf '%s\n' b86086ef-2b3c-4638-abcf-fc7f137dcb97 ;;
'PARTUUID|/dev/fixture-k3s-partition') printf '%s\n' b081b955-d6bc-442c-ac0f-db76560a5245 ;;
'TYPE|/dev/fixture-k3s-partition') printf '%s\n' ext4 ;;
'TYPE|/dev/mapper/k3s-recovery') printf '%s\n' ext4 ;;
'LABEL|/dev/mapper/k3s-recovery')
occurrence="$(/usr/bin/grep -Fc -- $'\t/usr/sbin/blkid\t--output\tvalue\t--match-tag\tLABEL\t--\t/dev/mapper/k3s-recovery' "$K3SLR_FAKE_LOG")"
if [[ "$K3SLR_FAKE_CASE" == open_wrong_inner_label ||
( "$K3SLR_FAKE_CASE" == open_drift_inner_label && "$occurrence" -ge 2 ) ]]; then
printf '%s\n' WRONG_LABEL
else
printf '%s\n' K3S_RECOVERY
fi
;;
*) return 1 ;;
esac
;;
/usr/sbin/smartctl)
case "$K3SLR_FAKE_CASE" in
smart_health_failed) smart_health=FAILED ;;
smart_reallocated_nonzero) smart_reallocated=1 ;;
smart_pending_nonzero) smart_pending=1 ;;
smart_uncorrectable_nonzero) smart_uncorrectable=1 ;;
esac
printf '%s\n' \
"SMART overall-health self-assessment test result: ${smart_health}" \
" 5 Reallocated_Sector_Ct 0x0033 100 100 010 Pre-fail Always - ${smart_reallocated}" \
"197 Current_Pending_Sector 0x0012 100 100 000 Old_age Always - ${smart_pending}" \
"198 Offline_Uncorrectable 0x0010 100 100 000 Old_age Offline - ${smart_uncorrectable}"
;;
/usr/bin/findmnt)
while (( $# > 0 )); do
case "$1" in
--mountpoint|--source) field="$1"; target="$2"; shift 2 ;;
*) shift ;;
esac
done
if [[ "$field" == --source ]]; then
[[ "$K3SLR_FAKE_CASE" == open_* ]] || return 1
printf '%s\n' /mnt/k3s-recovery-ssd
elif [[ "$field" == --mountpoint && "$K3SLR_FAKE_CASE" == open_* ]]; then
case "$target" in
/mnt/k3s-recovery-ssd)
if [[ "$K3SLR_FAKE_CASE" == open_wrong_outer_source ]]; then device=/dev/fixture-other-partition; else device=/dev/fixture-recovery-partition; fi
if [[ "$K3SLR_FAKE_CASE" == open_wrong_mount_options ]]; then
value='rw,nodev,nosuid,uid=1000,gid=1000,dmask=0077,fmask=0077'
else
value='rw,nodev,nosuid,noexec,uid=1000,gid=1000,dmask=0077,fmask=0077'
fi
if [[ "$K3SLR_FAKE_CASE" == open_wrong_container_device ]]; then field=8:9; else field=8:3; fi
printf '%s %s %s %s %s\n' "$device" ntfs3 "$value" 41 "$field"
;;
/srv/recovery/k3s) printf '%s\n' '/dev/mapper/k3s-recovery ext4 rw,nodev,nosuid,noexec 42 253:0' ;;
*) return 1 ;;
esac
else
return 1
fi
;;
/usr/bin/ntfs-3g.probe)
[[ "$#" -eq 2 && "$1" == --readwrite && "$2" == /dev/fixture-recovery-partition ]] || return 2
[[ "$K3SLR_FAKE_CASE" != ntfs_dirty_or_hibernated ]]
;;
/usr/sbin/losetup)
if [[ "$K3SLR_FAKE_CASE" == open_* ]]; then
if [[ "$K3SLR_FAKE_CASE" == open_wrong_container_device ]]; then field=8:9; else field=8:3; fi
printf '/dev/fixture-loop 4242 %s 7:0 0 0\n' "$field"
elif [[ "$K3SLR_FAKE_CASE" == unexpected_loop ]]; then
printf '%s\n' /dev/fixture-loop
fi
;;
/usr/sbin/cryptsetup)
mapping="${*: -1}"
if [[ "$mapping" == k3s-recovery-proof ]]; then return 4; fi
if [[ "$K3SLR_FAKE_CASE" == open_* || "$K3SLR_FAKE_CASE" == unexpected_mapping ]]; then
if [[ "$K3SLR_FAKE_CASE" == open_plain_mapping ]]; then value=plain; else value=LUKS2; fi
if [[ "$K3SLR_FAKE_CASE" == open_wrong_backing_file ]]; then device=/dev/fixture-other-loop; else device=/dev/fixture-loop; fi
printf '%s\n' "/dev/mapper/k3s-recovery is active and is in use." " type: ${value}" " device: ${device}"
else
return 4
fi
;;
*) return 1 ;;
esac
}
run_validator_command_fixture() {
local fixture="$1" stdout_file="$2" stderr_file="$3" log_file="$4"
shift 4
: >"$log_file"
export -f validator_command_fake
K3SLR_FAKE_CASE="$fixture" K3SLR_FAKE_LOG="$log_file" /usr/bin/bash -c '
source "$1"; shift
_k3slrv_exec() { validator_command_fake "$@"; }
k3slr_local_recovery_main "$@"
' review1-command "$VALIDATOR_PATH" "$@" >"$stdout_file" 2>"$stderr_file"
}
probe_review1_command_boundary_device_ready() (
local stdout_file="${fixture_root}/review1-command.stdout" stderr_file="${fixture_root}/review1-command.stderr"
local log_file="${fixture_root}/review1-command.log"
run_validator_command_fixture exact_device_ready "$stdout_file" "$stderr_file" "$log_file" --expect-device-ready || return 1
[[ ! -s "$stderr_file" && "$(<"$stdout_file")" == *'Recovery state: device_ready'* ]] || return 1
/usr/bin/grep -Fqx $'root\t/usr/bin/ntfs-3g.probe\t--readwrite\t/dev/fixture-recovery-partition' "$log_file" || return 1
/usr/bin/grep -Fqx $'root\t/usr/sbin/blkid\t--output\tvalue\t--match-tag\tUUID\t--\t/dev/fixture-recovery-partition' "$log_file"
)
probe_review1_command_boundary_open() (
local stdout_file="${fixture_root}/review1-command-open.stdout" stderr_file="${fixture_root}/review1-command-open.stderr"
local log_file="${fixture_root}/review1-command-open.log"
run_validator_command_fixture open_exact_lineage "$stdout_file" "$stderr_file" "$log_file" --expect-open || return 1
[[ ! -s "$stderr_file" && "$(<"$stdout_file")" == *'Recovery state: open'* ]] || return 1
/usr/bin/grep -Fqx $'root\t/usr/bin/findmnt\t--noheadings\t--raw\t--output\tSOURCE,FSTYPE,OPTIONS,ID,MAJ:MIN\t--mountpoint\t/mnt/k3s-recovery-ssd' "$log_file" || return 1
/usr/bin/grep -Fqx $'root\t/usr/bin/findmnt\t--noheadings\t--raw\t--output\tTARGET\t--source\t/dev/fixture-recovery-partition' "$log_file" || return 1
/usr/bin/grep -Fqx $'root\t/usr/sbin/losetup\t--list\t--associated\t/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks\t--noheadings\t--raw\t--output\tNAME,BACK-INO,BACK-MAJ:MIN,MAJ:MIN,OFFSET,SIZELIMIT' "$log_file" || return 1
/usr/bin/grep -Fqx $'root\t/usr/sbin/cryptsetup\tstatus\t--\tk3s-recovery' "$log_file" || return 1
/usr/bin/grep -Fqx $'root\t/usr/sbin/cryptsetup\tstatus\t--\tk3s-recovery-proof' "$log_file"
)
prepare_review1_latest_bundle() {
local case_name="$1" root="$2" inner relative bundle outside identity payload_hash metadata_hash
inner="${root}/inner"
relative='k3s-secrets-encryption-20260801T000000Z/post'
bundle="${inner}/${relative}"
outside="${root}/outside-payload"
mkdir -p -- "$bundle"
printf '%s\n' 'encrypted payload fixture' >"${bundle}/payload.bin"
printf '%s\n' \
'schema=platform-k3s-bundle-v1' \
'bundle_id=01234567-89ab-4cde-8fab-0123456789ab' \
'phase=post' \
'k3s_version=v1.36.2+k3s1' \
'datastore=sqlite' \
'created_at_utc=2026-08-01T00:00:00Z' \
'secret_count=1' >"${bundle}/bundle.env"
case "$case_name" in
malformed)
sed -i 's/^schema=.*/schema=not-the-approved-schema/' "${bundle}/bundle.env"
;;
duplicate)
printf '%s\n' 'phase=post' >>"${bundle}/bundle.env"
;;
valid|symlink_escape|hash_mismatch) ;;
*) return 1 ;;
esac
payload_hash="$(/usr/bin/sha256sum -- "${bundle}/payload.bin")"; payload_hash="${payload_hash%% *}"
metadata_hash="$(/usr/bin/sha256sum -- "${bundle}/bundle.env")"; metadata_hash="${metadata_hash%% *}"
if [[ "$case_name" == symlink_escape ]]; then
printf '%s\n' 'escaped payload fixture' >"$outside"
ln -s -- "$outside" "${bundle}/escaped-link"
payload_hash="$(/usr/bin/sha256sum -- "$outside")"; payload_hash="${payload_hash%% *}"
printf '%s %s\n' "$payload_hash" './escaped-link' >"${bundle}/verification.manifest"
else
[[ "$case_name" != hash_mismatch ]] || payload_hash='0000000000000000000000000000000000000000000000000000000000000000'
printf '%s %s\n%s %s\n' \
"$payload_hash" './payload.bin' "$metadata_hash" './bundle.env' >"${bundle}/verification.manifest"
fi
chmod 0700 -- "$bundle"
chmod 0600 -- "${bundle}/bundle.env" "${bundle}/verification.manifest" "${bundle}/payload.bin"
identity="$(/usr/bin/stat --format='%d:%i' -- "$bundle")"
printf '%s\n' \
'schema=k3slr-latest-post-bundle-v1' \
"relative_path=${relative}" \
"directory_identity=${identity}" >"${inner}/.latest-post-bundle.env"
chmod 0600 -- "${inner}/.latest-post-bundle.env"
}
run_review1_latest_bundle_case() (
local case_name="$1" expected_rc="$2" root inner rc
root="${fixture_root}/review1-latest-${case_name}"
mkdir -p -- "$root"
prepare_review1_latest_bundle "$case_name" "$root" || return 1
inner="${root}/inner"
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
K3SLR_INNER_MOUNT="$inner"
_k3slrv_exec() {
local scope="$1" command="$2" argument mode_kind
shift 2
[[ "$scope" == user || "$scope" == root ]] || return 1
if [[ "$command" == /usr/bin/stat && " $* " == *" --format=%u:%g:%a:%F "* ]]; then
mode_kind="$($command "${@/--format=%u:%g:%a:%F/--format=%a:%F}")" || return 1
printf '0:0:%s\n' "$mode_kind"
return 0
fi
"$command" "$@"
}
set +e
_k3slrv_verify_latest_bundle
rc=$?
set -e
[[ "$rc" -eq "$expected_rc" ]]
)
probe_review1_latest_bundle_actual() {
assert_succeeds run_review1_latest_bundle_case valid 0
assert_succeeds run_review1_latest_bundle_case malformed 1
assert_succeeds run_review1_latest_bundle_case duplicate 1
assert_succeeds run_review1_latest_bundle_case symlink_escape 1
assert_succeeds run_review1_latest_bundle_case hash_mismatch 1
}
run_review2_latest_main_case() (
local fixture_name="$1" expected_rc="$2" bundle_case="$3"
local root="${fixture_root}/review2-latest-main-${fixture_name}" inner
local stdout_file="${fixture_root}/review2-latest-main-${fixture_name}.stdout"
local stderr_file="${fixture_root}/review2-latest-main-${fixture_name}.stderr" rc output
mkdir -p -- "$root"
prepare_review1_latest_bundle "$bundle_case" "$root" || return 1
inner="${root}/inner"
export -f validator_device_evidence validator_state_evidence
set +e
K3SLR_TEST_INNER_MOUNT="$inner" /usr/bin/bash -c '
builtin source -- "$1"
_k3slrv_exec() {
local scope="$1" command="$2" mode_kind
shift 2
[[ "$scope" == user || "$scope" == root ]] || return 1
if [[ "$command" == /usr/bin/stat && " $* " == *" --format=%u:%g:%a:%F "* ]]; then
mode_kind="$($command "${@/--format=%u:%g:%a:%F/--format=%a:%F}")" || return 1
printf "0:0:%s\\n" "$mode_kind"
return 0
fi
"$command" "$@"
}
_k3slrv_verify_trusted_binaries() { :; }
_k3slrv_require_cached_sudo() { K3SLR_INNER_MOUNT="$K3SLR_TEST_INNER_MOUNT"; }
_k3slrv_collect_device_evidence() { validator_device_evidence open_exact_lineage; }
_k3slrv_collect_state_evidence() { validator_state_evidence open_exact_lineage open; }
k3slr_local_recovery_main --expect-open --check-latest-bundle
' review2-latest-main "$VALIDATOR_PATH" >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
if [[ "$rc" -ne "$expected_rc" ]]; then
printf 'task4 feasibility diagnostic: case=%s expected=%s actual=%s stderr=%s\n' \
"$fixture_case" "$expected_rc" "$rc" "$(<"$stderr_file")" >&2
/usr/bin/tail -n 30 "$TASK4_FEASIBILITY_COMMAND_LOG" >&2
return 1
fi
if (( expected_rc == 0 )); then
[[ ! -s "$stderr_file" ]] || return 1
output="$(<"$stdout_file")"
[[ "$output" == $'Recovery device: match\nRecovery state: open\nLineage: match\nLatest bundle: verified' ]] || return 1
else
[[ ! -s "$stdout_file" && "$(<"$stderr_file")" == 'Recovery validation failed' ]] || return 1
fi
validator_outputs_are_sanitized "$stdout_file" "$stderr_file"
)
probe_review2_latest_main_integration() {
assert_succeeds run_review2_latest_main_case latest_manifest_valid 0 valid
assert_succeeds run_review2_latest_main_case latest_manifest_invalid 1 hash_mismatch
}
probe_review1_record_preserving_evidence() (
local raw
local -A parsed=()
# shellcheck source=/dev/null
source "$VALIDATOR_PATH"
_k3slrv_exec() { printf 'fixture-value\n\n'; }
! _k3slrv_one_line /usr/bin/readlink -f -- /fixture || return 1
declare -F _k3slrv_capture >/dev/null || return 1
trailing_collector() { printf 'alpha=one\n\n'; }
_k3slrv_capture raw trailing_collector || return 1
! _k3slrv_parse_evidence "$raw" parsed alpha || return 1
! _k3slrv_parse_evidence $'alpha=one\nalpha=two' parsed alpha || return 1
! _k3slrv_parse_evidence $'alpha=one\nunknown=two' parsed alpha || return 1
! _k3slrv_parse_evidence $'alpha=one\r' parsed alpha || return 1
! _k3slrv_parse_evidence $'alpha=one\ncontinued' parsed alpha
)
probe_review1_leak_sentinels() (
local stdout_file="${fixture_root}/review1-leak.stdout" stderr_file="${fixture_root}/review1-leak.stderr"
: >"$stdout_file"
: >"$stderr_file"
validator_outputs_are_sanitized "$stdout_file" "$stderr_file" || return 1
printf '%s\n' '4670aa9f-9045-4bce-930d-9e84dfec9f38' >"$stdout_file"
! validator_outputs_are_sanitized "$stdout_file" "$stderr_file" || return 1
: >"$stdout_file"
printf '%s\n' '/srv/recovery/k3s/.latest-post-bundle.env' >"$stderr_file"
! validator_outputs_are_sanitized "$stdout_file" "$stderr_file"
)
assert_validator_case exact_device_ready 0 device_ready --expect-device-ready
assert_validator_case wrong_by_id_target 1 device_ready --expect-device-ready
assert_validator_case wrong_fs_uuid 1 device_ready --expect-device-ready
assert_validator_case wrong_partuuid 1 device_ready --expect-device-ready
assert_validator_case wrong_model 1 device_ready --expect-device-ready
assert_validator_case wrong_serial 1 device_ready --expect-device-ready
assert_validator_case wrong_wwn 1 device_ready --expect-device-ready
assert_validator_case same_parent_disk 1 device_ready --expect-device-ready
assert_validator_case smart_health_failed 1 device_ready --expect-device-ready
assert_validator_case smart_reallocated_nonzero 1 device_ready --expect-device-ready
assert_validator_case smart_pending_nonzero 1 device_ready --expect-device-ready
assert_validator_case smart_uncorrectable_nonzero 1 device_ready --expect-device-ready
assert_validator_case ntfs_dirty_or_hibernated 1 device_ready --expect-device-ready
assert_validator_case closed_no_residue 0 closed --expect-closed
assert_validator_case unexpected_loop 1 closed --expect-closed
assert_validator_case unexpected_mapping 1 closed --expect-closed
assert_validator_case open_exact_lineage 0 open --expect-open
assert_validator_case open_wrong_outer_source 1 open --expect-open
assert_validator_case open_plain_mapping 1 open --expect-open
assert_validator_case open_wrong_backing_file 1 open --expect-open
assert_validator_case open_wrong_inner_label 1 open --expect-open
assert_validator_case open_wrong_mount_options 1 open --expect-open
assert_validator_case open_wrong_container_device 1 open --expect-open
assert_validator_case open_drift_container_mode 1 open --expect-open
assert_validator_case open_drift_container_size 1 open --expect-open
assert_validator_case open_drift_container_allocated 1 open --expect-open
assert_validator_case open_drift_inner_label 1 open --expect-open
assert_validator_case open_drift_inner_root 1 open --expect-open
assert_validator_case exact_device_ready 2 device_ready
assert_validator_case exact_device_ready 2 device_ready --expect-open --expect-closed
assert_validator_case open_exact_lineage 2 open --expect-closed --check-latest-bundle
if [[ -z "$review_focus" || "$review_focus" == 1 ]]; then
assert_succeeds probe_review1_ntfs_probe_argv
fi
if [[ -z "$review_focus" || "$review_focus" == 2 ]]; then
assert_succeeds probe_review1_blkid_argv
fi
if [[ -z "$review_focus" || "$review_focus" == 3 ]]; then
assert_succeeds probe_review1_ntfs3_effective_masks
fi
if [[ -z "$review_focus" || "$review_focus" == 4 ]]; then
assert_succeeds probe_review1_initial_guard
fi
if [[ -z "$review_focus" || "$review_focus" == 5 ]]; then
assert_succeeds probe_review1_bash_x_rejected
fi
if [[ -z "$review_focus" || "$review_focus" == 6 ]]; then
assert_succeeds probe_review1_manifest_symlink_escape
fi
if [[ -z "$review_focus" || "$review_focus" == 7 ]]; then
assert_succeeds probe_review1_partition_mounted_elsewhere
fi
if [[ -z "$review_focus" || "$review_focus" == 8 ]]; then
assert_succeeds probe_review1_open_lineage_snapshot
fi
if [[ -z "$review_focus" || "$review_focus" == 9 ]]; then
assert_succeeds probe_review1_predefined_parser_cannot_bypass
fi
if [[ -z "$review_focus" || "$review_focus" == r2_9 ]]; then
assert_succeeds probe_review2_exported_source_cannot_intercept
fi
if [[ -z "$review_focus" || "$review_focus" == r2_latest ]]; then
assert_succeeds probe_review2_latest_main_integration
fi
if [[ -z "$review_focus" || "$review_focus" == 10 ]]; then
assert_succeeds probe_review1_command_boundary_device_ready
assert_succeeds probe_review1_command_boundary_open
assert_succeeds probe_review1_latest_bundle_actual
fi
if [[ -z "$review_focus" || "$review_focus" == m1 ]]; then
assert_succeeds probe_review1_record_preserving_evidence
fi
if [[ -z "$review_focus" || "$review_focus" == m2 ]]; then
assert_succeeds probe_review1_leak_sentinels
fi
task4_pipeline_contract() (
declare -F _k3slr_luks_format_from_keepass >/dev/null || return 1
declare -F _k3slr_luks_open_from_keepass >/dev/null || return 1
declare -F _k3slr_password_pipe_preflight >/dev/null || return 1
declare -F _k3slr_keepass_database_lineage_matches >/dev/null || return 1
)
task4_tty_and_secret_api_contract() (
local stdout_file="${fixture_root}/task4-nontty.stdout"
local stderr_file="${fixture_root}/task4-nontty.stderr"
declare -F _k3slr_keepass_password_stdout >/dev/null || return 1
! _k3slr_keepass_password_stdout /nonexistent "$K3SLR_KEEPASS_ENTRY" \
</dev/null >"$stdout_file" 2>"$stderr_file" || return 1
[[ ! -s "$stdout_file" ]] || return 1
declare -F _k3slr_keepass_password >/dev/null && return 1
declare -F _k3slr_read_keepass_password >/dev/null && return 1
if /usr/bin/rg -n \
'(^|[[:space:]])(local|declare|typeset)[^#]*(secret|password|passphrase)|read[^#]*(secret|password|passphrase)|KEEPASS[^[:space:]]*PASSWORD=|LUKS[^[:space:]]*PASSWORD=' \
"$LIBRARY_PATH" "$FEASIBILITY_PATH" >/dev/null 2>&1; then
return 1
fi
)
task4_feasibility_command_fake() {
local command="$1" operation path metadata process_identity=''
shift
if [[ ( "${TASK4_FEASIBILITY_CASE:-}" == signal_* ||
"${TASK4_FEASIBILITY_CASE:-}" == anchor_* ) &&
! -e "${TASK4_FEASIBILITY_MAIN_PID_FILE:-/nonexistent}" ]]; then
printf '%s\n' "$BASHPID" >"$TASK4_FEASIBILITY_MAIN_PID_FILE"
fi
printf '%s %s\n' "$command" "$*" >>"$TASK4_FEASIBILITY_COMMAND_LOG"
case "$command" in
/usr/bin/test)
if [[ "${1-}" == -t ]]; then return 0; fi
path="${*: -1}"
if [[ "$path" == /tmp/k3slr-feasibility.* ]]; then /usr/bin/test "$@"; else return 0; fi
;;
/usr/bin/stat)
path="${*: -1}"
if [[ "$path" == /tmp/k3slr-feasibility.* ]]; then
metadata="$(/usr/bin/stat "$@")" || return 1
if [[ -e "${TASK4_ATOMIC_MUTATED_MARKER:-/nonexistent}" &&
"$TASK4_FEASIBILITY_CASE" == atomic_wrong_owner && "$path" == */synthetic.kdbx ]]; then
printf '99999|%s\n' "${metadata#*|}"
elif [[ -e "${TASK4_ATOMIC_MUTATED_MARKER:-/nonexistent}" &&
"$TASK4_FEASIBILITY_CASE" == atomic_parent_drift && "$path" == "$TASK4_FEASIBILITY_REQUESTED_DIR" &&
! -s "${TASK4_ATOMIC_PARENT_STAT_LOG:-/nonexistent}" ]]; then
printf x >"$TASK4_ATOMIC_PARENT_STAT_LOG"
printf '%s|99:99\n' "${metadata%|*}"
else
printf '%s\n' "$metadata"
fi
else
printf '0|755|regular file\n'
fi
;;
/usr/bin/id|/usr/bin/mawk|/usr/bin/od|/usr/bin/ps|/usr/bin/sleep|/usr/bin/sha256sum|/usr/bin/cmp|/usr/bin/pkill)
if [[ -n "${TASK4_FEASIBILITY_ROLE_LOG:-}" &&
( "$command" == /usr/bin/mawk || "$command" == /usr/bin/sha256sum ) ]]; then
_k3slr_process_identity process_identity "$BASHPID" || return 1
printf '%s|%s\n' "${command##*/}" "$process_identity" >>"$TASK4_FEASIBILITY_ROLE_LOG"
fi
"$command" "$@"
;;
/usr/bin/readlink)
printf '%s\n' "${*: -1}"
;;
/usr/bin/dpkg-query)
case "${*: -1}" in
keepassxc)
case "$TASK4_FEASIBILITY_CASE" in
package_missing_lf) printf 'ii |2.7.6+dfsg.1-1build3' ;;
package_double_lf) printf 'ii |2.7.6+dfsg.1-1build3\n\n' ;;
*) printf 'ii |2.7.6+dfsg.1-1build3\n' ;;
esac
;;
cryptsetup-bin) printf 'ii |2:2.7.0-1ubuntu4.2\n' ;;
*) return 92 ;;
esac
;;
/usr/bin/mktemp)
/usr/bin/mkdir -- "$TASK4_FEASIBILITY_REQUESTED_DIR" || return 1
/usr/bin/chmod 0700 -- "$TASK4_FEASIBILITY_REQUESTED_DIR" || return 1
printf '%s\n' "$TASK4_FEASIBILITY_REQUESTED_DIR"
;;
/usr/bin/rm)
if [[ "$TASK4_FEASIBILITY_CASE" == cleanup_failure && "${1-}" == --recursive ]]; then
return 9
fi
/usr/bin/rm "$@"
;;
/usr/bin/sudo)
[[ "${1-}" == --non-interactive && "${2-}" == --validate ]]
;;
/usr/bin/keepassxc-cli)
operation="$1"
if [[ -n "${TASK4_FEASIBILITY_ROLE_LOG:-}" ]]; then
_k3slr_process_identity process_identity "$BASHPID" || return 1
printf 'keepass|%s\n' "$process_identity" >>"$TASK4_FEASIBILITY_ROLE_LOG"
fi
if [[ -n "${TASK4_CHILD_CMDLINE_LOG:-}" ]]; then
/usr/bin/tr '\0' '\n' <"/proc/${BASHPID}/cmdline" >>"$TASK4_CHILD_CMDLINE_LOG"
/usr/bin/tr '\0' '\n' <"/proc/${BASHPID}/environ" >>"$TASK4_CHILD_ENVIRON_LOG"
fi
if [[ "$TASK4_FEASIBILITY_CASE" == signal_* && "$operation" == db-create ]]; then
printf '%s\n' "$BASHPID" >"$TASK4_FEASIBILITY_CHILD_PID_FILE"
trap 'printf terminated >"$TASK4_FEASIBILITY_CHILD_TERM_FILE"; exit 143' TERM INT
while :; do /usr/bin/sleep 1; done
fi
if [[ "$TASK4_FEASIBILITY_CASE" == anchor_* && "$operation" == db-create ]]; then
printf '%s\n' "$BASHPID" >"$TASK4_FEASIBILITY_CHILD_PID_FILE"
trap '' TERM
printf ready >"$TASK4_FEASIBILITY_TERM_IGNORE_READY"
while :; do /usr/bin/sleep 1; done
fi
/usr/bin/mawk '{ next } END { exit 0 }' || return 1
case "$operation" in
db-create)
path="${*: -1}"
( set -o noclobber; printf 'synthetic-database\n' >"$path" ) || return 1
/usr/bin/chmod 0600 -- "$path"
;;
add)
path="${@: -2:1}"
printf 'synthetic-database-after-add\n' >"${path}.atomic"
if [[ "$TASK4_FEASIBILITY_CASE" == atomic_wrong_mode ]]; then
/usr/bin/chmod 0644 -- "${path}.atomic"
else
/usr/bin/chmod 0600 -- "${path}.atomic"
fi
/usr/bin/mv -- "${path}.atomic" "$path"
if [[ "$TASK4_FEASIBILITY_CASE" == atomic_symlink ]]; then
/usr/bin/unlink "$path"
/usr/bin/ln -s -- /dev/null "$path"
fi
if [[ -n "${TASK4_ATOMIC_MUTATED_MARKER:-}" ]]; then printf x >"$TASK4_ATOMIC_MUTATED_MARKER"; fi
;;
show)
[[ "$TASK4_FEASIBILITY_CASE" != synthetic_failure ]] || return 7
if (( $# == 6 )) && [[ "${2-}" == --show-protected &&
"${3-}" == --attributes && "${4-}" == Password &&
"${5-}" == */synthetic.kdbx && "${6-}" == "$K3SLR_KEEPASS_ENTRY" ]]; then
printf '%s\n' 'Zz9Yy8Xx7Ww6Vv5Uu4Tt3Ss2Rr1Qq0Pp8Oo7Nn6M'
elif (( $# == 7 )) && [[ "${2-}" == --quiet &&
"${3-}" == --attributes && "${4-}" == Title &&
"${5-}" == --show-attachments && "${6-}" == */synthetic.kdbx &&
"${7-}" == "$K3SLR_KEEPASS_ENTRY" ]]; then
if [[ -f "${6}.attachment-state" ]]; then
printf 'K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n'
else
printf 'K3s Recovery LUKS\n\nNo attachments present.\n'
fi
else
return 91
fi
;;
attachment-import)
path="$3"
printf 'synthetic-database-after-attachment\n' >"${path}.atomic"
/usr/bin/chmod 0600 -- "${path}.atomic"
/usr/bin/mv -- "${path}.atomic" "$path"
: >"${path}.attachment-state"
;;
attachment-export)
path="${*: -1}"
printf '%s\n' 'k3slr-attachment-roundtrip-v1' >"$path"
;;
*) return 91 ;;
esac
;;
*) return 90 ;;
esac
}
run_task4_feasibility_fixture() (
local fixture_case="$1" expected_rc="$2" requested_dir="$3"
local stdout_file="$4" stderr_file="$5" rc=0
# shellcheck source=/dev/null
source "$FEASIBILITY_PATH"
export TASK4_FEASIBILITY_CASE="$fixture_case"
export TASK4_FEASIBILITY_REQUESTED_DIR="$requested_dir"
export TASK4_FEASIBILITY_COMMAND_LOG="${fixture_root}/task4-${fixture_case}.commands"
: >"$TASK4_FEASIBILITY_COMMAND_LOG"
_k3slr_command() { task4_feasibility_command_fake "$@"; }
set +e
k3slr_local_recovery_feasibility_main --execute </dev/ptmx >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
if [[ "$rc" -ne "$expected_rc" ]]; then /usr/bin/tail -n 50 "$TASK4_FEASIBILITY_COMMAND_LOG" >&2; return 1; fi
if [[ "$fixture_case" == cleanup_failure ]]; then
[[ -d "$requested_dir" ]] || return 1
/usr/bin/rm -rf -- "$requested_dir"
else
if [[ -e "$requested_dir" ]]; then printf 'task4 feasibility diagnostic: fixture remains\n' >&2; return 1; fi
fi
if /usr/bin/grep -Fq -- 'Zz9Yy8Xx7Ww6Vv5Uu4Tt3Ss2Rr1Qq0Pp8Oo7Nn6M' "$stdout_file" "$stderr_file"; then
printf 'task4 feasibility diagnostic: sentinel output leak\n' >&2
return 1
fi
if [[ "$fixture_case" == success ]]; then
/usr/bin/grep -Fx -- "/usr/bin/keepassxc-cli db-create --quiet --set-password ${requested_dir}/synthetic.kdbx" "$TASK4_FEASIBILITY_COMMAND_LOG" >/dev/null || return 1
/usr/bin/grep -Fx -- "/usr/bin/keepassxc-cli add --quiet --generate --length 40 --lower --upper --numeric --every-group ${requested_dir}/synthetic.kdbx ${K3SLR_KEEPASS_ENTRY}" "$TASK4_FEASIBILITY_COMMAND_LOG" >/dev/null || return 1
/usr/bin/grep -Fx -- "/usr/bin/keepassxc-cli show --show-protected --attributes Password ${requested_dir}/synthetic.kdbx ${K3SLR_KEEPASS_ENTRY}" "$TASK4_FEASIBILITY_COMMAND_LOG" >/dev/null || return 1
[[ "$(/usr/bin/grep -Fxc -- "/usr/bin/keepassxc-cli show --quiet --attributes Title --show-attachments ${requested_dir}/synthetic.kdbx ${K3SLR_KEEPASS_ENTRY}" "$TASK4_FEASIBILITY_COMMAND_LOG")" -eq 2 ]] || return 1
/usr/bin/grep -Fx -- "/usr/bin/keepassxc-cli attachment-import --quiet ${requested_dir}/synthetic.kdbx ${K3SLR_KEEPASS_ENTRY} fixture.bin ${requested_dir}/input.bin" "$TASK4_FEASIBILITY_COMMAND_LOG" >/dev/null || return 1
/usr/bin/grep -E -- "^/usr/bin/keepassxc-cli attachment-export --quiet ${requested_dir}/synthetic\\.kdbx ${K3SLR_KEEPASS_ENTRY} fixture\\.bin /proc/[0-9]+/fd/[0-9]+$" "$TASK4_FEASIBILITY_COMMAND_LOG" >/dev/null || return 1
if [[ "$(( $(/usr/bin/wc -l <"$stdout_file") ))" -ne 1 ]]; then
printf 'task4 diagnostic: success stdout line count mismatch\n' >&2
return 1
fi
if [[ "$(<"$stdout_file")" != 'Local recovery feasibility: pass' ]]; then
printf 'task4 diagnostic: success stdout classification mismatch\n' >&2
return 1
fi
if [[ -s "$stderr_file" ]]; then
printf 'task4 diagnostic: success stderr is nonempty\n' >&2
return 1
fi
elif [[ "$fixture_case" == synthetic_failure || "$fixture_case" == cleanup_failure ]]; then
[[ ! -s "$stdout_file" && "$(<"$stderr_file")" == 'Local recovery feasibility failed' ]] || return 1
fi
return 0
)
task4_review1_blocking_child_is_reaped() (
local signal_name expected_rc requested_dir stdout_file stderr_file child_pid index rc killer_pid
local role pid ppid pgid start_time recorded_roles
# shellcheck source=/dev/null
source "$FEASIBILITY_PATH"
_k3slr_lifecycle_boundary() {
local phase="$1" tracked_pid="${2-}" tracked_identity=''
if [[ "$phase" == feasibility-pid-published && -n "${TASK4_FEASIBILITY_ROLE_LOG:-}" ]]; then
_k3slr_process_identity tracked_identity "$tracked_pid" || return 1
printf 'supervisor|%s\n' "$tracked_identity" >>"$TASK4_FEASIBILITY_ROLE_LOG"
fi
}
for signal_name in TERM INT; do
requested_dir="/tmp/k3slr-feasibility.signal${signal_name}${BASHPID}${RANDOM}"
stdout_file="${fixture_root}/task4-signal-${signal_name}.stdout"
stderr_file="${fixture_root}/task4-signal-${signal_name}.stderr"
export TASK4_FEASIBILITY_CASE="signal_${signal_name}"
export TASK4_FEASIBILITY_REQUESTED_DIR="$requested_dir"
export TASK4_FEASIBILITY_COMMAND_LOG="${fixture_root}/task4-signal-${signal_name}.commands"
export TASK4_FEASIBILITY_CHILD_PID_FILE="${fixture_root}/task4-signal-${signal_name}.pid"
export TASK4_FEASIBILITY_CHILD_TERM_FILE="${fixture_root}/task4-signal-${signal_name}.terminated"
export TASK4_FEASIBILITY_MAIN_PID_FILE="${fixture_root}/task4-signal-${signal_name}.main-pid"
export TASK4_FEASIBILITY_ROLE_LOG="${fixture_root}/task4-signal-${signal_name}.roles"
: >"$TASK4_FEASIBILITY_COMMAND_LOG"
: >"$TASK4_FEASIBILITY_ROLE_LOG"
_k3slr_command() { task4_feasibility_command_fake "$@"; }
(
for ((index=0; index<100; index++)); do
[[ -s "$TASK4_FEASIBILITY_CHILD_PID_FILE" && -s "$TASK4_FEASIBILITY_MAIN_PID_FILE" ]] && break
/usr/bin/sleep 0.05
done
[[ -s "$TASK4_FEASIBILITY_CHILD_PID_FILE" && -s "$TASK4_FEASIBILITY_MAIN_PID_FILE" ]] || exit 1
kill -"$signal_name" "$(<"$TASK4_FEASIBILITY_MAIN_PID_FILE")"
) &
killer_pid=$!
set +e
k3slr_local_recovery_feasibility_main --execute </dev/ptmx >"$stdout_file" 2>"$stderr_file"
rc=$?
wait "$killer_pid"
set -e
child_pid="$(<"$TASK4_FEASIBILITY_CHILD_PID_FILE")"
if [[ "$signal_name" == TERM ]]; then expected_rc=143; else expected_rc=130; fi
if [[ "$rc" -ne "$expected_rc" || ! -e "$TASK4_FEASIBILITY_CHILD_TERM_FILE" || -e "$requested_dir" ]] ||
kill -0 "$child_pid" 2>/dev/null; then
printf 'task4 signal diagnostic: signal=%s rc=%s expected=%s term=%s fixture=%s live=%s\n' \
"$signal_name" "$rc" "$expected_rc" "$([[ -e "$TASK4_FEASIBILITY_CHILD_TERM_FILE" ]] && printf yes || printf no)" \
"$([[ -e "$requested_dir" ]] && printf yes || printf no)" "$([[ -e /proc/$child_pid ]] && printf yes || printf no)" >&2
return 1
fi
recorded_roles=0
while IFS='|' read -r role pid start_time ppid pgid; do
[[ "$pid" =~ ^[1-9][0-9]*$ && "$ppid" =~ ^[0-9]+$ && "$pgid" =~ ^[1-9][0-9]*$ &&
"$start_time" =~ ^[1-9][0-9]*$ ]] || return 1
! kill -0 "$pid" 2>/dev/null || return 1
recorded_roles=$((recorded_roles + 1))
done <"$TASK4_FEASIBILITY_ROLE_LOG"
(( recorded_roles >= 3 )) || return 1
done
)
task4_feasibility_cleanup_and_leak_contract() (
local case_name expected_rc output error temporary uid
export TASK4_CHILD_CMDLINE_LOG="${fixture_root}/task4-child.cmdline"
export TASK4_CHILD_ENVIRON_LOG="${fixture_root}/task4-child.environ"
: >"$TASK4_CHILD_CMDLINE_LOG"
: >"$TASK4_CHILD_ENVIRON_LOG"
[[ -f "$FEASIBILITY_PATH" && ! -L "$FEASIBILITY_PATH" ]] || return 1
for case_name in success synthetic_failure cleanup_failure; do
case "$case_name" in
success) expected_rc=0 ;;
synthetic_failure|cleanup_failure) expected_rc=1 ;;
esac
temporary="/tmp/k3slr-feasibility.task4${BASHPID}${RANDOM}"
output="${fixture_root}/task4-${case_name}.stdout"
error="${fixture_root}/task4-${case_name}.stderr"
assert_succeeds run_task4_feasibility_fixture "$case_name" "$expected_rc" "$temporary" "$output" "$error"
done
uid="$(/usr/bin/id -u)"
if /usr/bin/find /tmp /run -xdev -user "$uid" \
-name '*Zz9Yy8Xx7Ww6Vv5Uu4Tt3Ss2Rr1Qq0Pp8Oo7Nn6M*' \
-print -quit 2>/dev/null | /usr/bin/grep -q .; then
return 1
fi
if /usr/bin/find /tmp /run -xdev -user "$uid" -type f -readable \
! -path "${fixture_root}/*" \
-exec /usr/bin/grep -IlF -- 'Zz9Yy8Xx7Ww6Vv5Uu4Tt3Ss2Rr1Qq0Pp8Oo7Nn6M' '{}' \; \
-print -quit 2>/dev/null | /usr/bin/grep -q .; then
return 1
fi
)
task4_review1_noclobber_lifecycle() (
local collision fixture_dir target
# shellcheck source=/dev/null
source "$FEASIBILITY_PATH"
export TASK4_FEASIBILITY_CASE=success
export TASK4_FEASIBILITY_COMMAND_LOG="${fixture_root}/task4-noclobber.commands"
_k3slr_command() { task4_feasibility_command_fake "$@"; }
for collision in preexisting symlink; do
fixture_dir="/tmp/k3slr-feasibility.noclobber${collision}${BASHPID}${RANDOM}"
/usr/bin/mkdir -m 0700 -- "$fixture_dir"
: >"$TASK4_FEASIBILITY_COMMAND_LOG"
if [[ "$collision" == preexisting ]]; then
target="${fixture_dir}/input.bin"
printf 'owned\n' >"$target"
else
target="${fixture_dir}/synthetic.kdbx"
/usr/bin/ln -s -- /dev/null "$target"
fi
K3SLRF_ACTIVE_CHILD_PID=''
assert_fails _k3slrf_run_synthetic "$fixture_dir"
[[ -e "$target" || -L "$target" ]] || return 1
! /usr/bin/grep -Fq -- '/usr/bin/keepassxc-cli ' "$TASK4_FEASIBILITY_COMMAND_LOG" || return 1
/usr/bin/rm -rf -- "$fixture_dir"
done
)
task4_review2_atomic_save_transition() (
local requested_dir="/tmp/k3slr-feasibility.atomic${BASHPID}${RANDOM}" atomic_case
local stdout_file="${fixture_root}/task4-review2-atomic.stdout" stderr_file="${fixture_root}/task4-review2-atomic.stderr"
export TASK4_ATOMIC_MUTATED_MARKER="${fixture_root}/task4-review2-atomic-mutated"
export TASK4_ATOMIC_PARENT_STAT_LOG="${fixture_root}/task4-review2-parent-stat"
/usr/bin/unlink "$TASK4_ATOMIC_MUTATED_MARKER" 2>/dev/null || true
: >"$TASK4_ATOMIC_PARENT_STAT_LOG"
assert_succeeds run_task4_feasibility_fixture success 0 "$requested_dir" "$stdout_file" "$stderr_file"
for atomic_case in atomic_symlink atomic_wrong_owner atomic_wrong_mode atomic_parent_drift; do
/usr/bin/unlink "$TASK4_ATOMIC_MUTATED_MARKER" 2>/dev/null || true
: >"$TASK4_ATOMIC_PARENT_STAT_LOG"
requested_dir="/tmp/k3slr-feasibility.atomicfault${BASHPID}${RANDOM}"
stdout_file="${fixture_root}/task4-review2-${atomic_case}.stdout"
stderr_file="${fixture_root}/task4-review2-${atomic_case}.stderr"
assert_succeeds run_task4_feasibility_fixture "$atomic_case" 1 "$requested_dir" "$stdout_file" "$stderr_file"
done
)
task4_review1_raw_package_record() (
local record_case
# shellcheck source=/dev/null
source "$FEASIBILITY_PATH"
export TASK4_FEASIBILITY_COMMAND_LOG="${fixture_root}/task4-package.commands"
: >"$TASK4_FEASIBILITY_COMMAND_LOG"
_k3slr_command() { task4_feasibility_command_fake "$@"; }
TASK4_FEASIBILITY_CASE=success
assert_succeeds _k3slrf_package_version_is_exact keepassxc '2.7.6+dfsg.1-1build3'
for record_case in package_missing_lf package_double_lf; do
TASK4_FEASIBILITY_CASE="$record_case"
assert_fails _k3slrf_package_version_is_exact keepassxc '2.7.6+dfsg.1-1build3'
done
)
task4_review1_command_fake() {
local command="$1" format path role pgid caller_pgid early_signal role_identity='' start_time=''
shift
printf '%s %s\n' "$command" "$*" >>"$TASK4_REVIEW1_COMMAND_LOG"
case "$command" in
/usr/bin/test)
if [[ "${1-}" == -t && "${TASK4_REVIEW3_SIGNAL_CASE:-}" == 1 &&
! -e "${TASK4_REVIEW3_CALLER_PID_FILE:-/nonexistent}" ]]; then
printf '%s\n' "$BASHPID" >"$TASK4_REVIEW3_CALLER_PID_FILE"
caller_pgid="$(/usr/bin/ps -o pgid= -p "$BASHPID")" || return 1
caller_pgid="${caller_pgid//[[:space:]]/}"
printf '%s\n' "$caller_pgid" >"$TASK4_REVIEW3_CALLER_PGID_FILE"
fi
if [[ "${1-}" == -t && "${TASK4_REVIEW2_REQUIRE_REAL_TTY:-}" == 1 ]]; then
printf 'tty-check pid=%s result=%s\n' "$BASHPID" "$([[ -t 0 ]] && printf tty || printf pipe)" >>"$TASK4_REVIEW1_COMMAND_LOG"
/usr/bin/test -t 0
return
fi
if [[ "${1-}" == -t && "${TASK4_REVIEW1_VALIDATION_CASE:-}" == non_tty ]]; then return 1; fi
if [[ "${1-}" == '!' && "${2-}" == -L && "${3-}" == "${K3SLR_OUTER_MOUNT}/${K3SLR_ROOT_RELATIVE}/vault" &&
"${TASK4_REVIEW1_VALIDATION_CASE:-}" == parent_symlink ]]; then return 1; fi
return 0
;;
/usr/bin/id)
[[ "$*" == '-u' ]] || return 97
printf '1000\n'
;;
/usr/bin/readlink)
printf '%s\n' "${*: -1}"
;;
/usr/bin/stat)
format="$1"
path="${*: -1}"
case "$format" in
--format=%u\|%a\|%F) printf '0|755|regular file\n' ;;
--format=%F) printf 'block special file\n' ;;
--format=%d:%i\|%F\|%u\|%a)
case "$path" in
"$K3SLR_OUTER_MOUNT") printf '8:10|directory|1000|700\n' ;;
"${K3SLR_OUTER_MOUNT}/${K3SLR_ROOT_RELATIVE}") printf '8:11|directory|1000|700\n' ;;
"${K3SLR_OUTER_MOUNT}/${K3SLR_ROOT_RELATIVE}/vault") printf '8:12|directory|1000|700\n' ;;
"${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}")
if [[ "${TASK4_REVIEW1_VALIDATION_CASE:-}" == post_drift ]]; then
printf 'x\n' >>"$TASK4_REVIEW1_DB_STAT_LOG"
if [[ "$(/usr/bin/wc -l <"$TASK4_REVIEW1_DB_STAT_LOG")" -gt 1 ]]; then
printf '8:99|regular file|1000|600\n'
else
printf '8:13|regular file|1000|600\n'
fi
else
printf '8:13|regular file|1000|600\n'
fi
;;
*) return 96 ;;
esac
;;
*) return 95 ;;
esac
;;
/usr/bin/keepassxc-cli)
if [[ -n "${TASK4_REVIEW4_IDENTITY_ROLE_LOG:-}" ]]; then
_k3slr_process_identity role_identity "$BASHPID" || return 1
printf 'keepass|%s\n' "$role_identity" >>"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
/usr/bin/sleep 0.05
fi
if [[ -n "${TASK4_CHILD_CMDLINE_LOG:-}" ]]; then
/usr/bin/tr '\0' '\n' <"/proc/${BASHPID}/cmdline" >>"$TASK4_CHILD_CMDLINE_LOG"
/usr/bin/tr '\0' '\n' <"/proc/${BASHPID}/environ" >>"$TASK4_CHILD_ENVIRON_LOG"
fi
printf '%s\n' "$*" >>"$TASK4_REVIEW1_PRODUCER_LOG"
if [[ "${TASK4_REVIEW2_REQUIRE_REAL_TTY:-}" == 1 ]]; then
/usr/bin/test -t 0 || return 88
fi
if [[ "$TASK4_REVIEW1_PRODUCER_CASE" == blocking_signal ]]; then
exec /usr/bin/bash -c '
pgid="$(/usr/bin/ps -o pgid= -p "$$")" || exit 1
pgid="${pgid//[[:space:]]/}"
IFS= read -r stat_record <"/proc/$$/stat" || exit 1
stat_tail="${stat_record##*) }"
read -r -a stat_fields <<<"$stat_tail"
start_time="${stat_fields[19]}"
printf "keepass %s %s %s %s\n" "$$" "$PPID" "$pgid" "$start_time" >>"$TASK4_REVIEW3_ROLE_LOG"
trap "exit 143" TERM
trap "exit 130" INT
while :; do :; done
'
fi
if [[ "$TASK4_REVIEW1_PRODUCER_CASE" == term_ignoring_descendant ]]; then
exec /usr/bin/bash -c '
trap "" TERM
printf ready >"$TASK4_REVIEW5_TERM_IGNORE_READY"
while :; do :; done
'
fi
case "$TASK4_REVIEW1_PRODUCER_CASE" in
partial_failure) printf '%s' partial; return 7 ;;
zero_failure) return 8 ;;
zero_success) return 0 ;;
missing_lf) printf '%s' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N' ;;
double_lf) printf '%s\n\n' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N' ;;
invalid_class) printf '%s\n' 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' ;;
malformed_length) printf '%s\n' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm' ;;
oversized_success) /usr/bin/mawk 'BEGIN { for (i=0; i<200000; i++) printf "X"; printf "\n" }' ;;
success) printf '%s\n' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N' ;;
*) return 94 ;;
esac
;;
/usr/bin/sudo)
if [[ "${1-}" == --non-interactive && "${2-}" == --validate ]]; then return 0; fi
printf 'dispatch\n' >>"$TASK4_REVIEW1_CONSUMER_LOG"
/usr/bin/mawk 'END { exit 0 }'
;;
/usr/bin/pkill)
/usr/bin/pkill "$@"
;;
/usr/bin/ps)
if [[ "${TASK4_REVIEW3_EARLY_SIGNAL:-}" =~ ^(INT|TERM)$ && "${1-} ${2-}" == '-o stat=' ]]; then
early_signal="$TASK4_REVIEW3_EARLY_SIGNAL"
TASK4_REVIEW3_EARLY_SIGNAL=''
printf '%s\n' "${*: -1}" >"$TASK4_REVIEW3_SUPERVISOR_PID_FILE"
/usr/bin/ps -o pgid= -p "${*: -1}" | /usr/bin/tr -d '[:space:]' >"$TASK4_REVIEW3_SUPERVISOR_PGID_FILE"
kill -"$early_signal" "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
fi
/usr/bin/ps "$@"
;;
/usr/bin/sleep)
/usr/bin/sleep "$@"
;;
/usr/bin/od|/usr/bin/mawk|/usr/bin/dd)
if [[ -n "${TASK4_REVIEW4_IDENTITY_ROLE_LOG:-}" && "$command" != /usr/bin/dd ]]; then
role="${command##*/}"
_k3slr_process_identity role_identity "$BASHPID" || return 1
printf '%s|%s\n' "$role" "$role_identity" >>"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
fi
if [[ "$TASK4_REVIEW1_PRODUCER_CASE" == blocking_signal && "$command" != /usr/bin/dd ]]; then
role="${command##*/}"
pgid="$(/usr/bin/ps -o pgid= -p "$BASHPID")" || return 1
pgid="${pgid//[[:space:]]/}"
_k3slr_process_identity role_identity "$BASHPID" || return 1
IFS='|' read -r _ start_time _ _ <<<"$role_identity"
printf '%s %s %s %s %s\n' "$role" "$BASHPID" "$PPID" "$pgid" "$start_time" >>"$TASK4_REVIEW3_ROLE_LOG"
exec "$command" "$@"
fi
"$command" "$@"
;;
*) return 93 ;;
esac
}
task4_review1_producer_must_finish_before_consumer() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
export TASK4_REVIEW1_CONSUMER_LOG="${fixture_root}/task4-review1-consumer.log"
export TASK4_REVIEW1_PRODUCER_LOG="${fixture_root}/task4-review1-producer.log"
export TASK4_REVIEW1_COMMAND_LOG="${fixture_root}/task4-review1-command.log"
export TASK4_REVIEW1_DB_STAT_LOG="${fixture_root}/task4-review1-db-stat.log"
export TASK4_CHILD_CMDLINE_LOG="${fixture_root}/task4-child.cmdline"
export TASK4_CHILD_ENVIRON_LOG="${fixture_root}/task4-child.environ"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_CHILD_CMDLINE_LOG"
: >"$TASK4_CHILD_ENVIRON_LOG"
_k3slr_command() { task4_review1_command_fake "$@"; }
TASK4_REVIEW1_PRODUCER_CASE=partial_failure
export TASK4_REVIEW1_PRODUCER_CASE
assert_fails _k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME"
[[ ! -s "$TASK4_REVIEW1_CONSUMER_LOG" ]] || return 1
TASK4_REVIEW1_PRODUCER_CASE=zero_failure
export TASK4_REVIEW1_PRODUCER_CASE
assert_fails _k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME"
[[ ! -s "$TASK4_REVIEW1_CONSUMER_LOG" ]] || return 1
TASK4_REVIEW1_PRODUCER_CASE=success
export TASK4_REVIEW1_PRODUCER_CASE
if ! _k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME"; then
printf 'task4 critical diagnostic: producer=%s consumer=%s\n' \
"$(/usr/bin/wc -l <"$TASK4_REVIEW1_PRODUCER_LOG")" \
"$(/usr/bin/wc -l <"$TASK4_REVIEW1_CONSUMER_LOG")" >&2
/usr/bin/tail -n 20 "$TASK4_REVIEW1_COMMAND_LOG" >&2
return 1
fi
[[ "$(/usr/bin/wc -l <"$TASK4_REVIEW1_CONSUMER_LOG")" -eq 1 ]] || return 1
[[ "$(/usr/bin/tail -n 1 "$TASK4_REVIEW1_PRODUCER_LOG")" == \
"show --show-protected --attributes Password ${database} ${K3SLR_KEEPASS_ENTRY}" ]]
)
task4_review3_cleanup_recorded_roles() {
local role pid ppid pgid start_time expected_identity current_identity caller_pgid cleanup_rc=0
[[ -f "$1" ]] || return 0
caller_pgid="$(/usr/bin/ps -o pgid= -p "$BASHPID")" || return 1
caller_pgid="${caller_pgid//[[:space:]]/}"
while read -r role pid ppid pgid start_time; do
[[ "$pid" =~ ^[1-9][0-9]*$ ]] || continue
expected_identity="${pid}|${start_time}|${ppid}|${pgid}"
if _k3slr_process_identity current_identity "$pid" 2>/dev/null &&
[[ "$current_identity" == "$expected_identity" && "$pgid" != "$caller_pgid" ]]; then
builtin kill -TERM "$pid" 2>/dev/null || cleanup_rc=1
fi
done <"$1"
/usr/bin/sleep 0.1
while read -r role pid ppid pgid start_time; do
[[ "$pid" =~ ^[1-9][0-9]*$ ]] || continue
expected_identity="${pid}|${start_time}|${ppid}|${pgid}"
if _k3slr_process_identity current_identity "$pid" 2>/dev/null &&
[[ "$current_identity" == "$expected_identity" && "$pgid" != "$caller_pgid" ]]; then
builtin kill -KILL "$pid" 2>/dev/null || cleanup_rc=1
fi
done <"$1"
return "$cleanup_rc"
}
task4_review3_nested_pipeline_signal_cleanup() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
local signal_name expected_rc iteration prefix killer_pid rc started_ns ended_ns elapsed_ms
local role pid ppid pgid start_time leaked dedicated_pgid caller_pgid
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1
export TASK4_REVIEW3_SIGNAL_CASE=1
TASK4_REVIEW1_PRODUCER_CASE=blocking_signal
export TASK4_REVIEW1_PRODUCER_CASE
_k3slr_command() { task4_review1_command_fake "$@"; }
for signal_name in INT TERM; do
if [[ "$signal_name" == INT ]]; then expected_rc=130; else expected_rc=143; fi
for iteration in 1 2; do
prefix="${fixture_root}/task4-review3-${signal_name}-${iteration}"
export TASK4_REVIEW1_CONSUMER_LOG="${prefix}.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${prefix}.producer"
export TASK4_REVIEW1_COMMAND_LOG="${prefix}.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${prefix}.db-stat"
export TASK4_REVIEW3_CALLER_PID_FILE="${prefix}.caller-pid"
export TASK4_REVIEW3_CALLER_PGID_FILE="${prefix}.caller-pgid"
export TASK4_REVIEW3_ROLE_LOG="${prefix}.roles"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_REVIEW3_ROLE_LOG"
/usr/bin/unlink "$TASK4_REVIEW3_CALLER_PID_FILE" 2>/dev/null || true
/usr/bin/unlink "$TASK4_REVIEW3_CALLER_PGID_FILE" 2>/dev/null || true
(
for ((probe=0; probe<200; probe++)); do
if [[ -s "$TASK4_REVIEW3_CALLER_PID_FILE" &&
"$(/usr/bin/wc -l <"$TASK4_REVIEW3_ROLE_LOG")" -ge 3 ]]; then break; fi
/usr/bin/sleep 0.01
done
[[ -s "$TASK4_REVIEW3_CALLER_PID_FILE" &&
"$(/usr/bin/wc -l <"$TASK4_REVIEW3_ROLE_LOG")" -ge 3 ]] || exit 1
kill -"$signal_name" "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
) &
killer_pid=$!
started_ns="$(/usr/bin/date +%s%N)"
set +e
_k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
rc=$?
wait "$killer_pid"
set -e
ended_ns="$(/usr/bin/date +%s%N)"
elapsed_ms=$(((ended_ns - started_ns) / 1000000))
leaked=0
dedicated_pgid=''
caller_pgid="$(<"$TASK4_REVIEW3_CALLER_PGID_FILE")"
while read -r role pid ppid pgid start_time; do
[[ "$role" == keepass || "$role" == od || "$role" == mawk ]] || continue
if [[ -z "$dedicated_pgid" ]]; then dedicated_pgid="$pgid"; fi
[[ "$pgid" == "$dedicated_pgid" ]] || leaked=$((leaked + 1))
if kill -0 "$pid" 2>/dev/null; then leaked=$((leaked + 1)); fi
done <"$TASK4_REVIEW3_ROLE_LOG"
if [[ "$rc" -ne "$expected_rc" || "$elapsed_ms" -ge 3000 ||
-s "$TASK4_REVIEW1_CONSUMER_LOG" || "$leaked" -ne 0 ||
-z "$dedicated_pgid" || "$dedicated_pgid" == "$caller_pgid" ]]; then
printf 'task4 review3 signal diagnostic: signal=%s iteration=%s rc=%s elapsed_ms=%s leaked=%s\n' \
"$signal_name" "$iteration" "$rc" "$elapsed_ms" "$leaked" >&2
/usr/bin/sed -n '1,20p' "$TASK4_REVIEW3_ROLE_LOG" >&2
/usr/bin/tail -n 40 "$TASK4_REVIEW1_COMMAND_LOG" >&2
while read -r role pid ppid pgid start_time; do
/usr/bin/ps -o pid=,ppid=,pgid=,stat=,args= -p "$pid" >&2 || true
done <"$TASK4_REVIEW3_ROLE_LOG"
task4_review3_cleanup_recorded_roles "$TASK4_REVIEW3_ROLE_LOG"
return 1
fi
done
done
)
task4_review3_launch_window_signal_cleanup() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
local signal_name expected_rc prefix rc started_ns ended_ns elapsed_ms supervisor_pid supervisor_pgid
local supervisor_identity current_identity caller_pgid cleanup_rc=0
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1
export TASK4_REVIEW3_SIGNAL_CASE=1
TASK4_REVIEW1_PRODUCER_CASE=blocking_signal
export TASK4_REVIEW1_PRODUCER_CASE
_k3slr_command() { task4_review1_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
local phase="$1" recorded_pgid='' recorded_identity=''
if [[ "$phase" == pid-published && "${TASK4_REVIEW3_EARLY_SIGNAL:-}" =~ ^(INT|TERM)$ ]]; then
printf '%s\n' "$producer_pid" >"$TASK4_REVIEW3_SUPERVISOR_PID_FILE"
recorded_pgid="$(/usr/bin/ps -o pgid= -p "$producer_pid")" || return 1
recorded_pgid="${recorded_pgid//[[:space:]]/}"
printf '%s\n' "$recorded_pgid" >"$TASK4_REVIEW3_SUPERVISOR_PGID_FILE"
_k3slr_process_identity recorded_identity "$producer_pid" || return 1
printf '%s\n' "$recorded_identity" >"$TASK4_REVIEW3_SUPERVISOR_IDENTITY_FILE"
kill -"$TASK4_REVIEW3_EARLY_SIGNAL" "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
TASK4_REVIEW3_EARLY_SIGNAL=''
fi
}
for signal_name in INT TERM; do
if [[ "$signal_name" == INT ]]; then expected_rc=130; else expected_rc=143; fi
prefix="${fixture_root}/task4-review3-early-${signal_name}"
export TASK4_REVIEW1_CONSUMER_LOG="${prefix}.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${prefix}.producer"
export TASK4_REVIEW1_COMMAND_LOG="${prefix}.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${prefix}.db-stat"
export TASK4_REVIEW3_CALLER_PID_FILE="${prefix}.caller-pid"
export TASK4_REVIEW3_CALLER_PGID_FILE="${prefix}.caller-pgid"
export TASK4_REVIEW3_ROLE_LOG="${prefix}.roles"
export TASK4_REVIEW3_SUPERVISOR_PID_FILE="${prefix}.supervisor-pid"
export TASK4_REVIEW3_SUPERVISOR_PGID_FILE="${prefix}.supervisor-pgid"
export TASK4_REVIEW3_SUPERVISOR_IDENTITY_FILE="${prefix}.supervisor-identity"
export TASK4_REVIEW3_EARLY_SIGNAL="$signal_name"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_REVIEW3_ROLE_LOG"
/usr/bin/unlink "$TASK4_REVIEW3_CALLER_PID_FILE" 2>/dev/null || true
/usr/bin/unlink "$TASK4_REVIEW3_CALLER_PGID_FILE" 2>/dev/null || true
started_ns="$(/usr/bin/date +%s%N)"
set +e
_k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
rc=$?
set -e
ended_ns="$(/usr/bin/date +%s%N)"
elapsed_ms=$(((ended_ns - started_ns) / 1000000))
supervisor_pid="$(<"$TASK4_REVIEW3_SUPERVISOR_PID_FILE")"
supervisor_pgid="$(<"$TASK4_REVIEW3_SUPERVISOR_PGID_FILE")"
supervisor_identity="$(<"$TASK4_REVIEW3_SUPERVISOR_IDENTITY_FILE")"
if [[ "$rc" -ne "$expected_rc" || "$elapsed_ms" -ge 3000 ||
-s "$TASK4_REVIEW1_CONSUMER_LOG" || -s "$TASK4_REVIEW3_ROLE_LOG" ]] ||
kill -0 "$supervisor_pid" 2>/dev/null; then
printf 'task4 review3 early diagnostic: signal=%s rc=%s elapsed_ms=%s supervisor=%s pgid=%s\n' \
"$signal_name" "$rc" "$elapsed_ms" "$supervisor_pid" "$supervisor_pgid" >&2
caller_pgid="$(/usr/bin/ps -o pgid= -p "$BASHPID")" || cleanup_rc=1
caller_pgid="${caller_pgid//[[:space:]]/}"
if _k3slr_process_identity current_identity "$supervisor_pid" 2>/dev/null &&
[[ "$current_identity" == "$supervisor_identity" && "$supervisor_pgid" != "$caller_pgid" ]]; then
builtin kill -KILL "$supervisor_pid" 2>/dev/null || cleanup_rc=1
fi
(( cleanup_rc == 0 )) || printf 'task4 review3 early cleanup failed\n' >&2
return 1
fi
done
)
task4_review4_stale_cached_group_boundary() (
local child_a unrelated_pid unrelated_pgid unrelated_identity='' caller_pgid supplied_identity=''
local current_identity='' current_pgid='' boundary_rc=0 killed=0 cleanup_rc=0 wait_rc
/usr/bin/setsid /usr/bin/sleep 30 &
unrelated_pid=$!
/usr/bin/sleep 0.05
_k3slr_process_identity unrelated_identity "$unrelated_pid" || return 1
unrelated_pgid="$(/usr/bin/ps -o pgid= -p "$unrelated_pid")" || return 1
unrelated_pgid="${unrelated_pgid//[[:space:]]/}"
caller_pgid="$(/usr/bin/ps -o pgid= -p "$BASHPID")" || return 1
caller_pgid="${caller_pgid//[[:space:]]/}"
[[ "$unrelated_pgid" =~ ^[1-9][0-9]*$ && "$unrelated_pgid" != "$caller_pgid" ]] || return 1
/usr/bin/sleep 30 &
child_a=$!
_k3slr_process_identity supplied_identity "$child_a" || return 1
set +e
_k3slr_terminate_and_reap "$child_a" "$unrelated_pgid" "$supplied_identity"
boundary_rc=$?
set -e
if ! kill -0 "$unrelated_pid" 2>/dev/null; then killed=1; fi
if kill -0 "$unrelated_pid" 2>/dev/null; then
_k3slr_process_identity current_identity "$unrelated_pid" 2>/dev/null || current_identity=''
current_pgid="$(/usr/bin/ps -o pgid= -p "$unrelated_pid" 2>/dev/null || true)"
current_pgid="${current_pgid//[[:space:]]/}"
caller_pgid="$(/usr/bin/ps -o pgid= -p "$BASHPID" 2>/dev/null || true)"
caller_pgid="${caller_pgid//[[:space:]]/}"
if [[ "$current_identity" == "$unrelated_identity" && "$current_pgid" == "$unrelated_pgid" &&
"$caller_pgid" =~ ^[1-9][0-9]*$ && "$current_pgid" != "$caller_pgid" ]]; then
builtin kill -TERM -- "-${unrelated_pgid}" 2>/dev/null || cleanup_rc=1
fi
fi
if wait "$unrelated_pid" 2>/dev/null; then wait_rc=0; else wait_rc=$?; fi
(( wait_rc != 127 )) || cleanup_rc=1
if _k3slr_process_identity current_identity "$child_a" 2>/dev/null &&
[[ "$current_identity" == "$supplied_identity" ]]; then
builtin kill -TERM "$child_a" 2>/dev/null || cleanup_rc=1
fi
if wait "$child_a" 2>/dev/null; then wait_rc=0; else wait_rc=$?; fi
(( wait_rc != 127 )) || cleanup_rc=1
(( cleanup_rc == 0 )) || return 1
if [[ "$killed" -ne 0 ]]; then
printf 'task4 review4 stale group RED: child=%s supplied_group=%s unrelated=killed boundary_rc=%s\n' \
"$child_a" "$unrelated_pgid" "$boundary_rc" >&2
return 1
fi
)
task4_review4_direct_fallback_boundaries() (
local mode child_pid child_identity='' child_group='' child_state='' attempt original_group_function
for mode in caller_group query_failure; do
( kill -STOP "$BASHPID"; printf 'unexpected child continuation\n' >&2 ) &
child_pid=$!
child_identity=''
for ((attempt=0; attempt<200; attempt++)); do
if _k3slr_process_record child_identity child_state "$child_pid" && [[ "$child_state" == T ]]; then break; fi
/usr/bin/sleep 0.01
done
[[ -n "$child_identity" && "$child_state" == T ]] || return 1
IFS='|' read -r _ _ _ child_group <<<"$child_identity"
if [[ "$mode" == query_failure ]]; then
original_group_function="$(declare -f _k3slr_process_group_for_pid)"
_k3slr_process_group_for_pid() { return 1; }
fi
_k3slr_terminate_and_reap "$child_pid" "$child_group" "$child_identity" || return 1
if [[ "$mode" == query_failure ]]; then eval "$original_group_function"; fi
if kill -0 "$child_pid" 2>/dev/null; then return 1; fi
done
)
task4_review4_term_ignoring_group_escalates() (
local supervisor_pid supervisor_pgid='' supervisor_identity='' descendant_pid='' state='' attempt
local descendant_file="${fixture_root}/task4-review4-term-ignoring-descendant"
: >"$descendant_file"
set -m
(
kill -STOP "$BASHPID"
trap '' TERM
( trap '' TERM; while :; do :; done ) &
printf '%s\n' "$!" >"$descendant_file"
wait
) &
supervisor_pid=$!
_k3slr_wait_for_stopped_group supervisor_pgid supervisor_identity "$supervisor_pid" || {
set +m
return 1
}
set +m
kill -CONT "$supervisor_pid" || return 1
for ((attempt=0; attempt<200; attempt++)); do
[[ -s "$descendant_file" ]] && break
/usr/bin/sleep 0.01
done
descendant_pid="$(<"$descendant_file")"
[[ "$descendant_pid" =~ ^[1-9][0-9]*$ ]] || return 1
_k3slr_terminate_and_reap "$supervisor_pid" "$supervisor_pgid" "$supervisor_identity" || return 1
! kill -0 "$supervisor_pid" 2>/dev/null || return 1
! kill -0 "$descendant_pid" 2>/dev/null || return 1
! _k3slr_group_exists "$supervisor_pgid"
)
task4_review4_wait_clear_is_atomic() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
local rc role pid ppid pgid start_time sentinel_pid sentinel_identity='' current_identity=''
local seen_supervisor=0 seen_process_sub=0 seen_keepass=0 seen_od=0 seen_mawk=0
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1 TASK4_REVIEW3_SIGNAL_CASE=1
export TASK4_REVIEW1_PRODUCER_CASE=success
export TASK4_REVIEW1_CONSUMER_LOG="${fixture_root}/task4-review4-wait-clear.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${fixture_root}/task4-review4-wait-clear.producer"
export TASK4_REVIEW1_COMMAND_LOG="${fixture_root}/task4-review4-wait-clear.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${fixture_root}/task4-review4-wait-clear.db-stat"
export TASK4_REVIEW3_CALLER_PID_FILE="${fixture_root}/task4-review4-wait-clear.caller"
export TASK4_REVIEW3_CALLER_PGID_FILE="${fixture_root}/task4-review4-wait-clear.caller-pgid"
export TASK4_REVIEW4_IDENTITY_ROLE_LOG="${fixture_root}/task4-review4-wait-clear.roles"
export TASK4_REVIEW4_NEGATIVE_KILL_LOG="${fixture_root}/task4-review4-wait-clear.negative-kill"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
: >"$TASK4_REVIEW4_NEGATIVE_KILL_LOG"
_k3slr_command() { task4_review1_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
local phase="$1" boundary_pid="${2-}" boundary_identity=''
case "$phase" in
pid-published)
_k3slr_process_identity boundary_identity "$boundary_pid" || return 1
printf 'supervisor|%s\n' "$boundary_identity" >>"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
;;
validator-stage-published)
_k3slr_process_identity boundary_identity "$boundary_pid" || return 1
printf 'process_sub|%s\n' "$boundary_identity" >>"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
;;
wait-reaped-before-clear)
builtin kill -TERM "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
;;
esac
}
kill() {
local argument
for argument in "$@"; do
if [[ "$argument" =~ ^-[1-9][0-9]*$ ]]; then printf '%s\n' "$argument" >>"$TASK4_REVIEW4_NEGATIVE_KILL_LOG"; fi
done
builtin kill "$@"
}
/usr/bin/setsid /usr/bin/sleep 30 &
sentinel_pid=$!
/usr/bin/sleep 0.02
_k3slr_process_identity sentinel_identity "$sentinel_pid" || return 1
set +e
_k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
rc=$?
set -e
[[ "$rc" -eq 143 && ! -s "$TASK4_REVIEW1_CONSUMER_LOG" &&
! -s "$TASK4_REVIEW4_NEGATIVE_KILL_LOG" ]] || return 1
while IFS='|' read -r role pid start_time ppid pgid; do
[[ "$pid" =~ ^[1-9][0-9]*$ && "$ppid" =~ ^[0-9]+$ && "$pgid" =~ ^[1-9][0-9]*$ &&
"$start_time" =~ ^[1-9][0-9]*$ ]] || return 1
case "$role" in
supervisor) seen_supervisor=1 ;;
process_sub) seen_process_sub=1 ;;
keepass) seen_keepass=1 ;;
od) seen_od=1 ;;
mawk) seen_mawk=1 ;;
*) return 1 ;;
esac
! kill -0 "$pid" 2>/dev/null || return 1
done <"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
(( seen_supervisor && seen_process_sub && seen_keepass && seen_od && seen_mawk )) || return 1
_k3slr_process_identity current_identity "$sentinel_pid" || return 1
[[ "$current_identity" == "$sentinel_identity" ]] || return 1
builtin kill -TERM "$sentinel_pid"
wait "$sentinel_pid" 2>/dev/null || true
)
task4_review4_signal_phase_matrix() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
local signal_name expected_rc injection_phase phase rc prefix supervisor_pid='' supervisor_pgid=''
local supervisor_identity='' sentinel_pid sentinel_identity='' current_identity='' role pid ppid pgid
local sentinel_start_time='' sentinel_parent='' sentinel_pgid='' fixture_cleanup_rc=0
local -a phases=(coproc-launch-before pid-published stop-query-complete cont-before cont-after \
wait-reaped-before-clear pre-consumer)
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1 TASK4_REVIEW3_SIGNAL_CASE=1
/usr/bin/setsid /usr/bin/sleep 60 &
sentinel_pid=$!
/usr/bin/sleep 0.02
_k3slr_process_identity sentinel_identity "$sentinel_pid" || return 1
IFS='|' read -r sentinel_pid sentinel_start_time sentinel_parent sentinel_pgid <<<"$sentinel_identity"
_task4_review4_phase_sentinel_cleanup() {
if _k3slr_process_identity current_identity "$sentinel_pid" 2>/dev/null &&
[[ "$current_identity" == "$sentinel_identity" ]]; then
_k3slr_signal_pid TERM "$sentinel_pid" "$sentinel_identity" "$sentinel_parent" \
"$sentinel_pgid" 2>/dev/null || return 1
if _k3slr_wait_child "$sentinel_pid"; then :; else [[ "$?" -ne 127 ]] || return 1; fi
fi
}
_task4_review4_phase_exit_cleanup() {
local original_status="$1" cleanup_status=0
trap - EXIT
if [[ -n "$prefix" ]]; then
_task4_review5_phase_fixture_cleanup "$prefix" || cleanup_status=1
fi
_task4_review4_phase_sentinel_cleanup || cleanup_status=1
(( cleanup_status == 0 )) || exit 1
exit "$original_status"
}
trap '_task4_review4_phase_exit_cleanup "$?"' EXIT
_k3slr_command() { task4_review1_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
local boundary_phase="$1" boundary_pid="${2-}" recorded_pgid=''
if [[ "$boundary_phase" == pid-published ]]; then
printf '%s\n' "$boundary_pid" >"${prefix}.supervisor-pid"
_k3slr_process_identity supervisor_identity "$boundary_pid" || return 1
printf '%s\n' "$supervisor_identity" >"${prefix}.supervisor-identity"
recorded_pgid="$(/usr/bin/ps -o pgid= -p "$boundary_pid")" || return 1
recorded_pgid="${recorded_pgid//[[:space:]]/}"
printf '%s\n' "$recorded_pgid" >"${prefix}.supervisor-pgid"
fi
if [[ "$boundary_phase" == "$injection_phase" ]]; then
printf 'injected\n' >"${prefix}.injected"
builtin kill -"$signal_name" "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
fi
}
for signal_name in INT TERM; do
if [[ "$signal_name" == INT ]]; then expected_rc=130; else expected_rc=143; fi
for phase in "${phases[@]}"; do
injection_phase="$phase"
prefix="${fixture_root}/task4-review4-phase-${signal_name}-${phase}"
export TASK4_REVIEW1_CONSUMER_LOG="${prefix}.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${prefix}.producer"
export TASK4_REVIEW1_COMMAND_LOG="${prefix}.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${prefix}.db-stat"
export TASK4_REVIEW3_CALLER_PID_FILE="${prefix}.caller-pid"
export TASK4_REVIEW3_CALLER_PGID_FILE="${prefix}.caller-pgid"
export TASK4_REVIEW3_ROLE_LOG="${prefix}.roles"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_REVIEW3_ROLE_LOG"
/usr/bin/unlink "${prefix}.injected" "${prefix}.supervisor-pid" \
"${prefix}.supervisor-pgid" "${prefix}.supervisor-identity" 2>/dev/null || true
if [[ "$phase" == wait-reaped-before-clear || "$phase" == pre-consumer ]]; then
TASK4_REVIEW1_PRODUCER_CASE=success
else
TASK4_REVIEW1_PRODUCER_CASE=blocking_signal
fi
export TASK4_REVIEW1_PRODUCER_CASE
set +e
_k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
rc=$?
set -e
if [[ "$rc" -ne "$expected_rc" || ! -s "${prefix}.injected" ||
-s "$TASK4_REVIEW1_CONSUMER_LOG" ]]; then
printf 'task4 review4 phase diagnostic: signal=%s phase=%s rc=%s expected=%s injected=%s consumer=%s\n' \
"$signal_name" "$phase" "$rc" "$expected_rc" "$([[ -s "${prefix}.injected" ]] && printf yes || printf no)" \
"$([[ -s "$TASK4_REVIEW1_CONSUMER_LOG" ]] && printf yes || printf no)" >&2
return 1
fi
if [[ "$phase" == coproc-launch-before || "$phase" == pid-published ||
"$phase" == stop-query-complete || "$phase" == cont-before ]] &&
[[ -s "$TASK4_REVIEW3_ROLE_LOG" ]]; then
printf 'task4 review5 pre-CONT worker dispatch: signal=%s phase=%s\n' \
"$signal_name" "$phase" >&2
return 1
fi
if [[ -s "${prefix}.supervisor-pid" ]]; then
supervisor_pid="$(<"${prefix}.supervisor-pid")"
supervisor_pgid="$(<"${prefix}.supervisor-pgid")"
if kill -0 "$supervisor_pid" 2>/dev/null || _k3slr_group_exists "$supervisor_pgid"; then
printf 'task4 review4 phase leak: signal=%s phase=%s supervisor=%s pgid=%s\n' \
"$signal_name" "$phase" "$supervisor_pid" "$supervisor_pgid" >&2
return 1
fi
fi
while read -r role pid ppid pgid start_time; do
[[ "$pid" =~ ^[1-9][0-9]*$ ]] || continue
! kill -0 "$pid" 2>/dev/null || return 1
done <"$TASK4_REVIEW3_ROLE_LOG"
_k3slr_process_identity current_identity "$sentinel_pid" || return 1
[[ "$current_identity" == "$sentinel_identity" ]] || return 1
done
done
return 0
)
task4_review5_exact_proc_record_contract() (
local raw_file="${fixture_root}/task4-review5-proc-stat" raw_record='' identity='' state='' live_identity=''
local valid='123 (comm with ) and ) delimiter) T 0 456 456 0 -1 4194560 1 2 3 4 5 6 7 8 9 10 11 12 18446744073709551615'
printf '%s\n' "$valid" >"$raw_file"
_k3slr_read_exact_record raw_record "$raw_file" || return 1
[[ "$raw_record" == "$valid" ]] || return 1
_k3slr_parse_proc_stat_record identity state 123 "$raw_record" || return 1
[[ "$identity" == '123|18446744073709551615|0|456' && "$state" == T ]] || return 1
_k3slr_parse_proc_stat_record identity state 123 \
'123 (lower stopped) t 7 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 999999999999999999999999' || return 1
[[ "$identity" == '123|999999999999999999999999|7|456' && "$state" == t ]] || return 1
_k3slr_parse_proc_stat_record identity state 123 \
'123 (idle) I 7 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' || return 1
_k3slr_parse_proc_stat_record identity state 123 \
'123 (paging) W 7 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' || return 1
_k3slr_parse_proc_stat_record identity state 123 \
'123 (consecutive ))) close) R 8 457 457 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 14' || return 1
[[ "$identity" == '123|14|8|457' && "$state" == R ]] || return 1
_k3slr_parse_proc_stat_record identity state 123 \
'123 (decoy ) R 91 777 777 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 15 still comm) S 7 458 458 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 16' || return 1
[[ "$identity" == '123|16|7|458' && "$state" == S ]] || return 1
_k3slr_parse_proc_stat_record identity state 123 \
'123 (comm ending in )) T 6 459 459 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 17' || return 1
[[ "$identity" == '123|17|6|459' && "$state" == T ]] || return 1
_k3slr_process_identity live_identity "$BASHPID" || return 1
[[ "$live_identity" == "${BASHPID}|"* ]] || return 1
local raw_line=sentinel resolved_record=sentinel queried_identity=sentinel
local raw_proc_record=sentinel parsed_state=sentinel collision_identity=sentinel
printf '%s\n' "$valid" >"$raw_file"
_k3slr_read_exact_record raw_line "$raw_file" || return 1
[[ "$raw_line" == "$valid" ]] || return 1
_k3slr_proc_stat_record resolved_record "$BASHPID" || return 1
[[ "$resolved_record" == "${BASHPID} "* ]] || return 1
_k3slr_process_identity queried_identity "$BASHPID" || return 1
[[ "$queried_identity" == "${BASHPID}|"* ]] || return 1
_k3slr_process_record raw_proc_record parsed_state "$BASHPID" || return 1
[[ "$raw_proc_record" == "${BASHPID}|"* && "$parsed_state" != sentinel ]] || return 1
local identity_destination=sentinel process_state=sentinel collision_state=sentinel collision_identity=sentinel
_k3slr_parse_proc_stat_record identity_destination collision_state 123 "$valid" || return 1
[[ "$identity_destination" == '123|18446744073709551615|0|456' && "$collision_state" == T ]] || return 1
_k3slr_parse_proc_stat_record collision_identity process_state 123 "$valid" || return 1
[[ "$collision_identity" == '123|18446744073709551615|0|456' && "$process_state" == T ]] || return 1
local destination_name=sentinel resolved_group=sentinel child_pid=sentinel
local identity_destination=sentinel state_destination=sentinel group_destination=sentinel
_k3slr_capture_one_line destination_name /usr/bin/bash -c 'printf "capture-matrix\n"' || return 1
[[ "$destination_name" == capture-matrix ]] || return 1
_k3slr_process_group_for_pid resolved_group "$BASHPID" || return 1
[[ "$resolved_group" =~ ^[1-9][0-9]*$ ]] || return 1
_k3slr_proc_stat_record child_pid "$BASHPID" || return 1
[[ "$child_pid" == "${BASHPID} "* ]] || return 1
_k3slr_process_identity destination_name "$BASHPID" || return 1
[[ "$destination_name" == "${BASHPID}|"* ]] || return 1
_k3slr_process_record identity_destination state_destination "$BASHPID" || return 1
[[ "$identity_destination" == "${BASHPID}|"* && "$state_destination" != sentinel ]] || return 1
! _k3slr_parse_proc_stat_record identity identity 123 "$valid" || return 1
! _k3slr_process_record identity identity "$BASHPID" || return 1
! _k3slr_wait_for_stopped_group identity identity "$BASHPID" || return 1
! _k3slr_parse_proc_stat_record _k3slr_parse_state state 123 "$valid" || return 1
printf '%s' "$valid" >"$raw_file"
! _k3slr_read_exact_record raw_record "$raw_file" || return 1
printf '%s\n\n' "$valid" >"$raw_file"
! _k3slr_read_exact_record raw_record "$raw_file" || return 1
printf '%s\r\n' "$valid" >"$raw_file"
! _k3slr_read_exact_record raw_record "$raw_file" || return 1
local malformed
for malformed in \
$'123 (cr\rcomm) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
$'123 (embedded\nrecord) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
$'123\t(tab prefix) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
$'123 (tab delimiter)\tT 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (double-space prefix) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123(no-space prefix) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (double-space delimiter) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (no-space delimiter)T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (decoy ) R 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13 real end) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 14' \
'123 (tail close) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 ) 13' \
'124 (wrong pid) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'0123 (leading pid) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (leading ppid) T 00 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (leading pgid) T 0 0456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (leading start) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 00' \
'123 (zero start) T 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 0' \
'123 (bad state) ? 0 456 456 0 -1 1 1 2 3 4 5 6 7 8 9 10 11 12 13' \
'123 (short) T 0 456'; do
identity=identity-sentinel
state=state-sentinel
! _k3slr_parse_proc_stat_record identity state 123 "$malformed" || return 1
[[ "$identity" == identity-sentinel && "$state" == state-sentinel ]] || return 1
done
)
task4_review5_cleanup_exact_recorded_roles() {
local log_file="$1" role pid start_time ppid pgid expected_identity current_identity caller_group
_k3slr_process_group_for_pid caller_group "$BASHPID" || return 1
while IFS='|' read -r role pid start_time ppid pgid; do
[[ "$pid" =~ ^[1-9][0-9]*$ && "$pgid" =~ ^[1-9][0-9]*$ ]] || continue
expected_identity="${pid}|${start_time}|${ppid}|${pgid}"
if _k3slr_process_identity current_identity "$pid" 2>/dev/null &&
[[ "$current_identity" == "$expected_identity" && "$pgid" != "$caller_group" ]]; then
builtin kill -KILL "$pid" 2>/dev/null || return 1
fi
done <"$log_file"
}
_task4_review5_phase_fixture_cleanup() {
local prefix="$1" supervisor_pid='' supervisor_pgid='' supervisor_identity=''
local current_identity='' observed_group='' caller_group='' identity_pid='' start_time=''
local parent_pid='' identity_group='' role pid ppid pgid expected_identity signal_name wait_rc
local cleanup_rc=0
if [[ -s "${prefix}.supervisor-pid" && -s "${prefix}.supervisor-pgid" &&
-s "${prefix}.supervisor-identity" ]]; then
supervisor_pid="$(<"${prefix}.supervisor-pid")"
supervisor_pgid="$(<"${prefix}.supervisor-pgid")"
supervisor_identity="$(<"${prefix}.supervisor-identity")"
IFS='|' read -r identity_pid start_time parent_pid identity_group <<<"$supervisor_identity"
if _k3slr_process_identity current_identity "$supervisor_pid" 2>/dev/null &&
[[ "$current_identity" == "$supervisor_identity" && "$identity_pid" == "$supervisor_pid" &&
"$identity_group" == "$supervisor_pgid" ]]; then
_k3slr_process_group_for_pid observed_group "$supervisor_pid" 2>/dev/null || observed_group=''
_k3slr_process_group_for_pid caller_group "$BASHPID" 2>/dev/null || caller_group=''
if [[ "$observed_group" == "$supervisor_pgid" && "$observed_group" != "$caller_group" ]]; then
_k3slr_terminate_and_reap "$supervisor_pid" "$supervisor_pgid" \
"$supervisor_identity" 2>/dev/null || :
fi
fi
if _k3slr_process_identity current_identity "$supervisor_pid" 2>/dev/null &&
[[ "$current_identity" == "$supervisor_identity" ]]; then
_k3slr_process_group_for_pid observed_group "$supervisor_pid" 2>/dev/null || observed_group=''
_k3slr_process_group_for_pid caller_group "$BASHPID" 2>/dev/null || caller_group=''
if [[ "$observed_group" == "$supervisor_pgid" && "$observed_group" != "$caller_group" ]]; then
_k3slr_signal_pid KILL "$supervisor_pid" "$supervisor_identity" "$parent_pid" \
"$supervisor_pgid" 2>/dev/null || :
if _k3slr_wait_child "$supervisor_pid"; then wait_rc=0; else wait_rc=$?; fi
(( wait_rc != 127 )) || cleanup_rc=1
fi
fi
fi
if [[ -f "${prefix}.roles" ]]; then
for signal_name in TERM KILL; do
while read -r role pid ppid pgid start_time; do
[[ "$pid" =~ ^[1-9][0-9]*$ && "$ppid" =~ ^[0-9]+$ &&
"$pgid" =~ ^[1-9][0-9]*$ && "$start_time" =~ ^[1-9][0-9]*$ ]] || continue
expected_identity="${pid}|${start_time}|${ppid}|${pgid}"
if _k3slr_process_identity current_identity "$pid" 2>/dev/null &&
[[ "$current_identity" == "$expected_identity" ]]; then
_k3slr_process_group_for_pid observed_group "$pid" 2>/dev/null || observed_group=''
_k3slr_process_group_for_pid caller_group "$BASHPID" 2>/dev/null || caller_group=''
if [[ "$observed_group" == "$pgid" && "$observed_group" != "$caller_group" ]]; then
_k3slr_signal_pid "$signal_name" "$pid" "$expected_identity" "$ppid" "$pgid" \
2>/dev/null || :
fi
fi
done <"${prefix}.roles"
[[ "$signal_name" == TERM ]] && /usr/bin/sleep 0.1
done
while read -r role pid ppid pgid start_time; do
[[ "$pid" =~ ^[1-9][0-9]*$ ]] || continue
expected_identity="${pid}|${start_time}|${ppid}|${pgid}"
if _k3slr_process_identity current_identity "$pid" 2>/dev/null &&
[[ "$current_identity" == "$expected_identity" ]]; then
cleanup_rc=1
fi
done <"${prefix}.roles"
fi
if [[ -n "$supervisor_pid" ]] &&
_k3slr_process_identity current_identity "$supervisor_pid" 2>/dev/null &&
[[ "$current_identity" == "$supervisor_identity" ]]; then
cleanup_rc=1
fi
return "$cleanup_rc"
}
task4_review5_phase_fixture_failure_cleanup_contract() (
local prefix="${fixture_root}/task4-review5-phase-fixture-cleanup"
local supervisor_pid supervisor_identity='' supervisor_pgid='' current_identity=''
/usr/bin/setsid /usr/bin/bash -c 'trap "" TERM; while :; do /usr/bin/sleep 1; done' &
supervisor_pid=$!
/usr/bin/sleep 0.02
_k3slr_process_identity supervisor_identity "$supervisor_pid" || return 1
_k3slr_process_group_for_pid supervisor_pgid "$supervisor_pid" || return 1
printf '%s\n' "$supervisor_pid" >"${prefix}.supervisor-pid"
printf '%s\n' "$supervisor_pgid" >"${prefix}.supervisor-pgid"
printf '%s\n' "$supervisor_identity" >"${prefix}.supervisor-identity"
: >"${prefix}.roles"
_task4_review5_phase_fixture_cleanup "$prefix" || {
if _k3slr_process_identity current_identity "$supervisor_pid" 2>/dev/null &&
[[ "$current_identity" == "$supervisor_identity" ]]; then
_k3slr_signal_pid KILL "$supervisor_pid" "$supervisor_identity" "$BASHPID" \
"$supervisor_pgid" 2>/dev/null || return 1
_k3slr_wait_child "$supervisor_pid" || :
fi
return 1
}
! kill -0 "$supervisor_pid" 2>/dev/null && ! _k3slr_group_exists "$supervisor_pgid"
)
task4_review5_default_supervisor_anchor() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
local prefix="${fixture_root}/task4-review5-anchor" killer_pid rc role pid ppid pgid start_time
local leaked=0 cleanup_rc=0 supervisor_pgid=''
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1 TASK4_REVIEW3_SIGNAL_CASE=1
export TASK4_REVIEW1_PRODUCER_CASE=term_ignoring_descendant
export TASK4_REVIEW1_CONSUMER_LOG="${prefix}.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${prefix}.producer"
export TASK4_REVIEW1_COMMAND_LOG="${prefix}.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${prefix}.db-stat"
export TASK4_REVIEW3_CALLER_PID_FILE="${prefix}.caller-pid"
export TASK4_REVIEW3_CALLER_PGID_FILE="${prefix}.caller-pgid"
export TASK4_REVIEW4_IDENTITY_ROLE_LOG="${prefix}.roles"
export TASK4_REVIEW5_TERM_IGNORE_READY="${prefix}.ready"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
_k3slr_command() { task4_review1_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
local phase="$1" boundary_pid="${2-}" identity=''
if [[ "$phase" == pid-published ]]; then
_k3slr_process_identity identity "$boundary_pid" || return 1
printf 'supervisor|%s\n' "$identity" >>"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
supervisor_pgid="$(/usr/bin/ps -o pgid= -p "$boundary_pid")" || return 1
supervisor_pgid="${supervisor_pgid//[[:space:]]/}"
printf '%s\n' "$supervisor_pgid" >"${prefix}.pgid"
fi
}
(
for ((probe=0; probe<300; probe++)); do
if [[ -s "$TASK4_REVIEW3_CALLER_PID_FILE" && -s "$TASK4_REVIEW5_TERM_IGNORE_READY" ]]; then break; fi
/usr/bin/sleep 0.01
done
[[ -s "$TASK4_REVIEW3_CALLER_PID_FILE" && -s "$TASK4_REVIEW5_TERM_IGNORE_READY" ]] || exit 1
builtin kill -TERM "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
) &
killer_pid=$!
set +e
_k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
rc=$?
wait "$killer_pid"
set -e
supervisor_pgid="$(<"${prefix}.pgid")"
while IFS='|' read -r role pid start_time ppid pgid; do
if kill -0 "$pid" 2>/dev/null; then leaked=$((leaked + 1)); fi
done <"$TASK4_REVIEW4_IDENTITY_ROLE_LOG"
if (( leaked > 0 )); then
task4_review5_cleanup_exact_recorded_roles "$TASK4_REVIEW4_IDENTITY_ROLE_LOG" || cleanup_rc=$?
fi
[[ "$cleanup_rc" -eq 0 ]] || return 1
if [[ "$rc" -ne 143 || "$leaked" -ne 0 || -s "$TASK4_REVIEW1_CONSUMER_LOG" ]] ||
_k3slr_group_exists "$supervisor_pgid"; then
printf 'task4 review5 anchor RED: rc=%s leaked=%s pgid_live=%s\n' \
"$rc" "$leaked" "$(_k3slr_group_exists "$supervisor_pgid" && printf yes || printf no)" >&2
return 1
fi
)
task4_review5_feasibility_default_supervisor_anchor() (
local prefix="${fixture_root}/task4-review5-feas-anchor" requested_dir
local killer_pid rc supervisor_pgid='' role pid start_time ppid pgid leaked=0 cleanup_rc=0
source "$FEASIBILITY_PATH"
requested_dir="/tmp/k3slr-feasibility.anchor${BASHPID}${RANDOM}"
export TASK4_FEASIBILITY_CASE=anchor_TERM
export TASK4_FEASIBILITY_REQUESTED_DIR="$requested_dir"
export TASK4_FEASIBILITY_COMMAND_LOG="${prefix}.commands"
export TASK4_FEASIBILITY_MAIN_PID_FILE="${prefix}.main"
export TASK4_FEASIBILITY_CHILD_PID_FILE="${prefix}.child"
export TASK4_FEASIBILITY_ROLE_LOG="${prefix}.roles"
export TASK4_FEASIBILITY_TERM_IGNORE_READY="${prefix}.ready"
: >"$TASK4_FEASIBILITY_COMMAND_LOG"
: >"$TASK4_FEASIBILITY_ROLE_LOG"
_k3slr_command() { task4_feasibility_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
local phase="$1" tracked_pid="${2-}" tracked_identity=''
if [[ "$phase" == feasibility-pid-published ]]; then
_k3slr_process_identity tracked_identity "$tracked_pid" || return 1
printf 'supervisor|%s\n' "$tracked_identity" >>"$TASK4_FEASIBILITY_ROLE_LOG"
supervisor_pgid="$(/usr/bin/ps -o pgid= -p "$tracked_pid")" || return 1
supervisor_pgid="${supervisor_pgid//[[:space:]]/}"
printf '%s\n' "$supervisor_pgid" >"${prefix}.pgid"
fi
}
(
local probe
for ((probe=0; probe<300; probe++)); do
if [[ -s "$TASK4_FEASIBILITY_MAIN_PID_FILE" &&
-s "$TASK4_FEASIBILITY_TERM_IGNORE_READY" ]]; then break; fi
/usr/bin/sleep 0.01
done
[[ -s "$TASK4_FEASIBILITY_MAIN_PID_FILE" &&
-s "$TASK4_FEASIBILITY_TERM_IGNORE_READY" ]] || exit 1
builtin kill -TERM "$(<"$TASK4_FEASIBILITY_MAIN_PID_FILE")"
) &
killer_pid=$!
set +e
k3slr_local_recovery_feasibility_main --execute </dev/ptmx >/dev/null 2>"${prefix}.stderr"
rc=$?
wait "$killer_pid"
set -e
supervisor_pgid="$(<"${prefix}.pgid")"
while IFS='|' read -r role pid start_time ppid pgid; do
if kill -0 "$pid" 2>/dev/null; then leaked=$((leaked + 1)); fi
done <"$TASK4_FEASIBILITY_ROLE_LOG"
if (( leaked > 0 )); then
task4_review5_cleanup_exact_recorded_roles "$TASK4_FEASIBILITY_ROLE_LOG" || cleanup_rc=$?
fi
[[ "$cleanup_rc" -eq 0 ]] || return 1
if [[ "$rc" -ne 143 || "$leaked" -ne 0 || -e "$requested_dir" ]] ||
_k3slr_group_exists "$supervisor_pgid"; then
printf 'task4 review5 feasibility anchor RED: rc=%s leaked=%s fixture=%s pgid_live=%s\n' \
"$rc" "$leaked" "$([[ -e "$requested_dir" ]] && printf yes || printf no)" \
"$(_k3slr_group_exists "$supervisor_pgid" && printf yes || printf no)" >&2
return 1
fi
)
task4_review5_published_unpinned_cleanup() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
local mode prefix rc supervisor_pid supervisor_pgid recorded_identity current_identity caller_pgid
local definition leaked cleanup_rc raw='' state=''
definition="$(declare -f _k3slr_proc_stat_record)"
eval "${definition/_k3slr_proc_stat_record/_task4_review5_original_proc_stat_record}"
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1 TASK4_REVIEW3_SIGNAL_CASE=1
export TASK4_REVIEW1_PRODUCER_CASE=blocking_signal
_k3slr_command() { task4_review1_command_fake "$@"; }
for mode in missing short malformed persistent; do
cleanup_rc=0
prefix="${fixture_root}/task4-review5-unpinned-${mode}"
export TASK4_REVIEW1_CONSUMER_LOG="${prefix}.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${prefix}.producer"
export TASK4_REVIEW1_COMMAND_LOG="${prefix}.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${prefix}.db-stat"
export TASK4_REVIEW3_CALLER_PID_FILE="${prefix}.caller-pid"
export TASK4_REVIEW3_CALLER_PGID_FILE="${prefix}.caller-pgid"
export TASK4_REVIEW3_ROLE_LOG="${prefix}.roles"
export TASK4_REVIEW5_UNPINNED_MODE="$mode"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_REVIEW3_ROLE_LOG"
: >"${prefix}.pid-signals"
: >"${prefix}.group-signals"
: >"${prefix}.waits"
TASK4_REVIEW5_UNPINNED_PID=''
_k3slr_lifecycle_boundary() {
local phase="$1" boundary_pid="${2-}" raw='' pinned='' state=''
if [[ "$phase" == pid-published ]]; then
_task4_review5_original_proc_stat_record raw "$boundary_pid" || return 1
_k3slr_parse_proc_stat_record pinned state "$boundary_pid" "$raw" || return 1
printf '%s\n' "$boundary_pid" >"${prefix}.pid"
printf '%s\n' "$pinned" >"${prefix}.identity"
/usr/bin/ps -o pgid= -p "$boundary_pid" | /usr/bin/tr -d '[:space:]' >"${prefix}.pgid"
TASK4_REVIEW5_UNPINNED_PID="$boundary_pid"
fi
}
_k3slr_proc_stat_record() {
local destination_name="$1" queried_pid="$2"
if [[ -n "$TASK4_REVIEW5_UNPINNED_PID" && "$queried_pid" == "$TASK4_REVIEW5_UNPINNED_PID" ]]; then
case "$TASK4_REVIEW5_UNPINNED_MODE" in
missing|persistent) return 1 ;;
short) printf -v "$destination_name" '%s' "${queried_pid} (short) T 1" ;;
malformed) printf -v "$destination_name" '%s' "${queried_pid} (bad) ? 01 02" ;;
esac
return 0
fi
_task4_review5_original_proc_stat_record "$destination_name" "$queried_pid"
}
_k3slr_signal_published_pid() {
printf '%s %s\n' "$1" "$2" >>"${prefix}.pid-signals"
builtin kill -KILL "$2"
}
_k3slr_signal_group() {
printf '%s %s\n' "$1" "$3" >>"${prefix}.group-signals"
return 99
}
_k3slr_wait_child() {
printf '%s\n' "$1" >>"${prefix}.waits"
wait "$1" >/dev/null 2>&1
}
set +e
_k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
rc=$?
set -e
supervisor_pid="$(<"${prefix}.pid")"
supervisor_pgid="$(<"${prefix}.pgid")"
recorded_identity="$(<"${prefix}.identity")"
leaked=0
if kill -0 "$supervisor_pid" 2>/dev/null || _k3slr_group_exists "$supervisor_pgid"; then leaked=1; fi
if (( leaked )); then
caller_pgid="$(/usr/bin/ps -o pgid= -p "$BASHPID")"
caller_pgid="${caller_pgid//[[:space:]]/}"
if _task4_review5_original_proc_stat_record raw "$supervisor_pid" 2>/dev/null &&
_k3slr_parse_proc_stat_record current_identity state "$supervisor_pid" "$raw" &&
[[ "$current_identity" == "$recorded_identity" && "$supervisor_pgid" != "$caller_pgid" ]]; then
builtin kill -KILL "$supervisor_pid" 2>/dev/null || cleanup_rc=$?
else
cleanup_rc=1
fi
fi
[[ "${cleanup_rc:-0}" -eq 0 ]] || return 1
if [[ "$rc" -ne 1 || "$leaked" -ne 0 || -s "$TASK4_REVIEW1_CONSUMER_LOG" ||
-s "$TASK4_REVIEW3_ROLE_LOG" || -s "${prefix}.group-signals" ||
"$(/usr/bin/wc -l <"${prefix}.pid-signals")" -ne 1 ||
"$(/usr/bin/wc -l <"${prefix}.waits")" -ne 1 ]]; then
printf 'task4 review5 unpinned diagnostic: mode=%s rc=%s leaked=%s pid_signals=%s group_signals=%s waits=%s\n' \
"$mode" "$rc" "$leaked" "$(/usr/bin/wc -l <"${prefix}.pid-signals")" \
"$(/usr/bin/wc -l <"${prefix}.group-signals")" "$(/usr/bin/wc -l <"${prefix}.waits")" >&2
return 1
fi
done
)
task4_review5_pending_priority() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}"
local prefix="${fixture_root}/task4-review5-priority-password" rc requested_dir
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1 TASK4_REVIEW3_SIGNAL_CASE=1
export TASK4_REVIEW1_PRODUCER_CASE=blocking_signal
export TASK4_REVIEW1_CONSUMER_LOG="${prefix}.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${prefix}.producer"
export TASK4_REVIEW1_COMMAND_LOG="${prefix}.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${prefix}.db-stat"
export TASK4_REVIEW3_CALLER_PID_FILE="${prefix}.caller-pid"
export TASK4_REVIEW3_CALLER_PGID_FILE="${prefix}.caller-pgid"
export TASK4_REVIEW3_ROLE_LOG="${prefix}.roles"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
: >"$TASK4_REVIEW3_ROLE_LOG"
_k3slr_command() { task4_review1_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
if [[ "$1" == coproc-launch-before ]]; then
builtin kill -TERM "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
builtin kill -INT "$(<"$TASK4_REVIEW3_CALLER_PID_FILE")"
fi
}
set +e
_k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
rc=$?
set -e
if [[ "$rc" -ne 143 || -s "$TASK4_REVIEW1_CONSUMER_LOG" || -s "$TASK4_REVIEW3_ROLE_LOG" ]]; then
printf 'task4 review5 password priority diagnostic: rc=%s\n' "$rc" >&2
return 1
fi
source "$FEASIBILITY_PATH"
requested_dir="/tmp/k3slr-feasibility.priority${BASHPID}${RANDOM}"
export TASK4_FEASIBILITY_CASE=signal_TERM
export TASK4_FEASIBILITY_REQUESTED_DIR="$requested_dir"
export TASK4_FEASIBILITY_COMMAND_LOG="${prefix}.feasibility-commands"
export TASK4_FEASIBILITY_MAIN_PID_FILE="${prefix}.feasibility-main"
: >"$TASK4_FEASIBILITY_COMMAND_LOG"
_k3slr_command() { task4_feasibility_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
if [[ "$1" == feasibility-launch-before ]]; then
builtin kill -TERM "$(<"$TASK4_FEASIBILITY_MAIN_PID_FILE")"
builtin kill -INT "$(<"$TASK4_FEASIBILITY_MAIN_PID_FILE")"
fi
}
set +e
k3slr_local_recovery_feasibility_main --execute </dev/ptmx >/dev/null 2>"${prefix}.feasibility-stderr"
rc=$?
set -e
if [[ "$rc" -ne 143 || -e "$requested_dir" ]]; then
printf 'task4 review5 feasibility priority RED: rc=%s fixture=%s\n' "$rc" \
"$([[ -e "$requested_dir" ]] && printf yes || printf no)" >&2
return 1
fi
)
task4_review5_feasibility_cleanup_failure_is_not_signal_success() (
local prefix="${fixture_root}/task4-review5-feas-cleanup-failure" requested_dir
local rc child_pid definition raw='' fail_pid=''
source "$FEASIBILITY_PATH"
requested_dir="/tmp/k3slr-feasibility.pendingfail${BASHPID}${RANDOM}"
export TASK4_FEASIBILITY_CASE=signal_TERM
export TASK4_FEASIBILITY_REQUESTED_DIR="$requested_dir"
export TASK4_FEASIBILITY_COMMAND_LOG="${prefix}.commands"
export TASK4_FEASIBILITY_MAIN_PID_FILE="${prefix}.main"
: >"$TASK4_FEASIBILITY_COMMAND_LOG"
: >"${prefix}.signal-count"
definition="$(declare -f _k3slr_proc_stat_record)"
eval "${definition/_k3slr_proc_stat_record/_task4_review5_feas_original_proc_stat_record}"
_k3slr_command() { task4_feasibility_command_fake "$@"; }
_k3slr_lifecycle_boundary() {
if [[ "$1" == feasibility-pid-published ]]; then
fail_pid="$2"
printf '%s\n' "$2" >"${prefix}.pid"
builtin kill -TERM "$(<"$TASK4_FEASIBILITY_MAIN_PID_FILE")"
fi
}
_k3slr_proc_stat_record() {
if [[ -n "$fail_pid" && "$2" == "$fail_pid" ]]; then return 1; fi
_task4_review5_feas_original_proc_stat_record "$@"
}
_k3slr_signal_published_pid() {
printf 'call\n' >>"${prefix}.signal-count"
if [[ "$(/usr/bin/wc -l <"${prefix}.signal-count")" -eq 1 ]]; then return 1; fi
builtin kill -KILL "$2"
}
set +e
k3slr_local_recovery_feasibility_main --execute </dev/ptmx >/dev/null 2>"${prefix}.stderr"
rc=$?
set -e
child_pid="$(<"${prefix}.pid")"
if [[ "$rc" -ne 1 || "$(/usr/bin/wc -l <"${prefix}.signal-count")" -ne 2 ||
-e "$requested_dir" ]] || kill -0 "$child_pid" 2>/dev/null; then
printf 'task4 review5 feasibility cleanup diagnostic: rc=%s calls=%s child_live=%s fixture=%s\n' \
"$rc" "$(/usr/bin/wc -l <"${prefix}.signal-count")" \
"$([[ -e /proc/$child_pid ]] && printf yes || printf no)" \
"$([[ -e "$requested_dir" ]] && printf yes || printf no)" >&2
return 1
fi
)
task4_review2_tty_and_bounded_validated_pipe() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}" producer_case
export TASK4_REVIEW1_CONSUMER_LOG="${fixture_root}/task4-review2-consumer.log"
export TASK4_REVIEW1_PRODUCER_LOG="${fixture_root}/task4-review2-producer.log"
export TASK4_REVIEW1_COMMAND_LOG="${fixture_root}/task4-review2-command.log"
export TASK4_REVIEW1_DB_STAT_LOG="${fixture_root}/task4-review2-db-stat.log"
export TASK4_REVIEW2_REQUIRE_REAL_TTY=1
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
_k3slr_command() { task4_review1_command_fake "$@"; }
TASK4_REVIEW1_PRODUCER_CASE=success
export TASK4_REVIEW1_PRODUCER_CASE
if ! _k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx; then
printf 'task4 review2 critical diagnostic\n' >&2
/usr/bin/tail -n 30 "$TASK4_REVIEW1_COMMAND_LOG" >&2
return 1
fi
[[ "$(/usr/bin/wc -l <"$TASK4_REVIEW1_CONSUMER_LOG")" -eq 1 ]] || return 1
/usr/bin/grep -q 'result=tty' "$TASK4_REVIEW1_COMMAND_LOG" || return 1
: >"$TASK4_REVIEW1_CONSUMER_LOG"
for producer_case in zero_success missing_lf double_lf invalid_class malformed_length oversized_success; do
TASK4_REVIEW1_PRODUCER_CASE="$producer_case"
export TASK4_REVIEW1_PRODUCER_CASE
assert_fails _k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME" </dev/ptmx
[[ ! -s "$TASK4_REVIEW1_CONSUMER_LOG" ]] || return 1
done
)
task4_review2_capture_one_line_exact_record() (
local captured=''
assert_succeeds _k3slr_capture_one_line captured /usr/bin/bash -c 'printf "abc\n"'
[[ "$captured" == abc ]] || return 1
assert_fails _k3slr_capture_one_line captured /usr/bin/bash -c 'printf abc'
assert_fails _k3slr_capture_one_line captured /usr/bin/bash -c 'printf "abc\n\n"'
assert_fails _k3slr_capture_one_line captured /usr/bin/bash -c 'printf "abc\r\n"'
assert_fails _k3slr_capture_one_line captured /usr/bin/bash -c 'printf "abc\ndef\n"'
assert_fails _k3slr_capture_one_line captured /usr/bin/bash -c 'printf "abc\n"; exit 7'
)
# The production breaks this catches are omission of --show-protected, adding
# --quiet back to the protected producer, or routing recovery through a
# read-write cryptsetup consumer. The only external boundary is closed below.
task4_review2_show_protected_and_readonly() (
local command_log="${fixture_root}/task1-show-protected.argv"
local stdout_file="${fixture_root}/task1-show-protected.stdout"
local stderr_file="${fixture_root}/task1-show-protected.stderr"
local secret_sentinel='Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N'
local rc=0
: >"$command_log"
: >"$stdout_file"
: >"$stderr_file"
# shellcheck source=/dev/null
source "$LIBRARY_PATH"
_k3slr_command() {
printf '%s\0' "$@" >>"$command_log"
case "$#:$1:${2-}:${3-}:${4-}:${5-}" in
'3:/usr/bin/test:-t:0::') return 0 ;;
'7:/usr/bin/keepassxc-cli:show:--show-protected:--attributes:Password')
[[ "${6-}" == /fixture/recovery.kdbx && "${7-}" == 'K3s Recovery LUKS' ]] || return 97
printf '%s\n' "$secret_sentinel"
;;
'11:/usr/bin/sudo:--non-interactive:--:/usr/sbin/cryptsetup:open')
[[ "${6-}" == --readonly && "${7-}" == --type && "${8-}" == luks2 &&
"${9-}" == --key-file=- && "${10-}" == /dev/loop23 &&
"${11-}" == k3s-recovery-proof ]] || return 97
IFS= read -r _ || return 1
;;
*) return 97 ;;
esac
}
_k3slr_keepass_password_stdout /fixture/recovery.kdbx 'K3s Recovery LUKS' \
>/dev/null 2>"$stderr_file" || rc=$?
if (( rc != 0 )); then
mapfile -d '' -t task1_red_argv <"$command_log"
[[ " ${task1_red_argv[*]} " == *' /usr/bin/keepassxc-cli show --quiet '* &&
" ${task1_red_argv[*]} " != *' --show-protected '* ]] ||
fail 'producer RED did not reach the legacy KeePass argv missing --show-protected'
fi
assert_eq 0 "$rc" 'protected KeePass producer uses the protected Password argv'
rc=0
_k3slr_cryptsetup_open_readonly_stdin /dev/loop23 k3s-recovery-proof \
< <(printf '%s\n' "$secret_sentinel") >"$stdout_file" 2>>"$stderr_file" || rc=$?
assert_eq 0 "$rc" 'read-only LUKS consumer succeeds'
export TASK4_REVIEW1_CONSUMER_LOG="${fixture_root}/task1-readonly-pipe.consumer"
export TASK4_REVIEW1_PRODUCER_LOG="${fixture_root}/task1-readonly-pipe.producer"
export TASK4_REVIEW1_COMMAND_LOG="${fixture_root}/task1-readonly-pipe.commands"
export TASK4_REVIEW1_DB_STAT_LOG="${fixture_root}/task1-readonly-pipe.db-stat"
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
TASK4_REVIEW1_PRODUCER_CASE=success
TASK4_REVIEW1_VALIDATION_CASE=''
export TASK4_REVIEW1_PRODUCER_CASE TASK4_REVIEW1_VALIDATION_CASE
_k3slr_command() { task4_review1_command_fake "$@"; }
assert_fails _k3slr_luks_open_readonly_from_keepass /fixture/recovery.kdbx \
'K3s Recovery LUKS' /dev/loop23
assert_fails _k3slr_luks_open_readonly_from_keepass /fixture/recovery.kdbx \
'K3s Recovery LUKS' /dev/loop23 k3s-recovery-proof extra
rc=0
_k3slr_luks_open_readonly_from_keepass \
"${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}" 'K3s Recovery LUKS' \
/dev/loop7 "$K3SLR_PROOF_MAPPING_NAME" </dev/ptmx || rc=$?
assert_eq 0 "$rc" 'four-argument read-only wrapper routes through open-readonly password pipe'
assert_eq 1 "$(/usr/bin/wc -l <"$TASK4_REVIEW1_CONSUMER_LOG")" \
'actual open-readonly password-pipe branch dispatches one consumer'
/usr/bin/grep -Fqx -- \
"/usr/bin/sudo --non-interactive -- /usr/sbin/cryptsetup open --readonly --type luks2 --key-file=- /dev/loop7 ${K3SLR_PROOF_MAPPING_NAME}" \
"$TASK4_REVIEW1_COMMAND_LOG" ||
fail 'actual open-readonly password-pipe branch dispatched a read-write or malformed consumer'
[[ ! -s "$stdout_file" && ! -s "$stderr_file" ]] ||
fail 'protected producer leaked a secret through public output'
! /usr/bin/grep -Fq -- "$secret_sentinel" "$command_log" ||
fail 'protected producer leaked a secret into the command log'
mapfile -d '' -t task1_argv <"$command_log"
assert_eq '/usr/bin/test' "${task1_argv[0]-}" 'producer checks the interactive stdin seam'
assert_eq '/usr/bin/keepassxc-cli' "${task1_argv[3]-}" 'producer uses KeePassXC'
assert_eq '--show-protected' "${task1_argv[5]-}" 'producer requests the protected Password field'
assert_eq '/usr/bin/sudo' "${task1_argv[10]-}" 'consumer uses the modeled sudo boundary'
assert_eq '--readonly' "${task1_argv[15]-}" 'consumer opens the LUKS mapping read-only'
)
# This uses the feasibility main's real synthetic flow, but replaces only its
# command seam and records the KDBX attachment state in a file so the model
# survives each tracked background subshell.
task4_review2_feasibility_show_protected() (
local case_root="${fixture_root}/task1-feas-show"
local command_log="${case_root}/commands.nul"
local trace_file="${case_root}/trace"
local stdout_file="${case_root}/stdout"
local stderr_file="${case_root}/stderr"
local secret_sentinel='Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N'
local rc=0
mkdir -p -- "$case_root"
: >"$command_log"
: >"$trace_file"
# shellcheck source=/dev/null
source "$FEASIBILITY_PATH"
_k3slrf_require_interactive_stdin() { return 0; }
_k3slrf_verify_prerequisites() { return 0; }
_k3slrf_require_cached_sudo() { return 0; }
_k3slrf_create_fixture_dir() {
printf -v "$1" '%s' "$case_root/database"
printf -v "$2" '%s' fixture-directory
printf -v "$3" '%s' true
mkdir -p -- "$case_root/database"
}
_k3slrf_cleanup_fixture_dir() { [[ "$1" == "$case_root/database" ]]; }
_k3slrf_directory_identity() { printf -v "$1" '%s' fixture-directory; }
_k3slrf_directory_identity_matches() { [[ "$2" == fixture-directory ]]; }
_k3slrf_file_identity() { [[ -f "$2" ]] && printf -v "$1" '%s' "file:$2"; }
_k3slrf_file_identity_matches() { [[ -f "$1" && "$2" == "file:$1" ]]; }
_k3slrf_run_tracked() {
( "$@" ) &
local child=$!
wait "$child"
}
_k3slrf_validate_generated_password_stdin() {
local received=''
IFS= read -r received
[[ "$received" == "$secret_sentinel" ]]
}
_k3slr_command() {
local database='' payload=''
printf '%s\0' "$@" >>"$command_log"
case "$1:${2-}" in
'/usr/bin/sha256sum:')
(( $# == 1 )) || return 97
printf 'master-hash\n' >>"$trace_file"
while IFS= read -r _; do :; done
printf '%064d -\n' 0
;;
'/usr/bin/mawk:'*)
(( $# == 2 )) && [[ "${2-}" == '{ print $1 }' ]] || return 97
printf 'master-field\n' >>"$trace_file"
while IFS= read -r _; do :; done
printf '%s\n' fixture-master
;;
'/usr/bin/stat:--format=%s')
(( $# == 4 )) && [[ "${3-}" == -- &&
"${4-}" == "${case_root}/database/keepass-listing."*.stdout ]] || return 97
/usr/bin/stat --format='%s' -- "${4}"
;;
'/usr/bin/rm:--')
(( $# == 4 )) && [[ "${3-}" == "${case_root}/database/keepass-listing."*.stdout &&
"${4-}" == "${case_root}/database/keepass-listing."*.stderr ]] || return 97
/usr/bin/rm -- "${3}" "${4}"
;;
'/usr/bin/keepassxc-cli:db-create')
(( $# == 5 )) && [[ "${3-}" == --quiet && "${4-}" == --set-password && "${5-}" == */synthetic.kdbx ]] || return 97
while IFS= read -r _; do :; done
: >"${5}"
printf 'db-create\n' >>"$trace_file"
;;
'/usr/bin/keepassxc-cli:add')
(( $# == 12 )) && [[ "${3-}" == --quiet && "${4-}" == --generate && "${5-}" == --length &&
"${6-}" == 40 && "${7-}" == --lower && "${8-}" == --upper &&
"${9-}" == --numeric && "${10-}" == --every-group &&
"${11-}" == */synthetic.kdbx && "${12-}" == 'K3s Recovery LUKS' ]] || return 97
while IFS= read -r _; do :; done
printf '%s\n' entry >"${11}"
printf 'entry-add\n' >>"$trace_file"
;;
'/usr/bin/keepassxc-cli:show')
if (( $# == 7 )); then
[[ "${4-}" == --attributes && "${5-}" == Password &&
"${6-}" == */synthetic.kdbx && "${7-}" == 'K3s Recovery LUKS' ]] || return 97
if [[ "${3-}" == --show-protected ]]; then
printf 'password-show-protected\n' >>"$trace_file"
while IFS= read -r _; do :; done
printf '%s\n' "$secret_sentinel"
elif [[ "${3-}" == --quiet ]]; then
printf 'password-show-legacy\n' >>"$trace_file"
return 97
else
return 97
fi
elif (( $# == 8 )); then
[[ "${3-}" == --quiet && "${4-}" == --attributes && "${5-}" == Title &&
"${6-}" == --show-attachments && "${7-}" == */synthetic.kdbx &&
"${8-}" == 'K3s Recovery LUKS' ]] || return 97
while IFS= read -r _; do :; done
if [[ -f "${7}.attachment-state" ]]; then
printf 'K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n'
else
printf 'K3s Recovery LUKS\n\nNo attachments present.\n'
fi
else
return 97
fi
;;
'/usr/bin/keepassxc-cli:attachment-import')
(( $# == 7 )) && [[ "${3-}" == --quiet && "${4-}" == */synthetic.kdbx &&
"${5-}" == 'K3s Recovery LUKS' && "${6-}" == fixture.bin && "${7-}" == */input.bin ]] || return 97
while IFS= read -r _; do :; done
printf '%s\n' attached >"${4}.attachment-state"
IFS= read -r payload <"${7}" || return 1
printf '%s\n' "$payload" >"${4}.attachment-data"
;;
'/usr/bin/keepassxc-cli:attachment-export')
(( $# == 7 )) && [[ "${3-}" == --quiet && "${4-}" == */synthetic.kdbx &&
"${5-}" == 'K3s Recovery LUKS' && "${6-}" == fixture.bin && "${7-}" == /proc/*/fd/* ]] || return 97
while IFS= read -r _; do :; done
IFS= read -r payload <"${4}.attachment-data" || return 1
printf '%s\n' "$payload" >"${7}"
;;
'/usr/bin/cmp:--silent') (( $# == 5 )) && [[ "${3-}" == -- && "${4-}" == */input.bin && "${5-}" == */output.bin ]] || return 97 ;;
*) return 97 ;;
esac
}
k3slr_local_recovery_feasibility_main --execute >"$stdout_file" 2>"$stderr_file" || rc=$?
if (( rc != 0 )); then
local task1_feas_index=0 task1_feas_legacy_seen=false task1_feas_protected_seen=false
mapfile -d '' -t task1_feas_red_argv <"$command_log"
for (( task1_feas_index=0; task1_feas_index+6<${#task1_feas_red_argv[@]}; task1_feas_index++ )); do
if [[ "${task1_feas_red_argv[task1_feas_index]}" == /usr/bin/keepassxc-cli &&
"${task1_feas_red_argv[task1_feas_index + 1]}" == show &&
"${task1_feas_red_argv[task1_feas_index + 2]}" == --show-protected &&
"${task1_feas_red_argv[task1_feas_index + 3]}" == --attributes &&
"${task1_feas_red_argv[task1_feas_index + 4]}" == Password &&
"${task1_feas_red_argv[task1_feas_index + 6]}" == 'K3s Recovery LUKS' ]]; then
task1_feas_protected_seen=true
break
elif [[ "${task1_feas_red_argv[task1_feas_index]}" == /usr/bin/keepassxc-cli &&
"${task1_feas_red_argv[task1_feas_index + 1]}" == show &&
"${task1_feas_red_argv[task1_feas_index + 2]}" == --quiet &&
"${task1_feas_red_argv[task1_feas_index + 3]}" == --attributes &&
"${task1_feas_red_argv[task1_feas_index + 4]}" == Password &&
"${task1_feas_red_argv[task1_feas_index + 6]}" == 'K3s Recovery LUKS' ]]; then
task1_feas_legacy_seen=true
break
fi
done
if "$task1_feas_protected_seen"; then
fail "feasibility failed after the protected password stage (stages: $(/usr/bin/tr '\n' ',' <"$trace_file"))"
fi
"$task1_feas_legacy_seen" || fail "feasibility RED did not reach the exact legacy password show argv (stages: $(/usr/bin/tr '\n' ',' <"$trace_file"))"
/usr/bin/grep -Fqx password-show-legacy "$trace_file" ||
fail 'feasibility RED did not persist the legacy password stage'
fi
assert_eq 0 "$rc" 'feasibility password check uses the protected Password argv'
! /usr/bin/grep -Fq -- "$secret_sentinel" "$stdout_file" "$stderr_file" "$command_log" ||
fail 'feasibility fixture leaked the password sentinel'
mapfile -d '' -t task1_feas_argv <"$command_log"
[[ " ${task1_feas_argv[*]} " == *' --show-protected '* ]] ||
fail 'feasibility did not request the protected Password field'
)
# The raw-NUL record is emitted only by the exact modeled show-attachments
# command. It is never placed in a Bash variable or command substitution by
# this fixture; feasibility must capture and reject it at its real boundary.
task4_review2_feasibility_attachment_listing() (
local case_root="${fixture_root}/task1-feas-listing"
local command_log="${case_root}/commands.nul"
local trace_file="${case_root}/trace"
local stdout_file="${case_root}/stdout"
local stderr_file="${case_root}/stderr"
local listing_case=raw_nul rc=0 state=''
mkdir -p -- "$case_root"
: >"$command_log"
: >"$trace_file"
# shellcheck source=/dev/null
source "$FEASIBILITY_PATH"
_k3slr_parse_keepass_attachment_listing() {
(( $# == 3 )) || return 97
case "$1" in
$'K3s Recovery LUKS\n\nNo attachments present.\n')
printf -v "$3" '%s' absent
printf 'parser:absent\n' >>"$trace_file"
;;
$'K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n')
printf -v "$3" '%s' present
printf 'parser:present\n' >>"$trace_file"
;;
*)
printf 'parser:rejected\n' >>"$trace_file"
return 1
;;
esac
}
_k3slrf_require_interactive_stdin() { return 0; }
_k3slrf_verify_prerequisites() { return 0; }
_k3slrf_require_cached_sudo() { return 0; }
_k3slrf_create_fixture_dir() {
local new_dir="${case_root}/database-${RANDOM}"
mkdir -p -- "$new_dir"
printf -v "$1" '%s' "$new_dir"
printf -v "$2" '%s' fixture-directory
printf -v "$3" '%s' true
}
_k3slrf_cleanup_fixture_dir() { [[ "$1" == "${case_root}"/database-* ]]; }
_k3slrf_directory_identity() { printf -v "$1" '%s' fixture-directory; }
_k3slrf_directory_identity_matches() { [[ "$2" == fixture-directory ]]; }
_k3slrf_file_identity() { [[ -f "$2" ]] && printf -v "$1" '%s' "file:$2"; }
_k3slrf_file_identity_matches() { [[ -f "$1" && "$2" == "file:$1" ]]; }
_k3slrf_run_tracked() {
( "$@" ) &
local child=$!
wait "$child"
}
_k3slrf_validate_generated_password_stdin() { IFS= read -r _; }
_k3slr_command() {
local payload=''
printf '%s\0' "$@" >>"$command_log"
case "$1:${2-}" in
'/usr/bin/sha256sum:')
(( $# == 1 )) || return 97
while IFS= read -r _; do :; done
printf '%064d -\n' 0
;;
'/usr/bin/mawk:'*)
(( $# == 2 )) && [[ "${2-}" == '{ print $1 }' ]] || return 97
while IFS= read -r _; do :; done
printf '%s\n' fixture-master
;;
'/usr/bin/stat:--format=%s')
(( $# == 4 )) && [[ "${3-}" == -- &&
"${4-}" == "${case_root}/database-"*/keepass-listing.*.stdout ]] || return 97
/usr/bin/stat --format='%s' -- "${4}"
;;
'/usr/bin/rm:--')
(( $# == 4 )) && [[ "${3-}" == "${case_root}/database-"*/keepass-listing.*.stdout &&
"${4-}" == "${case_root}/database-"*/keepass-listing.*.stderr ]] || return 97
/usr/bin/rm -- "${3}" "${4}"
;;
'/usr/bin/keepassxc-cli:db-create')
(( $# == 5 )) && [[ "${3-}" == --quiet && "${4-}" == --set-password && "${5-}" == */synthetic.kdbx ]] || return 97
while IFS= read -r _; do :; done
: >"${5}"
;;
'/usr/bin/keepassxc-cli:add')
(( $# == 12 )) && [[ "${3-}" == --quiet && "${4-}" == --generate && "${5-}" == --length &&
"${6-}" == 40 && "${7-}" == --lower && "${8-}" == --upper &&
"${9-}" == --numeric && "${10-}" == --every-group && "${11-}" == */synthetic.kdbx &&
"${12-}" == 'K3s Recovery LUKS' ]] || return 97
while IFS= read -r _; do :; done
: >"${11}.entry"
;;
'/usr/bin/keepassxc-cli:show')
if (( $# == 7 )); then
[[ ( "${3-}" == --quiet || "${3-}" == --show-protected ) && "${4-}" == --attributes &&
"${5-}" == Password && "${6-}" == */synthetic.kdbx &&
"${7-}" == 'K3s Recovery LUKS' ]] || return 97
while IFS= read -r _; do :; done
printf '%s\n' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N'
elif (( $# == 8 )); then
[[ "${3-}" == --quiet && "${4-}" == --attributes && "${5-}" == Title &&
"${6-}" == --show-attachments && "${7-}" == */synthetic.kdbx &&
"${8-}" == 'K3s Recovery LUKS' ]] || return 97
while IFS= read -r _; do :; done
case "$listing_case" in
raw_nul)
printf 'listing:raw-nul\n' >>"$trace_file"
printf 'K3s Recovery LUKS\n\nAttachments:\n fixture.bin\0 (30.0 B)\n'
;;
absent)
if [[ -f "${7}.attachment-state" ]]; then
printf 'listing:present\n' >>"$trace_file"
printf 'K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n'
else
printf 'listing:absent\n' >>"$trace_file"
printf 'K3s Recovery LUKS\n\nNo attachments present.\n'
fi
;;
present)
printf 'listing:present\n' >>"$trace_file"
printf 'K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n'
;;
size_8192)
printf 'listing:size-8192\n' >>"$trace_file"
printf 'K3s Recovery LUKS\n\n%08172d\n' 0
;;
size_8193)
printf 'listing:size-8193\n' >>"$trace_file"
printf 'K3s Recovery LUKS\n\n%08173d\n' 0
;;
*) return 97 ;;
esac
else
return 97
fi
;;
'/usr/bin/keepassxc-cli:attachment-import')
(( $# == 7 )) && [[ "${3-}" == --quiet && "${4-}" == */synthetic.kdbx &&
"${5-}" == 'K3s Recovery LUKS' && "${6-}" == fixture.bin && "${7-}" == */input.bin ]] || return 97
while IFS= read -r _; do :; done
IFS= read -r payload <"${7}" || return 1
printf '%s\n' "$payload" >"${4}.attachment-data"
: >"${4}.attachment-state"
printf 'import\n' >>"$trace_file"
;;
'/usr/bin/keepassxc-cli:attachment-export')
(( $# == 7 )) && [[ "${3-}" == --quiet && "${4-}" == */synthetic.kdbx &&
"${5-}" == 'K3s Recovery LUKS' && "${6-}" == fixture.bin && "${7-}" == /proc/*/fd/* ]] || return 97
while IFS= read -r _; do :; done
IFS= read -r payload <"${4}.attachment-data" || return 1
printf '%s\n' "$payload" >"${7}"
printf 'export\n' >>"$trace_file"
;;
'/usr/bin/cmp:--silent') (( $# == 5 )) && [[ "${3-}" == -- && "${4-}" == */input.bin && "${5-}" == */output.bin ]] || return 97 ;;
*) return 97 ;;
esac
}
set +e
k3slr_local_recovery_feasibility_main --execute >"$stdout_file" 2>"$stderr_file"
rc=$?
set -e
assert_eq 1 "$(/usr/bin/grep -Fxc 'listing:raw-nul' "$trace_file" || :)" \
'raw-NUL path dispatches exactly one attachment listing'
assert_eq 0 "$(/usr/bin/grep -Fc 'parser:' "$trace_file" || :)" \
'raw-NUL path reaches no parser'
assert_eq 0 "$(/usr/bin/grep -Fxc import "$trace_file" || :)" \
'raw-NUL path reaches no import'
assert_eq 0 "$(/usr/bin/grep -Fxc export "$trace_file" || :)" \
'raw-NUL path reaches no export'
! /usr/bin/grep -Fiq -- 'null byte' "$stderr_file" ||
fail 'raw-NUL capture emitted a Bash null-byte warning'
(( rc != 0 )) || fail 'raw-NUL attachment listing was accepted before bounded capture rejected it'
for listing_case in size_8192 size_8193; do
: >"$trace_file"
set +e
k3slr_local_recovery_feasibility_main --execute >>"$stdout_file" 2>>"$stderr_file"
rc=$?
set -e
(( rc != 0 )) || fail "$listing_case attachment listing was accepted"
if [[ "$listing_case" == size_8192 ]]; then
assert_eq $'listing:size-8192\nparser:rejected' "$(<"$trace_file")" \
'8192-byte listing reaches parser and is rejected only by grammar'
else
assert_eq 'listing:size-8193' "$(<"$trace_file")" \
'8193-byte listing is rejected by the capture size gate before parser'
fi
done
: >"$trace_file"
listing_case=absent
assert_succeeds k3slr_local_recovery_feasibility_main --execute >>"$stdout_file" 2>>"$stderr_file"
assert_eq $'listing:absent\nparser:absent\nimport\nlisting:present\nparser:present\nexport' \
"$(<"$trace_file")" 'normal feasibility trace is absent to present with exact parser ordering'
! /usr/bin/grep -Fq -- 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N' \
"$stdout_file" "$stderr_file" "$command_log" || fail 'attachment fixture leaked password sentinel'
)
task4_review2_attachment_parser_contract() (
local state=unchanged record=''
# shellcheck source=/dev/null
source "$LIBRARY_PATH"
assert_succeeds _k3slr_parse_keepass_attachment_listing \
$'K3s Recovery LUKS\n\nNo attachments present.\n' fixture.bin state
assert_eq absent "$state" 'canonical KeePassXC absent record parses'
assert_succeeds _k3slr_parse_keepass_attachment_listing \
$'K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n' fixture.bin state
assert_eq present "$state" 'canonical KeePassXC present record parses'
for record in \
$'K3s Recovery LUKS\n\nAttachments:\n archive.bin (1.0 KiB)\n' \
$'K3s Recovery LUKS\n\nAttachments:\n archive.bin (2.5 MiB)\n' \
$'K3s Recovery LUKS\n\nAttachments:\n archive.bin (3.0 GiB)\n'; do
state=unchanged
assert_succeeds _k3slr_parse_keepass_attachment_listing "$record" archive.bin state
assert_eq present "$state" 'parser accepts exact non-fixture attachment and IEC size unit'
state=unchanged
assert_fails _k3slr_parse_keepass_attachment_listing "$record" fixture.bin state
assert_eq unchanged "$state" 'non-target attachment leaves output unchanged'
done
for record in '' $'K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n fixture.bin (30.0 B)\n' \
$'K3s Recovery LUKS\r\n\r\nNo attachments present.\r\n' \
$'Title: K3s Recovery LUKS\n\nAttachments:\n fixture.bin (30.0 B)\n' \
$'K3s Recovery LUKS\n\nAttachments:\n fixture.bin.old (30.0 B)\n' \
$'K3s Recovery LUKS\n\nNo attachments present.\nextra\n' \
$'K3s Recovery LUKS\n\nNo attachments present.'; do
state=unchanged
assert_fails _k3slr_parse_keepass_attachment_listing "$record" fixture.bin state
assert_eq unchanged "$state" 'malformed attachment record leaves output state unchanged'
done
printf -v record 'K3s Recovery LUKS\n\n%08172d\n' 0
assert_eq 8192 "${#record}" '8192-byte parser record is final-LF framed'
state=unchanged
assert_fails _k3slr_parse_keepass_attachment_listing "$record" fixture.bin state
assert_eq unchanged "$state" '8192-byte grammar failure leaves output unchanged'
printf -v record 'K3s Recovery LUKS\n\n%08173d\n' 0
assert_eq 8193 "${#record}" '8193-byte parser record is final-LF framed'
state=unchanged
assert_fails _k3slr_parse_keepass_attachment_listing "$record" fixture.bin state
assert_eq unchanged "$state" 'oversize parser record leaves output unchanged'
assert_fails _k3slr_parse_keepass_attachment_listing \
$'K3s Recovery LUKS\n\nNo attachments present.\n' fixture.bin unsafe-name
state=unchanged
assert_fails _k3slr_parse_keepass_attachment_listing \
$'K3s Recovery LUKS\n\nNo attachments present.\n' fixture.bin _k3slr_attachment_state
assert_eq unchanged "$state" 'reserved production-local output name cannot mutate caller state'
record=$'K3s Recovery LUKS\n\nNo attachments present.\n'
assert_fails _k3slr_parse_keepass_attachment_listing \
"$record" fixture.bin _k3slr_attachment_record
assert_eq $'K3s Recovery LUKS\n\nNo attachments present.\n' "$record" \
'reserved record-local output name leaves the caller record unchanged'
)
task4_review1_two_sentinel_leak_contract() (
local first='Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N'
local second='Zz9Yy8Xx7Ww6Vv5Uu4Tt3Ss2Rr1Qq0Pp8Oo7Nn6M'
local value uid sink
[[ "$first" != "$second" && "${#first}" -eq 40 && "${#second}" -eq 40 ]] || return 1
for value in "$first" "$second"; do
[[ "$value" =~ [a-z] && "$value" =~ [A-Z] && "$value" =~ [0-9] ]] || return 1
for sink in "${fixture_root}/task4-child.cmdline" "${fixture_root}/task4-child.environ" \
"${fixture_root}"/task4-*.stdout "${fixture_root}"/task4-*.stderr; do
[[ -e "$sink" ]] || continue
! /usr/bin/grep -Fq -- "$value" "$sink" || return 1
done
uid="$(/usr/bin/id -u)"
! /usr/bin/find /tmp /run -xdev -user "$uid" -name "*${value}*" -print -quit 2>/dev/null |
/usr/bin/grep -q . || return 1
! /usr/bin/find /tmp /run -xdev -user "$uid" -type f -readable \
! -path "${fixture_root}/*" -exec /usr/bin/grep -IlF -- "$value" '{}' + \
2>/dev/null | /usr/bin/grep -q . || return 1
done
)
task4_review1_preflight_and_lineage_gate() (
local database="${K3SLR_OUTER_MOUNT}/${K3SLR_DATABASE_RELATIVE}" validation_case
export TASK4_REVIEW1_CONSUMER_LOG="${fixture_root}/task4-review1-gate-consumer.log"
export TASK4_REVIEW1_PRODUCER_LOG="${fixture_root}/task4-review1-gate-producer.log"
export TASK4_REVIEW1_COMMAND_LOG="${fixture_root}/task4-review1-gate-command.log"
export TASK4_REVIEW1_DB_STAT_LOG="${fixture_root}/task4-review1-gate-db-stat.log"
_k3slr_command() { task4_review1_command_fake "$@"; }
TASK4_REVIEW1_PRODUCER_CASE=success
export TASK4_REVIEW1_PRODUCER_CASE
for validation_case in non_tty parent_symlink; do
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_COMMAND_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
TASK4_REVIEW1_VALIDATION_CASE="$validation_case"
export TASK4_REVIEW1_VALIDATION_CASE
assert_fails _k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME"
[[ ! -s "$TASK4_REVIEW1_PRODUCER_LOG" && ! -s "$TASK4_REVIEW1_CONSUMER_LOG" ]] || return 1
done
: >"$TASK4_REVIEW1_CONSUMER_LOG"
: >"$TASK4_REVIEW1_PRODUCER_LOG"
: >"$TASK4_REVIEW1_DB_STAT_LOG"
TASK4_REVIEW1_VALIDATION_CASE=post_drift
export TASK4_REVIEW1_VALIDATION_CASE
assert_fails _k3slr_luks_open_from_keepass "$database" "$K3SLR_KEEPASS_ENTRY" /dev/loop7 "$K3SLR_MAPPING_NAME"
[[ "$(/usr/bin/wc -l <"$TASK4_REVIEW1_PRODUCER_LOG")" -eq 1 && ! -s "$TASK4_REVIEW1_CONSUMER_LOG" ]]
)
task4_review1_exact_constants_become_readonly() (
local assignment
/usr/bin/bash -c '
K3SLR_KEEPASS_ENTRY="K3s Recovery LUKS"
K3SLR_KEEPASS_HEADER_ATTACHMENT="luks-header-backup.bin"
_K3SLR_INT64_MAX=9223372036854775807
_K3SLR_REQUIRED_MINIMUM_FREE_BYTES=10737418240
source "$1"
' task4-readonly "$LIBRARY_PATH"
for assignment in 'K3SLR_KEEPASS_ENTRY=changed' \
'K3SLR_KEEPASS_HEADER_ATTACHMENT=changed' '_K3SLR_INT64_MAX=1' \
'_K3SLR_REQUIRED_MINIMUM_FREE_BYTES=1'; do
! /usr/bin/bash -c '
K3SLR_KEEPASS_ENTRY="K3s Recovery LUKS"
K3SLR_KEEPASS_HEADER_ATTACHMENT="luks-header-backup.bin"
_K3SLR_INT64_MAX=9223372036854775807
_K3SLR_REQUIRED_MINIMUM_FREE_BYTES=10737418240
source "$1"
eval "$2"
' task4-readonly "$LIBRARY_PATH" "$assignment" 2>/dev/null || return 1
done
)
task4_review1_generated_password_exact_lf() (
# shellcheck source=/dev/null
source "$FEASIBILITY_PATH"
if ! printf '%s\n' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N' |
_k3slrf_validate_generated_password_stdin; then printf 'exact-lf valid rejected\n' >&2; return 1; fi
if printf '%s' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N' |
_k3slrf_validate_generated_password_stdin; then printf 'exact-lf missing accepted\n' >&2; return 1; fi
if printf '%s\n\n' 'Aa0Bb1Cc2Dd3Ee4Ff5Gg6Hh7Ii8Jj9Kk0Ll1Mm2N' |
_k3slrf_validate_generated_password_stdin; then printf 'exact-lf double accepted\n' >&2; return 1; fi
)
task5a_wrapper_and_guard_contract() (
local wrapper main_name mode output status trace_output
for wrapper in "$PREPARE_WRAPPER_PATH" "$OPEN_WRAPPER_PATH" "$CLOSE_WRAPPER_PATH"; do
[[ -f "$wrapper" && ! -L "$wrapper" ]] || {
printf 'missing Task 5A lifecycle wrapper: %s\n' "$wrapper" >&2
return 1
}
assert_succeeds /usr/bin/bash -n "$wrapper"
done
for mode in prepare open close; do
case "$mode" in
prepare) wrapper="$PREPARE_WRAPPER_PATH" ;;
open) wrapper="$OPEN_WRAPPER_PATH" ;;
close) wrapper="$CLOSE_WRAPPER_PATH" ;;
esac
main_name="_k3slr_${mode}_main"
output="$({
PATH=/tmp/untrusted-path
LC_ALL=POSIX
umask 022
set -- --execute
# shellcheck source=/dev/null
source "$wrapper"
declare -F "$main_name" >/dev/null || return 1
declare -F _k3slr_wrapper_initial_guard >/dev/null || return 1
_k3slr_wrapper_initial_guard 0 '' >/dev/null 2>&1 && return 1
_k3slr_wrapper_initial_guard 1000 x >/dev/null 2>&1 && return 1
_k3slr_wrapper_initial_guard 1000 '' || return 1
printf '%s|%s|' "$PATH" "$LC_ALL"
umask
} 2>/dev/null)" || return 1
assert_eq '/usr/sbin:/usr/bin:/sbin:/bin|C|0077' "$output" \
"$mode wrapper establishes its early source-safe process guard"
trace_output="${fixture_root}/task5a-${mode}-xtrace.stderr"
if /usr/bin/bash -x "$wrapper" 2>"$trace_output"; then
printf '%s wrapper accepted xtrace\n' "$mode" >&2
return 1
fi
! /usr/bin/grep -Fq -- 'scripts/lib/k3s-local-recovery.sh' "$trace_output" || {
printf '%s wrapper discovered/sourced the repository before xtrace refusal\n' "$mode" >&2
return 1
}
output="$({
# shellcheck source=/dev/null
source "$wrapper"
_k3slr_lifecycle_main() {
(( $# == 2 )) || return 1
printf '%s|%s\n' "$1" "$2"
}
"$main_name"
} 2>/dev/null)" || return 1
assert_eq "${mode}|dry-run" "$output" "$mode no-argument parser selects dry-run"
for bad_args in '--help' '-h' '--execute --execute' '--unknown' 'positional'; do
# This deliberate word split enumerates fixed test arguments, never input.
# shellcheck disable=SC2086
if output="$({ source "$wrapper"; "$main_name" $bad_args; } 2>&1)"; then
printf '%s wrapper accepted invalid argv: %s\n' "$mode" "$bad_args" >&2
return 1
else
status=$?
fi
[[ "$status" == 2 && "$output" == *'Usage:'* ]] || {
printf '%s wrapper invalid argv did not produce usage exit 2: %s\n' "$mode" "$bad_args" >&2
return 1
}
done
done
assert_fails _k3slr_require_execute_tty
)
task5a_prepare_provider_capability_gate() (
local log="${fixture_root}/task5a-prepare-capability.log"
_k3slr_prove_header_restore() { :; }
export -f _k3slr_prove_header_restore
/usr/bin/bash -c '
source "$1"
! declare -F _k3slr_prove_header_restore >/dev/null
' task5a-provider-cleanup "$PREPARE_WRAPPER_PATH" || {
printf 'prepare wrapper retained an inherited/exported provider\n' >&2
return 1
}
unset -f _k3slr_prove_header_restore
# shellcheck source=/dev/null
source "$PREPARE_WRAPPER_PATH"
_k3slr_load_contract() {
(( $# == 1 )) || return 1
printf 'contract\n' >>"$log"
}
_k3slr_require_execute_tty() {
printf 'tty\n' >>"$log"
return 1
}
_k3slr_lifecycle_step() {
printf 'step:%s\n' "$1" >>"$log"
return 1
}
_k3slr_lifecycle_dispatch() {
printf 'dispatch\n' >>"$log"
return 1
}
_k3slr_command() {
printf 'unexpected-command:%s\n' "$*" >>"$log"
return 97
}
assert_fails _k3slr_prepare_main --execute
assert_eq contract "$(<"$log")" \
'prepare provider absence refuses after contract and before every later boundary'
)
task5a_open_close_capability_gate() (
local mode wrapper main_name state missing_binary log output expected line
local -a trusted_binaries=(
/usr/bin/keepassxc-cli
/usr/sbin/cryptsetup
/usr/sbin/losetup
/usr/sbin/blkid
/usr/sbin/smartctl
/usr/bin/mount
/usr/bin/umount
/usr/bin/findmnt
/usr/bin/lsblk
/usr/bin/readlink
/usr/bin/stat
/usr/bin/ntfsinfo
/usr/bin/ntfs-3g.probe
/usr/bin/fuser
/usr/bin/sync
/usr/bin/cmp
/usr/bin/cat
/usr/bin/sha256sum
/usr/bin/mawk
/usr/bin/od
/usr/bin/bash
/usr/bin/du
/usr/bin/test
/usr/bin/dd
/usr/bin/id
/usr/bin/ps
/usr/bin/sleep
/usr/bin/mkdir
/usr/bin/dpkg-query
/usr/bin/mktemp
/usr/bin/rm
/usr/bin/rmdir
/usr/bin/sudo
)
for mode in open close; do
case "$mode" in
open) wrapper="$OPEN_WRAPPER_PATH" ;;
close) wrapper="$CLOSE_WRAPPER_PATH" ;;
esac
main_name="_k3slr_${mode}_main"
log="${fixture_root}/task5a-${mode}-capability.log"
: >"$log"
# shellcheck source=/dev/null
source "$wrapper"
_k3slr_load_contract() {
(( $# == 1 )) || return 1
printf 'contract\n' >>"$log"
}
_k3slr_query_package_state() {
local destination_name="$1" package="$2" expected_version="$3" result
printf 'package:%s:%s\n' "$package" "$expected_version" >>"$log"
case "$package" in
keepassxc) result="${TASK5A_KEEPASS_STATE:-installed}" ;;
cryptsetup-bin) result="${TASK5A_CRYPTSETUP_STATE:-installed}" ;;
*) return 1 ;;
esac
printf -v "$destination_name" '%s' "$result"
}
_k3slr_trusted_root_executable() {
printf 'binary:%s\n' "$1" >>"$log"
[[ -z "${TASK5A_MISSING_BINARY:-}" || "$1" != "$TASK5A_MISSING_BINARY" ]]
}
_k3slr_require_execute_tty() {
printf 'tty\n' >>"$log"
}
_k3slr_lifecycle_dispatch() {
printf 'dispatch:%s:%s\n' "$1" "$2" >>"$log"
}
_k3slr_command() {
printf 'unexpected-command:%s\n' "$*" >>"$log"
return 97
}
TASK5A_KEEPASS_STATE=installed
TASK5A_CRYPTSETUP_STATE=installed
TASK5A_MISSING_BINARY=''
assert_succeeds "$main_name" --execute
expected=$'contract\npackage:keepassxc:2.7.6+dfsg.1-1build3\npackage:cryptsetup-bin:2:2.7.0-1ubuntu4.2'
for line in "${trusted_binaries[@]}"; do
expected+=$'\n'binary:"$line"
done
expected+=$'\ntty\ndispatch:'"$mode"':_k3slr_prove_header_restore'
assert_eq "$expected" "$(<"$log")" \
"$mode gates packages and every trusted binary before TTY/dispatch"
for state in absent mixed wrong malformed; do
: >"$log"
case "$state" in
absent)
TASK5A_KEEPASS_STATE=absent
TASK5A_CRYPTSETUP_STATE=absent
;;
mixed)
TASK5A_KEEPASS_STATE=installed
TASK5A_CRYPTSETUP_STATE=absent
;;
wrong)
TASK5A_KEEPASS_STATE=wrong-version
TASK5A_CRYPTSETUP_STATE=installed
;;
malformed)
TASK5A_KEEPASS_STATE=malformed
TASK5A_CRYPTSETUP_STATE=installed
;;
esac
assert_fails "$main_name" --execute
output="$(<"$log")"
expected=$'contract\npackage:keepassxc:2.7.6+dfsg.1-1build3\npackage:cryptsetup-bin:2:2.7.0-1ubuntu4.2'
assert_eq "$expected" "$output" "$mode $state package state stops before binary/TTY/dispatch"
done
: >"$log"
TASK5A_KEEPASS_STATE=installed
TASK5A_CRYPTSETUP_STATE=installed
TASK5A_MISSING_BINARY=/usr/bin/stat
assert_fails "$main_name" --execute
output="$(<"$log")"
[[ "$output" == *$'binary:/usr/bin/stat' && "$output" != *$'\ntty' &&
"$output" != *$'\ndispatch:' && "$output" != *'unexpected-command:'* ]] || {
printf '%s missing binary did not stop at the capability boundary\n' "$mode" >&2
return 1
}
done
)
task5a_lifecycle_dispatch_contract() (
local mode step failure_step log expected actual
local -a prepare_steps=(
sudo
context
device-validator
estimate-phase
capacity-preflight
confirm-prepare
install-packages
feasibility
revalidate-device
mount-outer
revalidate-capacity
create-layout
create-kdbx
create-container
validate-allocation
create-loop
luks-format
luks-open
mkfs-ext4
mount-inner
write-marker-metadata
header-proof
close-inner
close-mapping
detach-loop
unmount-outer
closed-validator
)
local -a open_steps=(
sudo
context
confirm-open
'device-validator(--expect-device-ready)'
revalidate-context-device
mount-outer
validate-layout-kdbx-container
validate-allocation-and-ntfs-attributes
validate-outer-metadata
collision-check
create-loop
luks-open
mount-inner
validate-inner-metadata-marker-capacity
'open-validator(--expect-open)'
)
local -a close_steps=(
sudo
context
confirm-close
'open-validator(--expect-open)'
validate-metadata-and-full-lineage
busy-process-query-pass-1
busy-process-query-pass-2
sync-inner-filesystem
unmount-inner
close-main-mapping
detach-original-loop
unmount-outer
'closed-validator(--expect-closed)'
)
log="${fixture_root}/task5a-dispatch.log"
_k3slr_lifecycle_step() {
(( $# == 1 )) || return 1
printf 'hook:%s\n' "$1" >>"$log"
}
_k3slr_lifecycle_action() {
case "$1" in
prepare)
(( $# == 3 )) || return 1
[[ "$3" == _k3slr_prepare_invocation ]] || return 1
;;
open|close)
(( $# == 2 )) || return 1
;;
*) return 1 ;;
esac
printf 'action:%s\n' "$2" >>"$log"
[[ -z "${failure_step:-}" || "$2" != "$failure_step" ]]
}
task5a_fixture_header_provider() {
return 1
}
for mode in prepare open close; do
local -a steps=()
case "$mode" in
prepare) steps=("${prepare_steps[@]}") ;;
open) steps=("${open_steps[@]}") ;;
close) steps=("${close_steps[@]}") ;;
esac
: >"$log"
failure_step=''
assert_succeeds _k3slr_lifecycle_dispatch "$mode" task5a_fixture_header_provider
expected=''
for step in "${steps[@]}"; do
[[ -z "$expected" ]] || expected+=$'\n'
expected+="hook:${step}"$'\n'"action:${step}"
done
actual="$(<"$log")"
assert_eq "$expected" "$actual" "$mode emits the independent exact lifecycle order"
case "$mode" in
prepare) failure_step=create-loop ;;
open) failure_step=collision-check ;;
close) failure_step=unmount-inner ;;
esac
: >"$log"
assert_fails _k3slr_lifecycle_dispatch "$mode" task5a_fixture_header_provider
expected=''
for step in "${steps[@]}"; do
[[ -z "$expected" ]] || expected+=$'\n'
expected+="hook:${step}"$'\n'"action:${step}"
[[ "$step" == "$failure_step" ]] && break
done
actual="$(<"$log")"
assert_eq "$expected" "$actual" \
"$mode failure includes its own hook/action and dispatches no later action"
done
)
task5a_production_actions_fail_closed() (
local lifecycle step command_calls=0 action_rc=0 probe_rc=0 record=''
local -a prepare_unavailable=(
context estimate-phase capacity-preflight confirm-prepare install-packages
feasibility revalidate-device mount-outer revalidate-capacity create-layout
create-kdbx create-container validate-allocation create-loop luks-format luks-open
mkfs-ext4 mount-inner write-marker-metadata header-proof close-inner close-mapping
detach-loop unmount-outer closed-validator
)
local -a open_unavailable=(
sudo context confirm-open 'device-validator(--expect-device-ready)'
revalidate-context-device mount-outer validate-layout-kdbx-container
validate-allocation-and-ntfs-attributes validate-outer-metadata collision-check
create-loop luks-open mount-inner validate-inner-metadata-marker-capacity
'open-validator(--expect-open)'
)
local -a close_unavailable=(
sudo context confirm-close 'open-validator(--expect-open)'
validate-metadata-and-full-lineage busy-process-query-pass-1
busy-process-query-pass-2 sync-inner-filesystem unmount-inner close-main-mapping
detach-original-loop unmount-outer 'closed-validator(--expect-closed)'
)
_k3slr_command() {
(( command_calls += 1 ))
return 97
}
task5b_unavailable_probe_payload() {
local lifecycle="${1-}" step="${2-}" action_rc=0 command_calls=0
(( $# == 2 )) || return 1
if [[ "$lifecycle" == prepare ]]; then
_k3slr_lifecycle_action prepare "$step" _k3slr_prepare_invocation ||
action_rc=$?
else
_k3slr_lifecycle_action "$lifecycle" "$step" || action_rc=$?
fi
task5b_production_probe_emit_ok unavailable "$action_rc" "$command_calls"
}
task5b_production_probe_adversary_matrix || return 1
task5b_production_probe_static_graph_is_closed unavailable || return 1
for lifecycle in prepare open close; do
local -a actions=()
case "$lifecycle" in
prepare) actions=("${prepare_unavailable[@]}") ;;
open) actions=("${open_unavailable[@]}") ;;
close) actions=("${close_unavailable[@]}") ;;
esac
for step in "${actions[@]}"; do
record=''
probe_rc=0
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm unavailable || return 1
task5b_unavailable_probe_payload "$lifecycle" "$step"
)" || probe_rc=$?
assert_eq 0 "$probe_rc" \
"historical unavailable probe completes safely: $lifecycle/$step"
assert_eq 'TASK5B_PROBE_OK|unavailable|1|0' "$record" \
"historical unavailable action is fail-closed before commands: $lifecycle/$step"
done
done
)
task5a_unowned_capture_cleanup_is_rejected() (
local description="$1" capture_directory capture_record
local rm_rc=0 rmdir_rc=0 records_preserved=0 directory_preserved=0
capture_directory="$(/usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
[[ "$capture_directory" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ &&
-d "$capture_directory" && ! -L "$capture_directory" ]] || return 1
: >"${capture_directory}/stdout"
: >"${capture_directory}/stderr"
_k3slr_command /usr/bin/rm -- "${capture_directory}/stdout" \
"${capture_directory}/stderr" >/dev/null 2>&1 || rm_rc=$?
if [[ -f "${capture_directory}/stdout" && ! -L "${capture_directory}/stdout" &&
-f "${capture_directory}/stderr" && ! -L "${capture_directory}/stderr" ]]; then
records_preserved=1
fi
for capture_record in "${capture_directory}/stdout" "${capture_directory}/stderr"; do
if [[ -f "$capture_record" && ! -L "$capture_record" ]]; then
/usr/bin/rm -- "$capture_record" || return 1
elif [[ -e "$capture_record" || -L "$capture_record" ]]; then
return 1
fi
done
_k3slr_command /usr/bin/rmdir -- "$capture_directory" \
>/dev/null 2>&1 || rmdir_rc=$?
if [[ -d "$capture_directory" && ! -L "$capture_directory" ]]; then
directory_preserved=1
/usr/bin/rmdir -- "$capture_directory" || return 1
elif [[ -e "$capture_directory" || -L "$capture_directory" ]]; then
return 1
fi
assert_eq 97 "$rm_rc" "$description rejects unowned record deletion before execution"
assert_eq 1 "$records_preserved" "$description preserves unowned capture records"
assert_eq 97 "$rmdir_rc" "$description rejects unowned directory deletion before execution"
assert_eq 1 "$directory_preserved" "$description preserves the unowned capture directory"
)
task5a_capture_path_is_safe() {
(( $# == 1 )) && [[ "$1" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ ]]
}
task5a_capture_directory_identity() {
local capture_directory="${1-}" identity
(( $# == 1 )) && task5a_capture_path_is_safe "$capture_directory" &&
[[ -d "$capture_directory" && ! -L "$capture_directory" ]] || return 97
identity="$(/usr/bin/stat --format='%d:%i' -- "$capture_directory")" || return 97
[[ "$identity" =~ ^[0-9]+:[0-9]+$ ]] || return 97
printf '%s\n' "$identity"
}
task5a_capture_log_read_exact() {
local log="${1-}" destination_name="${2-}" size line probe
local row_count=0
(( $# == 2 )) && [[ -f "$log" && ! -L "$log" ]] &&
[[ "$destination_name" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]] || return 97
local -n destination="$destination_name"
destination=()
size="$(/usr/bin/stat --format='%s' -- "$log")" || return 97
[[ "$size" =~ ^[0-9]+$ ]] && (( size <= 1048576 )) || return 97
if IFS= read -r -d '' probe <"$log"; then
return 97
fi
while :; do
line=''
if IFS= read -r line; then
row_count=$((row_count + 1))
(( row_count <= 4096 && ${#line} > 0 && ${#line} <= 512 )) || return 97
[[ "$line" != *$'\r'* ]] || return 97
destination+=("$line")
else
[[ -z "$line" ]] || return 97
break
fi
done <"$log"
}
task5a_capture_ownership_log_require() {
local ownership_log="${1-}" capture_directory="${2-}" expected_phase="${3-}"
local row event path identity active_path='' active_identity='' active_phase=''
local target_phase=absent target_identity=''
local -a rows=()
local -A phase_by_path=() identity_by_path=()
(( $# == 3 )) || return 97
case "$expected_phase" in
complete) [[ -z "$capture_directory" ]] || return 97 ;;
absent|created|records-removed|removed)
task5a_capture_path_is_safe "$capture_directory" || return 97
;;
*) return 97 ;;
esac
task5a_capture_log_read_exact "$ownership_log" rows || return 97
for row in "${rows[@]}"; do
[[ "$row" =~ ^(created|records-removed|removed)\|(/tmp/k3slr-capture\.[A-Za-z0-9]{8})\|([0-9]+:[0-9]+)$ ]] || return 97
event="${BASH_REMATCH[1]}"
path="${BASH_REMATCH[2]}"
identity="${BASH_REMATCH[3]}"
case "$event" in
created)
[[ -z "$active_path" && -z "${phase_by_path[$path]+present}" ]] || return 97
active_path="$path"
active_identity="$identity"
active_phase=created
phase_by_path["$path"]=created
identity_by_path["$path"]="$identity"
;;
records-removed)
[[ "$active_path" == "$path" && "$active_phase" == created &&
"$active_identity" == "$identity" ]] || return 97
active_phase=records-removed
phase_by_path["$path"]=records-removed
;;
removed)
[[ "$active_path" == "$path" && "$active_phase" == records-removed &&
"$active_identity" == "$identity" ]] || return 97
phase_by_path["$path"]=removed
active_path=''
active_identity=''
active_phase=''
;;
esac
done
if [[ -n "$capture_directory" && -n "${phase_by_path[$capture_directory]+present}" ]]; then
target_phase="${phase_by_path[$capture_directory]}"
target_identity="${identity_by_path[$capture_directory]}"
fi
case "$expected_phase" in
complete) [[ -z "$active_path" ]] ;;
absent) [[ "$target_phase" == absent && -z "$active_path" ]] ;;
created|records-removed)
[[ "$target_phase" == "$expected_phase" && "$active_path" == "$capture_directory" ]]
;;
removed) [[ "$target_phase" == removed && -z "$active_path" ]] ;;
esac || return 97
[[ "$expected_phase" == complete || "$expected_phase" == absent ]] ||
printf '%s\n' "$target_identity"
}
task5a_capture_event_log_require() {
local event_log="${1-}" capture_directory="${2-}" expected_phase="${3-}"
local row event path command_id active_path='' active_phase='' target_phase=absent
local command_count=0
local -a rows=()
local -A phase_by_path=()
(( $# == 3 )) || return 97
case "$expected_phase" in
complete) [[ -z "$capture_directory" ]] || return 97 ;;
absent|created|records-removed|removed)
task5a_capture_path_is_safe "$capture_directory" || return 97
;;
*) return 97 ;;
esac
task5a_capture_log_read_exact "$event_log" rows || return 97
for row in "${rows[@]}"; do
if [[ "$row" =~ ^(mktemp|rm|rmdir)\|(/tmp/k3slr-capture\.[A-Za-z0-9]{8})$ ]]; then
event="${BASH_REMATCH[1]}"
path="${BASH_REMATCH[2]}"
case "$event" in
mktemp)
[[ -z "$active_path" && -z "${phase_by_path[$path]+present}" ]] || return 97
active_path="$path"
active_phase=created
command_count=0
phase_by_path["$path"]=created
;;
rm)
[[ "$active_path" == "$path" && "$active_phase" == created ]] || return 97
active_phase=records-removed
phase_by_path["$path"]=records-removed
;;
rmdir)
[[ "$active_path" == "$path" && "$active_phase" == records-removed ]] || return 97
phase_by_path["$path"]=removed
active_path=''
active_phase=''
command_count=0
;;
esac
elif [[ "$row" =~ ^command\|([A-Za-z0-9][A-Za-z0-9._:-]{0,63})$ ]]; then
command_id="${BASH_REMATCH[1]}"
[[ -n "$command_id" && -n "$active_path" && "$active_phase" == created &&
"$command_count" == 0 ]] || return 97
command_count=1
else
return 97
fi
done
if [[ -n "$capture_directory" && -n "${phase_by_path[$capture_directory]+present}" ]]; then
target_phase="${phase_by_path[$capture_directory]}"
fi
case "$expected_phase" in
complete) [[ -z "$active_path" ]] ;;
absent) [[ "$target_phase" == absent && -z "$active_path" ]] ;;
created|records-removed)
[[ "$target_phase" == "$expected_phase" && "$active_path" == "$capture_directory" ]]
;;
removed) [[ "$target_phase" == removed && -z "$active_path" ]] ;;
esac || return 97
}
task5a_capture_ledgers_require_coherent() {
local ownership_log="${1-}" event_log="${2-}" capture_directory="${3-}"
local expected_phase="${4-}" ownership_identity row phase path entry_index
local -a ownership_rows=() event_rows=()
local -a ownership_entries=() event_entries=()
(( $# == 4 )) || return 97
ownership_identity="$(task5a_capture_ownership_log_require \
"$ownership_log" "$capture_directory" "$expected_phase")" || return 97
task5a_capture_event_log_require \
"$event_log" "$capture_directory" "$expected_phase" || return 97
task5a_capture_log_read_exact "$ownership_log" ownership_rows || return 97
task5a_capture_log_read_exact "$event_log" event_rows || return 97
for row in "${ownership_rows[@]}"; do
[[ "$row" =~ ^(created|records-removed|removed)\|(/tmp/k3slr-capture\.[A-Za-z0-9]{8})\|[0-9]+:[0-9]+$ ]] || return 97
ownership_entries+=("${BASH_REMATCH[1]}|${BASH_REMATCH[2]}")
done
for row in "${event_rows[@]}"; do
if [[ "$row" =~ ^(mktemp|rm|rmdir)\|(/tmp/k3slr-capture\.[A-Za-z0-9]{8})$ ]]; then
case "${BASH_REMATCH[1]}" in
mktemp) phase=created ;;
rm) phase=records-removed ;;
rmdir) phase=removed ;;
*) return 97 ;;
esac
path="${BASH_REMATCH[2]}"
event_entries+=("${phase}|${path}")
elif [[ "$row" =~ ^command\|[A-Za-z0-9][A-Za-z0-9._:-]{0,63}$ ]]; then
continue
else
return 97
fi
done
(( ${#ownership_entries[@]} == ${#event_entries[@]} )) || return 97
for (( entry_index=0; entry_index<${#ownership_entries[@]}; entry_index++ )); do
[[ "${ownership_entries[entry_index]}" == "${event_entries[entry_index]}" ]] ||
return 97
done
case "$expected_phase" in
created|records-removed|removed) printf '%s\n' "$ownership_identity" ;;
esac
}
task5a_capture_ledgers_allow_registration() {
local ownership_log="${1-}" event_log="${2-}"
(( $# == 2 )) || return 97
task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete
}
task5a_capture_ledger_register() {
local ownership_log="${1-}" event_log="${2-}" capture_directory="${3-}"
local current_identity
(( $# == 3 )) && task5a_capture_path_is_safe "$capture_directory" || return 97
task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" "$capture_directory" absent || return 97
current_identity="$(task5a_capture_directory_identity "$capture_directory")" || return 97
printf 'created|%s|%s\n' "$capture_directory" "$current_identity" >>"$ownership_log" ||
return 97
printf 'mktemp|%s\n' "$capture_directory" >>"$event_log" || return 97
task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" "$capture_directory" created >/dev/null || return 97
}
task5a_capture_ledger_require() {
task5a_capture_ownership_log_require "$@"
}
task5a_capture_event_phase_is() {
task5a_capture_event_log_require "$@"
}
task5a_remove_owned_capture_records() {
local ownership_log="${1-}" event_log="${2-}" capture_directory pinned_identity
local current_identity
shift 2 || return 97
(( $# == 4 )) && [[ "$1" == /usr/bin/rm && "$2" == -- &&
"$3" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}/stdout$ ]] || return 97
capture_directory="${3%/stdout}"
[[ "$4" == "${capture_directory}/stderr" && -d "$capture_directory" &&
! -L "$capture_directory" && -f "$3" && ! -L "$3" &&
-f "$4" && ! -L "$4" ]] || return 97
pinned_identity="$(task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" "$capture_directory" created)" || return 97
current_identity="$(task5a_capture_directory_identity "$capture_directory")" || return 97
[[ "$current_identity" == "$pinned_identity" ]] || return 97
"$@" || return $?
[[ ! -e "$3" && ! -L "$3" && ! -e "$4" && ! -L "$4" ]] || return 1
printf 'records-removed|%s|%s\n' "$capture_directory" "$pinned_identity" \
>>"$ownership_log" || return 97
printf 'rm|%s\n' "$capture_directory" >>"$event_log" || return 97
task5a_capture_ledgers_require_coherent "$ownership_log" "$event_log" \
"$capture_directory" records-removed >/dev/null || return 97
}
task5a_remove_owned_capture_directory() {
local ownership_log="${1-}" event_log="${2-}" capture_directory pinned_identity
local current_identity
shift 2 || return 97
(( $# == 3 )) && [[ "$1" == /usr/bin/rmdir && "$2" == -- ]] || return 97
capture_directory="$3"
task5a_capture_path_is_safe "$capture_directory" &&
[[ -d "$capture_directory" && ! -L "$capture_directory" ]] || return 97
pinned_identity="$(task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" "$capture_directory" records-removed)" || return 97
current_identity="$(task5a_capture_directory_identity "$capture_directory")" || return 97
[[ "$current_identity" == "$pinned_identity" ]] || return 97
"$@" || return $?
[[ ! -e "$capture_directory" && ! -L "$capture_directory" ]] || return 1
printf 'removed|%s|%s\n' "$capture_directory" "$pinned_identity" \
>>"$ownership_log" || return 97
printf 'rmdir|%s\n' "$capture_directory" >>"$event_log" || return 97
task5a_capture_ledgers_require_coherent "$ownership_log" "$event_log" \
"$capture_directory" removed >/dev/null || return 97
}
task5a_cleanup_capture_probe_path() {
local capture_directory="${1-}" capture_record
(( $# == 1 )) && task5a_capture_path_is_safe "$capture_directory" || return 1
if [[ ! -e "$capture_directory" && ! -L "$capture_directory" ]]; then
return 0
fi
[[ -d "$capture_directory" && ! -L "$capture_directory" ]] || return 1
for capture_record in "${capture_directory}/stdout" "${capture_directory}/stderr"; do
if [[ -f "$capture_record" && ! -L "$capture_record" ]]; then
/usr/bin/rm -- "$capture_record" || return 1
elif [[ -e "$capture_record" || -L "$capture_record" ]]; then
return 1
fi
done
/usr/bin/rmdir -- "$capture_directory"
}
task5a_capture_cleanup_phase_and_identity_is_enforced() (
local description="$1" early_path repeat_path drift_path replacement_path
local early_rmdir_rc=0 early_rm_rc=0 early_finish_rmdir_rc=0
local repeat_rm_rc=0 repeat_second_rm_rc=0 repeat_rmdir_rc=0
local reuse_rm_rc=0 reuse_rmdir_rc=0 drift_rm_rc=0 drift_rmdir_rc=0
local early_preserved=0 early_completed=0 repeat_records_preserved=0 repeat_removed=0
local reuse_records_preserved=0 reuse_directory_preserved=0
local drift_records_preserved=0 drift_directory_preserved=0
local drift_identity replacement_identity
early_path="$(_k3slr_command /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
task5a_capture_path_is_safe "$early_path" &&
[[ -d "$early_path" && ! -L "$early_path" ]] || return 1
_k3slr_command /usr/bin/rmdir -- "$early_path" \
>/dev/null 2>&1 || early_rmdir_rc=$?
[[ -d "$early_path" && ! -L "$early_path" ]] && early_preserved=1
: >"${early_path}/stdout"
: >"${early_path}/stderr"
_k3slr_command /usr/bin/rm -- "${early_path}/stdout" \
"${early_path}/stderr" >/dev/null 2>&1 || early_rm_rc=$?
_k3slr_command /usr/bin/rmdir -- "$early_path" \
>/dev/null 2>&1 || early_finish_rmdir_rc=$?
[[ ! -e "$early_path" && ! -L "$early_path" ]] && early_completed=1
task5a_cleanup_capture_probe_path "$early_path" || return 1
repeat_path="$(_k3slr_command /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
task5a_capture_path_is_safe "$repeat_path" &&
[[ -d "$repeat_path" && ! -L "$repeat_path" ]] || return 1
: >"${repeat_path}/stdout"
: >"${repeat_path}/stderr"
_k3slr_command /usr/bin/rm -- "${repeat_path}/stdout" \
"${repeat_path}/stderr" >/dev/null 2>&1 || repeat_rm_rc=$?
: >"${repeat_path}/stdout"
: >"${repeat_path}/stderr"
_k3slr_command /usr/bin/rm -- "${repeat_path}/stdout" \
"${repeat_path}/stderr" >/dev/null 2>&1 || repeat_second_rm_rc=$?
if [[ -f "${repeat_path}/stdout" && ! -L "${repeat_path}/stdout" &&
-f "${repeat_path}/stderr" && ! -L "${repeat_path}/stderr" ]]; then
repeat_records_preserved=1
fi
/usr/bin/rm -- "${repeat_path}/stdout" "${repeat_path}/stderr" || return 1
_k3slr_command /usr/bin/rmdir -- "$repeat_path" \
>/dev/null 2>&1 || repeat_rmdir_rc=$?
if [[ ! -e "$repeat_path" && ! -L "$repeat_path" ]]; then
repeat_removed=1
/usr/bin/mkdir -- "$repeat_path" || return 1
fi
[[ -d "$repeat_path" && ! -L "$repeat_path" ]] || return 1
: >"${repeat_path}/stdout"
: >"${repeat_path}/stderr"
_k3slr_command /usr/bin/rm -- "${repeat_path}/stdout" \
"${repeat_path}/stderr" >/dev/null 2>&1 || reuse_rm_rc=$?
if [[ -f "${repeat_path}/stdout" && ! -L "${repeat_path}/stdout" &&
-f "${repeat_path}/stderr" && ! -L "${repeat_path}/stderr" ]]; then
reuse_records_preserved=1
fi
/usr/bin/rm -- "${repeat_path}/stdout" "${repeat_path}/stderr" || return 1
_k3slr_command /usr/bin/rmdir -- "$repeat_path" \
>/dev/null 2>&1 || reuse_rmdir_rc=$?
[[ -d "$repeat_path" && ! -L "$repeat_path" ]] && reuse_directory_preserved=1
task5a_cleanup_capture_probe_path "$repeat_path" || return 1
drift_path="$(_k3slr_command /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
replacement_path="$(/usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
task5a_capture_path_is_safe "$drift_path" &&
task5a_capture_path_is_safe "$replacement_path" || return 1
drift_identity="$(task5a_capture_directory_identity "$drift_path")" || return 1
replacement_identity="$(task5a_capture_directory_identity "$replacement_path")" || return 1
[[ "$drift_identity" != "$replacement_identity" ]] || return 1
/usr/bin/rmdir -- "$drift_path" || return 1
/usr/bin/mv -- "$replacement_path" "$drift_path" || return 1
[[ -d "$drift_path" && ! -L "$drift_path" ]] || return 1
: >"${drift_path}/stdout"
: >"${drift_path}/stderr"
_k3slr_command /usr/bin/rm -- "${drift_path}/stdout" \
"${drift_path}/stderr" >/dev/null 2>&1 || drift_rm_rc=$?
if [[ -f "${drift_path}/stdout" && ! -L "${drift_path}/stdout" &&
-f "${drift_path}/stderr" && ! -L "${drift_path}/stderr" ]]; then
drift_records_preserved=1
fi
/usr/bin/rm -- "${drift_path}/stdout" "${drift_path}/stderr" || return 1
_k3slr_command /usr/bin/rmdir -- "$drift_path" \
>/dev/null 2>&1 || drift_rmdir_rc=$?
[[ -d "$drift_path" && ! -L "$drift_path" ]] && drift_directory_preserved=1
task5a_cleanup_capture_probe_path "$drift_path" || return 1
assert_eq 97 "$early_rmdir_rc" "$description rejects rmdir before record removal"
assert_eq 1 "$early_preserved" "$description preserves an early-rmdir owned directory"
assert_eq 0 "$early_rm_rc" "$description completes record removal after early rmdir rejection"
assert_eq 0 "$early_finish_rmdir_rc" \
"$description completes rmdir after early rmdir rejection"
assert_eq 1 "$early_completed" "$description completes the early-rmdir capture lifecycle"
assert_eq 0 "$repeat_rm_rc" "$description permits the first owned record removal"
assert_eq 97 "$repeat_second_rm_rc" "$description rejects repeated owned record removal"
assert_eq 1 "$repeat_records_preserved" "$description preserves records on repeated rm"
assert_eq 0 "$repeat_rmdir_rc" "$description permits rmdir after one successful rm"
assert_eq 1 "$repeat_removed" "$description removes a completed owned directory"
assert_eq 97 "$reuse_rm_rc" "$description rejects completed-path reuse for rm"
assert_eq 1 "$reuse_records_preserved" "$description preserves reused-path records"
assert_eq 97 "$reuse_rmdir_rc" "$description rejects completed-path reuse for rmdir"
assert_eq 1 "$reuse_directory_preserved" "$description preserves a reused directory"
assert_eq 97 "$drift_rm_rc" "$description rejects identity drift for rm"
assert_eq 1 "$drift_records_preserved" "$description preserves identity-drifted records"
assert_eq 97 "$drift_rmdir_rc" "$description rejects identity drift for rmdir"
assert_eq 1 "$drift_directory_preserved" "$description preserves an identity-drifted directory"
)
task5a_global_corrupt_capture_ledgers_are_rejected() (
local ownership_log="$1" event_log="$2" description="$3" target_path foreign_path
local rm_rc=0 records_preserved=0
local -a foreign_paths=(
/tmp/k3slr-capture.R5A00001
/tmp/k3slr-capture.R5A00002
/tmp/k3slr-capture.R5A00003
/tmp/k3slr-capture.R5A00004
)
target_path="$(_k3slr_command /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
task5a_capture_path_is_safe "$target_path" &&
[[ -d "$target_path" && ! -L "$target_path" ]] || return 1
for foreign_path in "${foreign_paths[@]}"; do
[[ "$target_path" != "$foreign_path" ]] || return 1
done
: >"${target_path}/stdout"
: >"${target_path}/stderr"
printf '%s\n' \
'removed|/tmp/k3slr-capture.R5A00001|101:101' \
'created|/tmp/k3slr-capture.R5A00002|102:102' \
'created|/tmp/k3slr-capture.R5A00002|102:102' \
'created|/tmp/k3slr-capture.R5A00003|103:103' \
'records-removed|/tmp/k3slr-capture.R5A00003|104:104' \
>>"$ownership_log"
printf '%s' 'created|/tmp/k3slr-capture.R5A00004|105:105' >>"$ownership_log"
printf '%s\n' \
'rm|/tmp/k3slr-capture.R5A00001' \
'mktemp|/tmp/k3slr-capture.R5A00002' \
'mktemp|/tmp/k3slr-capture.R5A00002' \
'malformed-event-row' \
>>"$event_log"
printf '%s' 'command|unterminated-probe' >>"$event_log"
_k3slr_command /usr/bin/rm -- "${target_path}/stdout" \
"${target_path}/stderr" >/dev/null 2>&1 || rm_rc=$?
if [[ -f "${target_path}/stdout" && ! -L "${target_path}/stdout" &&
-f "${target_path}/stderr" && ! -L "${target_path}/stderr" ]]; then
records_preserved=1
fi
task5a_cleanup_capture_probe_path "$target_path" || return 1
assert_eq 97 "$rm_rc" "$description rejects globally corrupt ledgers before deletion"
assert_eq 1 "$records_preserved" "$description preserves records for global ledger corruption"
)
task5a_cross_ledger_coherence_is_enforced() (
local ownership_log="$1" event_log="$2" path_log="$3" description="$4"
local registration_output='' registration_rc=0 registration_orphan=0
local rm_target rmdir_target target_identity rm_rc=0 rmdir_rc=0
local rm_preserved=0 rmdir_preserved=0 path_index
local -a registration_paths_before=() registration_paths_after=()
local -a path_log_before=() path_log_after=()
: >"$ownership_log"
: >"$event_log"
: >"$path_log"
printf '%s\n' \
'created|/tmp/k3slr-capture.R6OWN001|601:701' \
'records-removed|/tmp/k3slr-capture.R6OWN001|601:701' \
'removed|/tmp/k3slr-capture.R6OWN001|601:701' >"$ownership_log"
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R6EVT001' \
'rm|/tmp/k3slr-capture.R6EVT001' \
'rmdir|/tmp/k3slr-capture.R6EVT001' >"$event_log"
shopt -s nullglob
registration_paths_before=(/tmp/k3slr-capture.????????)
mapfile -t path_log_before <"$path_log"
registration_output="$(_k3slr_command /usr/bin/mktemp --directory \
--tmpdir=/tmp k3slr-capture.XXXXXXXX)" || registration_rc=$?
registration_paths_after=(/tmp/k3slr-capture.????????)
mapfile -t path_log_after <"$path_log"
if (( ${#registration_paths_before[@]} != ${#registration_paths_after[@]} )); then
registration_orphan=1
else
for (( path_index=0; path_index<${#registration_paths_before[@]}; path_index++ )); do
if [[ "${registration_paths_before[path_index]}" != \
"${registration_paths_after[path_index]}" ]]; then
registration_orphan=1
fi
done
fi
if [[ -n "$registration_output" ]]; then
task5a_capture_path_is_safe "$registration_output" || return 1
task5a_cleanup_capture_probe_path "$registration_output" || return 1
fi
rm_target="$(/usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
target_identity="$(task5a_capture_directory_identity "$rm_target")" || return 1
: >"${rm_target}/stdout"
: >"${rm_target}/stderr"
printf '%s\n' \
'created|/tmp/k3slr-capture.R6OWN002|602:702' \
'records-removed|/tmp/k3slr-capture.R6OWN002|602:702' \
'removed|/tmp/k3slr-capture.R6OWN002|602:702' \
"created|${rm_target}|${target_identity}" >"$ownership_log"
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R6EVT002' \
'rm|/tmp/k3slr-capture.R6EVT002' \
'rmdir|/tmp/k3slr-capture.R6EVT002' \
"mktemp|${rm_target}" >"$event_log"
_k3slr_command /usr/bin/rm -- "${rm_target}/stdout" \
"${rm_target}/stderr" >/dev/null 2>&1 || rm_rc=$?
if [[ -f "${rm_target}/stdout" && ! -L "${rm_target}/stdout" &&
-f "${rm_target}/stderr" && ! -L "${rm_target}/stderr" ]]; then
rm_preserved=1
fi
task5a_cleanup_capture_probe_path "$rm_target" || return 1
rmdir_target="$(/usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
target_identity="$(task5a_capture_directory_identity "$rmdir_target")" || return 1
printf '%s\n' \
'created|/tmp/k3slr-capture.R6OWN003|603:703' \
'records-removed|/tmp/k3slr-capture.R6OWN003|603:703' \
'removed|/tmp/k3slr-capture.R6OWN003|603:703' \
"created|${rmdir_target}|${target_identity}" \
"records-removed|${rmdir_target}|${target_identity}" >"$ownership_log"
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R6EVT003' \
'rm|/tmp/k3slr-capture.R6EVT003' \
'rmdir|/tmp/k3slr-capture.R6EVT003' \
"mktemp|${rmdir_target}" \
"rm|${rmdir_target}" >"$event_log"
_k3slr_command /usr/bin/rmdir -- "$rmdir_target" \
>/dev/null 2>&1 || rmdir_rc=$?
[[ -d "$rmdir_target" && ! -L "$rmdir_target" ]] && rmdir_preserved=1
task5a_cleanup_capture_probe_path "$rmdir_target" || return 1
assert_eq 97 "$registration_rc" \
"$description rejects registration for individually complete mismatched ledgers"
assert_eq '' "$registration_output" \
"$description emits no mktemp output for mismatched completed histories"
assert_eq 0 "$registration_orphan" \
"$description creates no real mktemp directory for mismatched completed histories"
assert_eq "${#path_log_before[@]}" "${#path_log_after[@]}" \
"$description does not register a path for mismatched completed histories"
assert_eq 97 "$rm_rc" \
"$description rejects rm for mismatched history plus the same active target"
assert_eq 1 "$rm_preserved" \
"$description preserves records for mismatched history plus the same active target"
assert_eq 97 "$rmdir_rc" \
"$description rejects rmdir for mismatched history plus the same active target"
assert_eq 1 "$rmdir_preserved" \
"$description preserves the directory for mismatched history plus the same active target"
)
task5a_command_append_failure_is_rejected() (
local append_target="$1" ownership_log="$2" event_log="$3" path_log="$4"
local description="$5" capture_path original_mode output='' command_rc=0
local target_preserved=0 row_index
local -a append_before=() append_after=()
local -a ownership_before=() ownership_after=()
local -a event_before=() event_after=()
local -a paths_before=() paths_after=()
shift 5 || return 1
(( $# > 0 )) && [[ -f "$append_target" && ! -L "$append_target" ]] || return 1
original_mode="$(/usr/bin/stat --format='%a' -- "$append_target")" || return 1
[[ "$original_mode" =~ ^[0-7]{3,4}$ ]] || return 1
capture_path="$(_k3slr_command /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || return 1
task5a_capture_path_is_safe "$capture_path" &&
[[ -d "$capture_path" && ! -L "$capture_path" ]] || return 1
task5a_append_failure_cleanup() {
/usr/bin/chmod "$original_mode" -- "$append_target" >/dev/null 2>&1 || :
task5a_cleanup_capture_probe_path "$capture_path" >/dev/null 2>&1 || :
}
trap task5a_append_failure_cleanup EXIT
mapfile -t append_before <"$append_target"
mapfile -t ownership_before <"$ownership_log"
mapfile -t event_before <"$event_log"
mapfile -t paths_before <"$path_log"
/usr/bin/chmod 0400 -- "$append_target" || return 1
output="$(_k3slr_command "$@" 2>/dev/null)" || command_rc=$?
/usr/bin/chmod "$original_mode" -- "$append_target" || return 1
mapfile -t append_after <"$append_target"
mapfile -t ownership_after <"$ownership_log"
mapfile -t event_after <"$event_log"
mapfile -t paths_after <"$path_log"
[[ -d "$capture_path" && ! -L "$capture_path" ]] && target_preserved=1
task5a_cleanup_capture_probe_path "$capture_path" || return 1
trap - EXIT
assert_eq 97 "$command_rc" "$description returns rc 97 when its required audit append fails"
assert_eq '' "$output" "$description does not enter the recognized command branch"
assert_eq 1 "$target_preserved" "$description preserves the active capture target"
assert_eq "${#append_before[@]}" "${#append_after[@]}" \
"$description preserves the append target row count"
for (( row_index=0; row_index<${#append_before[@]}; row_index++ )); do
assert_eq "${append_before[row_index]}" "${append_after[row_index]}" \
"$description preserves every append target row"
done
assert_eq "${#ownership_before[@]}" "${#ownership_after[@]}" \
"$description preserves the ownership ledger row count"
for (( row_index=0; row_index<${#ownership_before[@]}; row_index++ )); do
assert_eq "${ownership_before[row_index]}" "${ownership_after[row_index]}" \
"$description preserves every ownership ledger row"
done
assert_eq "${#event_before[@]}" "${#event_after[@]}" \
"$description appends no command event"
for (( row_index=0; row_index<${#event_before[@]}; row_index++ )); do
assert_eq "${event_before[row_index]}" "${event_after[row_index]}" \
"$description preserves every event ledger row"
done
assert_eq "${#paths_before[@]}" "${#paths_after[@]}" \
"$description preserves the path ledger row count"
for (( row_index=0; row_index<${#paths_before[@]}; row_index++ )); do
assert_eq "${paths_before[row_index]}" "${paths_after[row_index]}" \
"$description preserves every path ledger row"
done
)
task5a_capture_global_parser_contract() (
local ownership_log="${fixture_root}/task5a-parser-ownership.log"
local event_log="${fixture_root}/task5a-parser-events.log"
local target=/tmp/k3slr-capture.R5P00001
declare -F task5a_capture_ownership_log_require >/dev/null || return 1
declare -F task5a_capture_event_log_require >/dev/null || return 1
declare -F task5a_capture_ledgers_require_coherent >/dev/null || return 1
printf '%s\n' "created|${target}|201:301" >"$ownership_log"
printf '%s\n' "mktemp|${target}" 'command|parser-probe' >"$event_log"
assert_succeeds task5a_capture_ownership_log_require \
"$ownership_log" "$target" created >/dev/null
assert_succeeds task5a_capture_event_log_require "$event_log" "$target" created
printf 'created|%s|201:301\0\n' "$target" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf 'created|%s|201:301\r\n' "$target" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf '\n' >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf 'created|%s|201:301|extra\n' "$target" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf 'malformed-ownership-row\n' >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf 'created|%s|201:301' "$target" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf '%s\n' \
'removed|/tmp/k3slr-capture.R5P00002|202:302' \
"created|${target}|201:301" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf '%s\n' \
'created|/tmp/k3slr-capture.R5P00002|202:302' \
'created|/tmp/k3slr-capture.R5P00002|202:302' \
"created|${target}|201:301" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf '%s\n' \
'created|/tmp/k3slr-capture.R5P00002|202:302' \
'records-removed|/tmp/k3slr-capture.R5P00002|203:303' \
"created|${target}|201:301" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf '%s\n' \
'created|/tmp/k3slr-capture.R5P00002|202:302' \
"created|${target}|201:301" >"$ownership_log"
assert_fails task5a_capture_ownership_log_require "$ownership_log" "$target" created
printf 'mktemp|%s\0\n' "$target" >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf 'mktemp|%s\r\n' "$target" >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf '\n' >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf 'mktemp|%s|extra\n' "$target" >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf 'malformed-event-row\n' >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf 'mktemp|%s' "$target" >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf '%s\n' \
'rm|/tmp/k3slr-capture.R5P00002' \
"mktemp|${target}" >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R5P00002' \
'mktemp|/tmp/k3slr-capture.R5P00002' \
"mktemp|${target}" >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf '%s\n' 'command|orphan-command' "mktemp|${target}" >"$event_log"
assert_fails task5a_capture_event_log_require "$event_log" "$target" created
printf '%s\n' \
'created|/tmp/k3slr-capture.R6ORD001|611:711' \
'records-removed|/tmp/k3slr-capture.R6ORD001|611:711' \
'removed|/tmp/k3slr-capture.R6ORD001|611:711' \
'created|/tmp/k3slr-capture.R6ORD002|612:712' \
'records-removed|/tmp/k3slr-capture.R6ORD002|612:712' \
'removed|/tmp/k3slr-capture.R6ORD002|612:712' >"$ownership_log"
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R6ORD002' \
'rm|/tmp/k3slr-capture.R6ORD002' \
'rmdir|/tmp/k3slr-capture.R6ORD002' \
'mktemp|/tmp/k3slr-capture.R6ORD001' \
'rm|/tmp/k3slr-capture.R6ORD001' \
'rmdir|/tmp/k3slr-capture.R6ORD001' >"$event_log"
assert_fails task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete
printf '%s\n' \
'created|/tmp/k3slr-capture.R6PHS001|613:713' \
'records-removed|/tmp/k3slr-capture.R6PHS001|613:713' \
'removed|/tmp/k3slr-capture.R6PHS001|613:713' \
"created|${target}|201:301" >"$ownership_log"
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R6PHS001' \
'rm|/tmp/k3slr-capture.R6PHS001' \
'rmdir|/tmp/k3slr-capture.R6PHS001' \
"mktemp|${target}" \
"rm|${target}" >"$event_log"
assert_fails task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" "$target" created
printf '%s\n' \
'created|/tmp/k3slr-capture.R6MAT001|614:714' \
'records-removed|/tmp/k3slr-capture.R6MAT001|614:714' \
'removed|/tmp/k3slr-capture.R6MAT001|614:714' >"$ownership_log"
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R6MAT001' \
'rm|/tmp/k3slr-capture.R6MAT001' \
'rmdir|/tmp/k3slr-capture.R6MAT001' >"$event_log"
assert_succeeds task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete
printf '%s\n' \
'mktemp|/tmp/k3slr-capture.R6MAT001' \
'command|already-validated' \
'rm|/tmp/k3slr-capture.R6MAT001' \
'rmdir|/tmp/k3slr-capture.R6MAT001' >"$event_log"
assert_succeeds task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete
)
task5a_corrupt_capture_registration_is_rejected() (
local ownership_log="$1" event_log="$2" description="$3"
local created_output='' registration_rc=0
: >"$ownership_log"
: >"$event_log"
printf '%s\n' 'created|/tmp/k3slr-capture.R5R00001|401:501' >"$ownership_log"
printf '%s\n' 'mktemp|/tmp/k3slr-capture.R5R00001' >"$event_log"
created_output="$(_k3slr_command /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX)" || registration_rc=$?
if [[ -n "$created_output" ]]; then
task5a_capture_path_is_safe "$created_output" &&
[[ -d "$created_output" && ! -L "$created_output" ]] || return 1
task5a_cleanup_capture_probe_path "$created_output" || return 1
fi
assert_eq 97 "$registration_rc" \
"$description rejects registration with a foreign incomplete capture"
assert_eq '' "$created_output" "$description creates no directory for rejected registration"
)
task5a_assert_capture_ownership_complete() {
local ownership_log="$1" event_log="$2" path_log="$3" description="$4"
local capture_path pinned_identity
local -a capture_paths=() ownership_rows=()
mapfile -t capture_paths <"$path_log"
mapfile -t ownership_rows <"$ownership_log"
assert_eq "$(( ${#capture_paths[@]} * 3 ))" "${#ownership_rows[@]}" \
"$description records exactly three ownership phases per capture"
for capture_path in "${capture_paths[@]}"; do
pinned_identity="$(task5a_capture_ledger_require \
"$ownership_log" "$capture_path" removed)" || return 1
[[ "$pinned_identity" =~ ^[0-9]+:[0-9]+$ ]] || return 1
task5a_capture_event_log_require "$event_log" "$capture_path" removed || return 1
done
task5a_capture_ownership_log_require "$ownership_log" '' complete || return 1
task5a_capture_event_log_require "$event_log" '' complete || return 1
}
task5a_exact_record_and_pure_parser_contract() (
local value state total free used phase marker_hash package_output package_error package_rc
local valid_hash=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
local valid_metadata valid_metadata_hex valid_marker valid_marker_hex invalid_hex expected
local context_case='' package_case='' invalid_rc=0
local event_log="${fixture_root}/task5a-record-events.log"
local path_log="${fixture_root}/task5a-record-paths.log"
local ownership_log="${fixture_root}/task5a-record-ownership.log"
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
task5a_record_argv_equals() {
local expected_count="${1-}" argument_index
local -a compared_arguments=()
shift || return 1
[[ "$expected_count" =~ ^[1-9][0-9]*$ ]] || return 1
compared_arguments=("$@")
(( ${#compared_arguments[@]} == 2 * expected_count )) || return 1
for (( argument_index=0; argument_index<expected_count; argument_index++ )); do
[[ "${compared_arguments[argument_index]}" == \
"${compared_arguments[expected_count + argument_index]}" ]] || return 1
done
}
task5a_assert_record_capture_lineage() {
local expected_ids_record="$1" description="$2"
local event_index capture_path expected_count
local -a expected_ids=() events=() capture_paths=()
mapfile -t expected_ids <<<"$expected_ids_record"
mapfile -t events <"$event_log"
mapfile -t capture_paths <"$path_log"
expected_count="${#expected_ids[@]}"
assert_eq "$expected_count" "${#capture_paths[@]}" \
"$description creates one capture directory per query"
assert_eq "$((expected_count * 4))" "${#events[@]}" \
"$description records complete capture lifecycle groups"
for (( event_index=0; event_index<expected_count; event_index++ )); do
capture_path="${capture_paths[event_index]}"
[[ "$capture_path" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ ]] || return 1
assert_eq "mktemp|${capture_path}" "${events[event_index * 4]}" \
"$description starts capture with the exact created path"
assert_eq "command|${expected_ids[event_index]}" \
"${events[event_index * 4 + 1]}" \
"$description preserves the exact query order"
assert_eq "rm|${capture_path}" "${events[event_index * 4 + 2]}" \
"$description removes exactly the two capture records"
assert_eq "rmdir|${capture_path}" "${events[event_index * 4 + 3]}" \
"$description removes the same capture directory"
[[ ! -e "$capture_path" && ! -L "$capture_path" ]] || return 1
done
task5a_assert_capture_ownership_complete \
"$ownership_log" "$event_log" "$path_log" "$description"
}
_k3slr_command() {
local created capture_directory command_id=rejected-command
case "${1-}" in
/usr/bin/mktemp)
task5a_record_argv_equals 4 /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX "$@" || return 97
task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete || return 97
created="$("$@")" || return 1
[[ "$created" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ &&
-d "$created" && ! -L "$created" ]] || return 97
if ! task5a_capture_ledger_register \
"$ownership_log" "$event_log" "$created"; then
task5a_cleanup_capture_probe_path "$created" || return 1
return 97
fi
printf '%s\n' "$created" >>"$path_log"
printf '%s\n' "$created"
return 0
;;
/usr/bin/rm)
task5a_remove_owned_capture_records "$ownership_log" "$event_log" "$@"
return
;;
/usr/bin/rmdir)
task5a_remove_owned_capture_directory "$ownership_log" "$event_log" "$@"
return
;;
/usr/local/bin/k3s)
task5a_record_argv_equals 4 /usr/local/bin/k3s kubectl config \
current-context "$@" || return 97
command_id=current-context
;;
/usr/bin/dpkg-query)
if task5a_record_argv_equals 4 /usr/bin/dpkg-query --show \
'--showformat=${Status}|${Version}\n' keepassxc "$@"; then
command_id=package-keepassxc
elif task5a_record_argv_equals 4 /usr/bin/dpkg-query --show \
'--showformat=${Status}|${Version}\n' cryptsetup-bin "$@"; then
command_id=package-cryptsetup-bin
else
return 97
fi
;;
*) return 97 ;;
esac
printf 'command|%s\n' "$command_id" >>"$event_log" || return 97
case "$command_id" in
current-context)
case "$context_case" in
success) printf 'default\n' ;;
missing_lf) printf default ;;
stderr) printf 'default\n'; printf 'warning\n' >&2 ;;
status) printf 'default\n'; return 1 ;;
nul) printf 'default\0\n' ;;
*) return 97 ;;
esac
;;
package-keepassxc|package-cryptsetup-bin)
printf '%s' "$package_output"
printf '%s' "$package_error" >&2
return "$package_rc"
;;
esac
}
task5a_fixture_ascii_hex() {
local input="$1" index character encoded
for (( index=0; index<${#input}; index++ )); do
character="${input:index:1}"
printf -v encoded '%02x' "'$character"
printf '%s' "$encoded"
done
}
valid_metadata=$'schema=k3slr-runtime-v1\nluks_uuid=12345678-1234-4abc-8def-1234567890ab\ncontainer_bytes=34359738368\ninner_label=K3S_RECOVERY\nmarker_sha256=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n'
valid_metadata_hex="$(task5a_fixture_ascii_hex "$valid_metadata")"$'\n'
valid_marker=$'12345678-1234-4abc-8def-1234567890ab\n'
valid_marker_hex="$(task5a_fixture_ascii_hex "$valid_marker")"$'\n'
assert_succeeds _k3slr_parse_current_context_record $'default\n' value
assert_eq default "$value" 'exact current context parses'
for value in default $'default\n\n' $'default\r\n' $'other\n' ''; do
assert_fails _k3slr_parse_current_context_record "$value" value
done
assert_succeeds task5a_unowned_capture_cleanup_is_rejected 'record capture fake'
assert_succeeds task5a_corrupt_capture_registration_is_rejected \
"$ownership_log" "$event_log" 'record capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
assert_succeeds task5a_cross_ledger_coherence_is_enforced \
"$ownership_log" "$event_log" "$path_log" 'record capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
assert_succeeds task5a_capture_global_parser_contract
assert_succeeds task5a_global_corrupt_capture_ledgers_are_rejected \
"$ownership_log" "$event_log" 'record capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
assert_succeeds task5a_capture_cleanup_phase_and_identity_is_enforced \
'record capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
context_case=success
assert_succeeds task5a_command_append_failure_is_rejected \
"$event_log" "$ownership_log" "$event_log" "$path_log" \
'record fake command-event append failure' \
/usr/local/bin/k3s kubectl config current-context
context_case=''
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
_k3slr_command /fixture/unknown >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'record fake rejects unknown argv with rc 97'
invalid_rc=0
_k3slr_command /usr/local/bin/k3s 'kubectl config' current-context \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" \
'context fake rejects flattened text with wrong argv grouping'
invalid_rc=0
_k3slr_command /usr/local/bin/k3s kubectl config current-context unexpected \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'context fake rejects an extra argv element'
invalid_rc=0
_k3slr_command /usr/local/bin/k3s kubectl config \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'context fake rejects a missing argv element'
invalid_rc=0
_k3slr_command /usr/local/bin/k3s kubectl config get-contexts \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'context fake rejects unknown same-executable argv'
invalid_rc=0
_k3slr_command /usr/bin/dpkg-query --show \
'--showformat=${Status}|${Version}\n keepassxc' \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" \
'package fake rejects flattened text with wrong argv grouping'
invalid_rc=0
_k3slr_command /usr/bin/dpkg-query --show \
'--showformat=${Status}|${Version}\n' keepassxc unexpected \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'package fake rejects an extra argv element'
invalid_rc=0
_k3slr_command /usr/bin/dpkg-query --show \
'--showformat=${Status}|${Version}\n' \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'package fake rejects a missing argv element'
invalid_rc=0
_k3slr_command /usr/bin/dpkg-query --list keepassxc \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'package fake rejects unknown same-executable argv'
[[ ! -s "$event_log" && ! -s "$path_log" ]] || return 1
context_case=success
assert_succeeds _k3slr_current_context value
assert_eq default "$value" 'current context query uses exact argv and exact record'
context_case=missing_lf
assert_fails _k3slr_current_context value
context_case=stderr
assert_fails _k3slr_current_context value
context_case=status
assert_fails _k3slr_current_context value
context_case=nul
assert_fails _k3slr_current_context value
assert_succeeds _k3slr_parse_package_query_record state keepassxc \
'2.7.6+dfsg.1-1build3' 0 $'install ok installed|2.7.6+dfsg.1-1build3\n' ''
assert_eq installed "$state" 'exact installed package record parses'
assert_succeeds _k3slr_parse_package_query_record state cryptsetup-bin \
'2:2.7.0-1ubuntu4.2' 1 '' $'dpkg-query: no packages found matching cryptsetup-bin\n'
assert_eq absent "$state" 'exact absent package record parses'
assert_succeeds _k3slr_parse_package_query_record state keepassxc \
'2.7.6+dfsg.1-1build3' 0 $'install ok installed|9.9.9\n' ''
assert_eq wrong-version "$state" 'wrong installed package version is classified'
assert_succeeds _k3slr_parse_package_query_record state keepassxc \
'2.7.6+dfsg.1-1build3' 0 $'deinstall ok config-files|2.7.6+dfsg.1-1build3\n' ''
assert_eq malformed "$state" 'wrong dpkg status is malformed'
assert_succeeds _k3slr_parse_package_query_record state keepassxc \
'2.7.6+dfsg.1-1build3' 1 'unexpected' $'dpkg-query: no packages found matching keepassxc\n'
assert_eq malformed "$state" 'absent record with stdout is malformed'
assert_succeeds _k3slr_parse_package_query_record state keepassxc \
'2.7.6+dfsg.1-1build3' 0 $'install ok installed|2.7.6+dfsg.1-1build3\n' $'warning\n'
assert_eq malformed "$state" 'installed record with stderr is malformed'
assert_succeeds _k3slr_parse_package_query_record state keepassxc \
'2.7.6+dfsg.1-1build3' 2 '' ''
assert_eq malformed "$state" 'unexpected dpkg exit is malformed'
assert_fails _k3slr_parse_package_query_record state other-package 1.0 0 $'install ok installed|1.0\n' ''
package_output=$'install ok installed|2.7.6+dfsg.1-1build3\n'
package_error=''
package_rc=0
assert_succeeds _k3slr_query_package_state state keepassxc '2.7.6+dfsg.1-1build3'
assert_eq installed "$state" 'package query uses exact argv and parses its record'
package_output=''
package_error=$'dpkg-query: no packages found matching cryptsetup-bin\n'
package_rc=1
assert_succeeds _k3slr_query_package_state state cryptsetup-bin '2:2.7.0-1ubuntu4.2'
assert_eq absent "$state" 'package query preserves exact absent stderr record'
package_output='install ok installed|2.7.6+dfsg.1-1build3'
package_error=''
package_rc=0
assert_succeeds _k3slr_query_package_state state keepassxc '2.7.6+dfsg.1-1build3'
assert_eq malformed "$state" 'package query preserves missing-LF framing for rejection'
package_output=$'install ok installed|2.7.6+dfsg.1-1build3\n'
package_error=$'warning\n'
package_rc=0
assert_succeeds _k3slr_query_package_state state keepassxc '2.7.6+dfsg.1-1build3'
assert_eq malformed "$state" 'package query preserves unexpected stderr for rejection'
expected="$(printf '%s\n' \
current-context \
current-context \
current-context \
current-context \
current-context \
package-keepassxc \
package-cryptsetup-bin \
package-keepassxc \
package-keepassxc)"
assert_succeeds task5a_assert_record_capture_lineage "$expected" \
'context and package composition'
assert_succeeds _k3slr_parse_phase_bytes_record $'phase_bytes=4294967296\n' phase
assert_eq 4294967296 "$phase" 'exact estimator record parses'
for value in 'phase_bytes=1' $'phase_bytes=01\n' $'phase_bytes=-1\n' \
$'phase_bytes=1\nextra=2\n' $'bytes=1\n' $'phase_bytes=9223372036854775808\n'; do
assert_fails _k3slr_parse_phase_bytes_record "$value" phase
done
assert_succeeds _k3slr_max_phase_bytes phase 7 11 9
assert_eq 11 "$phase" 'phase maximum never decreases after a lower observation'
assert_succeeds _k3slr_max_phase_bytes phase 11 11 15
assert_eq 15 "$phase" 'phase maximum advances for a larger third observation'
assert_fails _k3slr_max_phase_bytes phase 1 bad 3
assert_succeeds _k3slr_parse_statfs_record $'4096|1000|250\n' total free used
assert_eq 4096000 "$total" 'statfs total bytes use fundamental block size'
assert_eq 1024000 "$free" 'statfs free bytes use blocks available to user'
assert_eq 3072000 "$used" 'statfs conservative used bytes exclude unavailable blocks'
for value in '4096|1000|250' $'4096|1000|1001\n' $'0|1|1\n' \
$'4096|01|1\n' $'9223372036854775807|2|1\n' $'4096|1|1\nextra\n'; do
assert_fails _k3slr_parse_statfs_record "$value" total free used
done
assert_fails _k3slr_parse_statfs_record $'1|1|1\n' total total used
K3SLR_MINIMUM_FREE_BYTES=10737418240
assert_succeeds _k3slr_conservative_inner_capacity_fits \
34359738368 2147483648
assert_fails _k3slr_conservative_inner_capacity_fits \
34359738368 4294967296
assert_fails _k3slr_conservative_inner_capacity_fits 0 1
K3SLR_CONTAINER_SIZE_BYTES=34359738368
K3SLR_INNER_LABEL=K3S_RECOVERY
! declare -F _k3slr_parse_runtime_metadata >/dev/null || \
fail 'raw runtime metadata/UUID shell-variable parser API is forbidden'
! declare -F _k3slr_marker_record_is_valid >/dev/null || \
fail 'raw 37-byte marker shell-variable parser API is forbidden'
assert_succeeds _k3slr_parse_runtime_metadata_hex "$valid_metadata_hex" marker_hash
assert_eq "$valid_hash" "$marker_hash" 'runtime metadata returns marker hash'
assert_succeeds _k3slr_marker_hex_is_valid "$valid_marker_hex"
for value in \
"${valid_metadata%$'\n'}" \
"${valid_metadata/luks_uuid=/luks-uuid=}" \
"${valid_metadata/container_bytes=34359738368/container_bytes=34359738369}" \
"${valid_metadata/inner_label=K3S_RECOVERY/inner_label=OTHER}" \
"${valid_metadata/marker_sha256=/marker_sha256=A}" \
$'schema=k3slr-runtime-v1\r\nluks_uuid=12345678-1234-4abc-8def-1234567890ab\ncontainer_bytes=34359738368\ninner_label=K3S_RECOVERY\nmarker_sha256=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n'; do
invalid_hex="$(task5a_fixture_ascii_hex "$value")"$'\n'
assert_fails _k3slr_parse_runtime_metadata_hex "$invalid_hex" marker_hash
done
for value in $'12345678-1234-3abc-8def-1234567890ab\n' \
$'12345678-1234-4abc-7def-1234567890ab\n' \
'12345678-1234-4abc-8def-1234567890ab'; do
invalid_hex="$(task5a_fixture_ascii_hex "$value")"$'\n'
assert_fails _k3slr_marker_hex_is_valid "$invalid_hex"
done
)
task5a_ntfs_capacity_and_prepackage_seams() (
local value valid_text valid_hex invalid_hex od_record oversized snapshot snapshot_after
local total free identity output expected_identity calls=0
task5a_fixture_ascii_hex() {
local input="$1" index character encoded
for (( index=0; index<${#input}; index++ )); do
character="${input:index:1}"
printf -v encoded '%02x' "'$character"
printf '%s' "$encoded"
done
}
task5a_fixture_prepackage_snapshot() {
printf '%s\n' \
'canonical_partition=/dev/sda3' \
'partition_major_minor=8:3' \
'canonical_parent_disk=/dev/sda' \
'disk_major_minor=8:0' \
"filesystem_uuid=${K3SLR_RECOVERY_FS_UUID}" \
"partuuid=${K3SLR_RECOVERY_PARTUUID}" \
'filesystem_type=ntfs' \
"model=${K3SLR_RECOVERY_MODEL}" \
"serial=${K3SLR_RECOVERY_SERIAL}" \
"wwn=${K3SLR_RECOVERY_WWN}" \
'canonical_k3s_partition=/dev/sdb2' \
'k3s_partition_major_minor=8:34' \
'canonical_k3s_disk=/dev/sdb' \
'k3s_disk_major_minor=8:32' \
"k3s_filesystem_uuid=${K3SLR_K3S_FS_UUID}" \
"k3s_partuuid=${K3SLR_K3S_PARTUUID}" \
'k3s_filesystem_type=ext4' \
"k3s_model=${K3SLR_K3S_MODEL}" \
"k3s_serial=${K3SLR_K3S_SERIAL}" \
"k3s_wwn=${K3SLR_K3S_WWN}" \
'smart_health=PASSED' \
'smart_reallocated=0' \
'smart_pending=0' \
'smart_uncorrectable=0' \
'outer_mount=absent' \
'inner_mount=absent' \
'source_mounts=absent' \
'loops=absent' \
'mapper_directory=physical-root-safe' \
'mapping_path=absent' \
'proof_mapping_path=absent' \
'mapping_names=absent' \
'ntfs_probe=pass'
}
od_record=$' 20 30 61 0a\n'
assert_succeeds _k3slr_normalize_od_hex_record "$od_record" value
assert_eq $'2030610a\n' "$value" 'od tokens normalize to one bounded lowercase hex record'
for od_record in $' 20 0A 0a\n' $' 20 gg 0a\n' ' 20 0a' $'\n'; do
assert_fails _k3slr_normalize_od_hex_record "$od_record" value
done
oversized='00 '
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16; do
oversized+="$oversized"
done
oversized+='00 '
oversized="${oversized:0:196611}"$'\n'
assert_fails _k3slr_normalize_od_hex_record "$oversized" value
valid_text=$'Volume Information\n\tCluster Size: 4096\n Volume Size in Clusters: 1000000\nAllocated clusters 250000 (25.0%)\nFree Clusters: 750000 (75.0%)\n'
valid_hex="$(task5a_fixture_ascii_hex "$valid_text")"$'\n'
assert_succeeds _k3slr_parse_unmounted_ntfs_capacity_hex "$valid_hex" total free
assert_eq 4096000000 "$total" 'NTFS total bytes are cluster size times volume clusters'
assert_eq 3072000000 "$free" 'NTFS free bytes are cluster size times free clusters'
output="$(_k3slr_parse_unmounted_ntfs_capacity_hex "$valid_hex" total free)" || return 1
assert_eq '' "$output" 'NTFS parser never logs normalized or decoded evidence'
for value in \
"${valid_text%$'\n'}" \
"${valid_text/Cluster Size: 4096/Cluster Size: 04096}" \
$'Volume Information\n\tCluster\tSize: 4096\nVolume Size in Clusters: 1000000\nAllocated clusters 250000 (25.0%)\nFree Clusters: 750000 (75.0%)\n' \
$'Volume Information\n\tCluster Size:\t4096\nVolume Size in Clusters: 1000000\nAllocated clusters 250000 (25.0%)\nFree Clusters: 750000 (75.0%)\n' \
"${valid_text/Allocated clusters 250000/Allocated clusters 250001}" \
"${valid_text/25.0%/25.1%}" \
"${valid_text/Free Clusters:/Free clusters:}" \
"${valid_text/Free Clusters: 750000 (75.0%)/Free Clusters: 750000 (75.0%) suffix}" \
"${valid_text}Free Clusters: 750000 (75.0%)" \
"${valid_text}Cluster Size: 4096 suffix"$'\n' \
$'Cluster Size: 9223372036854775807\nVolume Size in Clusters: 2\nAllocated clusters 1 (50.0%)\nFree Clusters: 1 (50.0%)\n'; do
invalid_hex="$(task5a_fixture_ascii_hex "$value")"$'\n'
assert_fails _k3slr_parse_unmounted_ntfs_capacity_hex "$invalid_hex" total free
done
invalid_hex="${valid_hex%$'\n'}"
invalid_hex="${invalid_hex:0:12}00${invalid_hex:12}"$'\n'
assert_fails _k3slr_parse_unmounted_ntfs_capacity_hex "$invalid_hex" total free
invalid_hex="${valid_hex%$'\n'}"
invalid_hex="${invalid_hex:0:12}0d${invalid_hex:12}"$'\n'
assert_fails _k3slr_parse_unmounted_ntfs_capacity_hex "$invalid_hex" total free
invalid_hex="${valid_hex^^}"
assert_fails _k3slr_parse_unmounted_ntfs_capacity_hex "$invalid_hex" total free
assert_fails _k3slr_parse_unmounted_ntfs_capacity_hex "${valid_hex%?}f" total free
snapshot="$(task5a_fixture_prepackage_snapshot)"$'\n'
assert_succeeds _k3slr_validate_prepackage_snapshot "$snapshot"
assert_fails _k3slr_parse_prepackage_snapshot_fields "$snapshot" 'bad-name'
assert_fails _k3slr_parse_prepackage_snapshot_fields "$snapshot" _k3slr_snapshot_alias
for snapshot_after in \
"${snapshot/smart_pending=0/smart_pending=1}" \
"${snapshot/ntfs_probe=pass/ntfs_probe=fail}" \
"${snapshot/loops=absent/loops=present}" \
"${snapshot/disk_major_minor=8:0/disk_major_minor=8:32}" \
"${snapshot/filesystem_uuid=${K3SLR_RECOVERY_FS_UUID}/filesystem_uuid=AAAAAAAAAAAAAAAA}" \
"${snapshot}mapping_names=absent"; do
assert_fails _k3slr_validate_prepackage_snapshot "$snapshot_after"
done
_k3slr_collect_prepackage_snapshot() {
local destination_name="$1" selected="$snapshot"
calls=$((calls + 1))
if (( calls == 2 )) && [[ -n "${TASK5A_SECOND_SNAPSHOT:-}" ]]; then
selected="$TASK5A_SECOND_SNAPSHOT"
fi
printf -v "$destination_name" '%s' "$selected"
}
_k3slr_capture_ntfsinfo_hex() {
local destination_name="$1" partition="$2"
[[ "$partition" == /dev/sda3 ]] || return 1
printf -v "$destination_name" '%s' "$valid_hex"
}
calls=0
TASK5A_SECOND_SNAPSHOT=''
assert_succeeds _k3slr_prepackage_device_preflight
assert_eq 2 "$calls" 'pre-package device preflight pins byte-equal before/after snapshots'
calls=0
TASK5A_SECOND_SNAPSHOT="${snapshot/serial=${K3SLR_RECOVERY_SERIAL}/serial=DRIFTED}"
assert_fails _k3slr_prepackage_device_preflight
assert_eq 2 "$calls" 'pre-package drift is observed before refusal'
calls=0
TASK5A_SECOND_SNAPSHOT=''
assert_succeeds _k3slr_collect_unmounted_ntfs_capacity value identity
assert_eq "$valid_hex" "$value" 'capacity collector returns only normalized hex'
expected_identity="/dev/sda3|8:3|/dev/sda|8:0|${K3SLR_RECOVERY_FS_UUID}|${K3SLR_RECOVERY_PARTUUID}|ntfs|${K3SLR_RECOVERY_MODEL}|${K3SLR_RECOVERY_SERIAL}|${K3SLR_RECOVERY_WWN}|8:32"
assert_eq "$expected_identity" "$identity" 'capacity collector returns the strict pinned identity tuple'
assert_eq 2 "$calls" 'capacity collector repeats the full snapshot around ntfsinfo'
calls=0
TASK5A_SECOND_SNAPSHOT="${snapshot/partition_major_minor=8:3/partition_major_minor=8:4}"
value=unchanged
identity=unchanged
assert_fails _k3slr_collect_unmounted_ntfs_capacity value identity
assert_eq unchanged "$value" 'drift failure does not publish normalized hex'
assert_eq unchanged "$identity" 'drift failure does not publish identity'
)
task5a_exact_process_capture_contract() (
local capture_case=success stdout stderr status invalid_rc=0 expected
local event_log="${fixture_root}/task5a-capture-events.log"
local path_log="${fixture_root}/task5a-capture-paths.log"
local ownership_log="${fixture_root}/task5a-capture-ownership.log"
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
task5a_capture_argv_equals() {
local expected_count="${1-}" argument_index
local -a compared_arguments=()
shift || return 1
[[ "$expected_count" =~ ^[1-9][0-9]*$ ]] || return 1
compared_arguments=("$@")
(( ${#compared_arguments[@]} == 2 * expected_count )) || return 1
for (( argument_index=0; argument_index<expected_count; argument_index++ )); do
[[ "${compared_arguments[argument_index]}" == \
"${compared_arguments[expected_count + argument_index]}" ]] || return 1
done
}
task5a_assert_process_capture_lineage() {
local expected_ids_record="$1" description="$2"
local event_index capture_path expected_count
local -a expected_ids=() events=() capture_paths=()
mapfile -t expected_ids <<<"$expected_ids_record"
mapfile -t events <"$event_log"
mapfile -t capture_paths <"$path_log"
expected_count="${#expected_ids[@]}"
assert_eq "$expected_count" "${#capture_paths[@]}" \
"$description creates one capture directory per execution"
assert_eq "$((expected_count * 4))" "${#events[@]}" \
"$description records complete capture lifecycle groups"
for (( event_index=0; event_index<expected_count; event_index++ )); do
capture_path="${capture_paths[event_index]}"
[[ "$capture_path" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ ]] || return 1
assert_eq "mktemp|${capture_path}" "${events[event_index * 4]}" \
"$description starts capture with its exact created path"
assert_eq "command|${expected_ids[event_index]}" \
"${events[event_index * 4 + 1]}" \
"$description runs the exact fixture argv"
assert_eq "rm|${capture_path}" "${events[event_index * 4 + 2]}" \
"$description removes exactly stdout and stderr"
assert_eq "rmdir|${capture_path}" "${events[event_index * 4 + 3]}" \
"$description removes the same capture directory"
[[ ! -e "$capture_path" && ! -L "$capture_path" ]] || return 1
done
task5a_assert_capture_ownership_complete \
"$ownership_log" "$event_log" "$path_log" "$description"
}
_k3slr_command() {
local created capture_directory
case "${1-}" in
/usr/bin/mktemp)
task5a_capture_argv_equals 4 /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX "$@" || return 97
task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete || return 97
created="$("$@")" || return 1
[[ "$created" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ &&
-d "$created" && ! -L "$created" ]] || return 97
if ! task5a_capture_ledger_register \
"$ownership_log" "$event_log" "$created"; then
task5a_cleanup_capture_probe_path "$created" || return 1
return 97
fi
printf '%s\n' "$created" >>"$path_log"
printf '%s\n' "$created"
return 0
;;
/usr/bin/rm)
task5a_remove_owned_capture_records "$ownership_log" "$event_log" "$@"
return
;;
/usr/bin/rmdir)
task5a_remove_owned_capture_directory "$ownership_log" "$event_log" "$@"
return
;;
/fixture/capture)
task5a_capture_argv_equals 3 /fixture/capture --mode emit "$@" || return 97
printf 'command|fixture-capture\n' >>"$event_log" || return 97
case "$capture_case" in
success)
printf 'stdout line\n'
printf 'stderr line\n' >&2
return 7
;;
nul) printf 'unsafe\0record\n' ;;
*) return 97 ;;
esac
;;
*) return 97 ;;
esac
}
assert_succeeds task5a_unowned_capture_cleanup_is_rejected \
'generic process capture fake'
assert_succeeds task5a_capture_cleanup_phase_and_identity_is_enforced \
'generic process capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
assert_succeeds task5a_cross_ledger_coherence_is_enforced \
"$ownership_log" "$event_log" "$path_log" 'generic process capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
capture_case=success
assert_succeeds task5a_command_append_failure_is_rejected \
"$event_log" "$ownership_log" "$event_log" "$path_log" \
'generic fake command-event append failure' \
/fixture/capture --mode emit
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
_k3slr_command /fixture/unknown >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'process-capture fake rejects unknown argv with rc 97'
invalid_rc=0
_k3slr_command /fixture/capture '--mode emit' \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" \
'process-capture fake rejects flattened text with wrong argv grouping'
invalid_rc=0
_k3slr_command /fixture/capture --mode emit unexpected \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" \
'process-capture fake rejects the known executable with an extra argv element'
invalid_rc=0
_k3slr_command /fixture/capture --mode >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" \
'process-capture fake rejects the known executable with a missing argv element'
invalid_rc=0
_k3slr_command /fixture/capture --mode discard \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" \
'process-capture fake rejects unknown same-executable argv'
[[ ! -s "$event_log" && ! -s "$path_log" ]] || return 1
assert_succeeds _k3slr_capture_process stdout stderr status \
/fixture/capture --mode emit
assert_eq $'stdout line\n' "$stdout" 'process capture preserves stdout terminal LF'
assert_eq $'stderr line\n' "$stderr" 'process capture preserves stderr terminal LF'
assert_eq 7 "$status" 'process capture preserves nonzero exit status'
capture_case=nul
assert_fails _k3slr_capture_process stdout stderr status \
/fixture/capture --mode emit
expected="$(printf '%s\n' fixture-capture fixture-capture)"
assert_succeeds task5a_assert_process_capture_lineage "$expected" \
'success and NUL-rejection process capture'
assert_fails _k3slr_capture_process stdout stdout status \
/fixture/capture --mode emit
assert_succeeds task5a_assert_process_capture_lineage "$expected" \
'duplicate-destination rejection leaves no capture residue'
)
task5a_prepackage_system_command_matrix() (
local log="${fixture_root}/task5a-prepackage-system.log"
local event_log="${fixture_root}/task5a-prepackage-system-events.log"
local path_log="${fixture_root}/task5a-prepackage-system-paths.log"
local ownership_log="${fixture_root}/task5a-prepackage-system-ownership.log"
local cryptsetup_log="${fixture_root}/task5a-prepackage-system-cryptsetup.log"
local snapshot system_case expected unknown_rc=0 wrong_group_rc=0 extra_arg_rc=0 missing_arg_rc=0
: >"$log"
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
: >"$cryptsetup_log"
task5a_argv_equals() {
local expected_count="${1-}" argument_index
local -a compared_arguments=()
shift || return 1
[[ "$expected_count" =~ ^[1-9][0-9]*$ ]] || return 1
compared_arguments=("$@")
(( ${#compared_arguments[@]} == 2 * expected_count )) || return 1
for (( argument_index=0; argument_index<expected_count; argument_index++ )); do
[[ "${compared_arguments[argument_index]}" == \
"${compared_arguments[expected_count + argument_index]}" ]] || return 1
done
}
task5a_assert_capture_lineage_and_cleanup() {
local expected_ids_record="$1" description="$2"
local event_index capture_path expected_count
local -a expected_ids=() events=() capture_paths=()
mapfile -t expected_ids <<<"$expected_ids_record"
mapfile -t events <"$event_log"
mapfile -t capture_paths <"$path_log"
expected_count="${#expected_ids[@]}"
assert_eq "$expected_count" "${#capture_paths[@]}" \
"$description creates one capture directory per collector command"
assert_eq "$((expected_count * 4))" "${#events[@]}" \
"$description records complete capture lifecycle groups"
for (( event_index=0; event_index<expected_count; event_index++ )); do
capture_path="${capture_paths[event_index]}"
[[ "$capture_path" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ ]] || return 1
assert_eq "mktemp|${capture_path}" "${events[event_index * 4]}" \
"$description starts capture with the exact created path"
assert_eq "command|${expected_ids[event_index]}" "${events[event_index * 4 + 1]}" \
"$description preserves collector command order"
assert_eq "rm|${capture_path}" "${events[event_index * 4 + 2]}" \
"$description removes only the capture records"
assert_eq "rmdir|${capture_path}" "${events[event_index * 4 + 3]}" \
"$description removes the same capture directory"
[[ ! -e "$capture_path" && ! -L "$capture_path" ]] || return 1
done
task5a_assert_capture_ownership_complete \
"$ownership_log" "$event_log" "$path_log" "$description"
}
_k3slr_command() {
local created capture_directory command_id=rejected-command argument
for argument in "$@"; do
case "$argument" in
/usr/bin/cryptsetup|/usr/sbin/cryptsetup|cryptsetup)
printf 'cryptsetup-argument\n' >>"$cryptsetup_log"
;;
esac
done
case "${1-}" in
/usr/bin/mktemp)
(( $# == 4 )) && [[ "$2" == --directory && "$3" == --tmpdir=/tmp &&
"$4" == k3slr-capture.XXXXXXXX ]] || return 97
task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete || return 97
created="$("$@")" || return 1
[[ "$created" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ &&
-d "$created" && ! -L "$created" ]] || return 97
if ! task5a_capture_ledger_register \
"$ownership_log" "$event_log" "$created"; then
task5a_cleanup_capture_probe_path "$created" || return 1
return 97
fi
printf '%s\n' "$created" >>"$path_log"
printf '%s\n' "$created"
return 0
;;
/usr/bin/rm)
task5a_remove_owned_capture_records "$ownership_log" "$event_log" "$@"
return
;;
/usr/bin/rmdir)
task5a_remove_owned_capture_directory "$ownership_log" "$event_log" "$@"
return
;;
esac
case "${1-}" in
/usr/bin/readlink)
if task5a_argv_equals 4 \
/usr/bin/readlink -f -- "$K3SLR_RECOVERY_PARTITION_BY_ID" "$@"; then
command_id=readlink-recovery-partition
elif task5a_argv_equals 4 \
/usr/bin/readlink -f -- "$K3SLR_RECOVERY_DISK_BY_ID" "$@"; then
command_id=readlink-recovery-disk
elif task5a_argv_equals 4 \
/usr/bin/readlink -f -- "$K3SLR_K3S_PARTITION_BY_ID" "$@"; then
command_id=readlink-k3s-partition
elif task5a_argv_equals 4 \
/usr/bin/readlink -f -- "$K3SLR_K3S_DISK_BY_ID" "$@"; then
command_id=readlink-k3s-disk
else
:
fi
;;
/usr/bin/lsblk)
if task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output PKNAME -- /dev/sda3 "$@"; then
command_id=lsblk-recovery-partition-parent
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output MAJ:MIN -- /dev/sda3 "$@"; then
command_id=lsblk-recovery-partition-majmin
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output MODEL -- /dev/sda "$@"; then
command_id=lsblk-recovery-model
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output SERIAL -- /dev/sda "$@"; then
command_id=lsblk-recovery-serial
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output WWN -- /dev/sda "$@"; then
command_id=lsblk-recovery-wwn
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output MAJ:MIN -- /dev/sda "$@"; then
command_id=lsblk-recovery-disk-majmin
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output PKNAME -- /dev/sdb2 "$@"; then
command_id=lsblk-k3s-partition-parent
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output MAJ:MIN -- /dev/sdb2 "$@"; then
command_id=lsblk-k3s-partition-majmin
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output MODEL -- /dev/sdb "$@"; then
command_id=lsblk-k3s-model
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output SERIAL -- /dev/sdb "$@"; then
command_id=lsblk-k3s-serial
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output WWN -- /dev/sdb "$@"; then
command_id=lsblk-k3s-wwn
elif task5a_argv_equals 7 \
/usr/bin/lsblk --noheadings --paths --output MAJ:MIN -- /dev/sdb "$@"; then
command_id=lsblk-k3s-disk-majmin
elif task5a_argv_equals 5 \
/usr/bin/lsblk --noheadings --raw --output NAME,TYPE "$@"; then
command_id=lsblk-all-names-types
else
:
fi
;;
/usr/bin/sudo)
if task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/sbin/blkid --output value \
--match-tag UUID -- /dev/sda3 "$@"; then
command_id=blkid-recovery-uuid
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/sbin/blkid --output value \
--match-tag PARTUUID -- /dev/sda3 "$@"; then
command_id=blkid-recovery-partuuid
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/sbin/blkid --output value \
--match-tag TYPE -- /dev/sda3 "$@"; then
command_id=blkid-recovery-type
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/sbin/blkid --output value \
--match-tag UUID -- /dev/sdb2 "$@"; then
command_id=blkid-k3s-uuid
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/sbin/blkid --output value \
--match-tag PARTUUID -- /dev/sdb2 "$@"; then
command_id=blkid-k3s-partuuid
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/sbin/blkid --output value \
--match-tag TYPE -- /dev/sdb2 "$@"; then
command_id=blkid-k3s-type
elif task5a_argv_equals 7 \
/usr/bin/sudo --non-interactive -- /usr/sbin/smartctl -H -A /dev/sda "$@"; then
command_id=smartctl-recovery-health-attributes
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/bin/findmnt --noheadings --raw \
--output SOURCE,FSTYPE,OPTIONS,ID,MAJ:MIN --mountpoint \
/mnt/k3s-recovery-ssd "$@"; then
command_id=findmnt-outer-mountpoint
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/bin/findmnt --noheadings --raw \
--output SOURCE,FSTYPE,OPTIONS,ID,MAJ:MIN --mountpoint \
/srv/recovery/k3s "$@"; then
command_id=findmnt-inner-mountpoint
elif task5a_argv_equals 10 \
/usr/bin/sudo --non-interactive -- /usr/bin/findmnt --noheadings --raw \
--output TARGET --source /dev/sda3 "$@"; then
command_id=findmnt-recovery-source
elif task5a_argv_equals 9 \
/usr/bin/sudo --non-interactive -- /usr/sbin/losetup --list \
--noheadings --raw --output \
NAME,BACK-FILE,BACK-INO,BACK-MAJ:MIN,MAJ:MIN,OFFSET,SIZELIMIT "$@"; then
command_id=losetup-all
elif task5a_argv_equals 6 \
/usr/bin/sudo --non-interactive -- /usr/bin/ntfs-3g.probe \
--readwrite /dev/sda3 "$@"; then
command_id=ntfs-probe-readwrite
else
:
fi
;;
/usr/bin/stat)
if task5a_argv_equals 4 /usr/bin/stat '--format=%F|%u|%g|%a' -- \
/dev/mapper "$@"; then
command_id=stat-mapper-directory
fi
;;
/usr/bin/test)
if task5a_argv_equals 4 /usr/bin/test ! -L /dev/mapper "$@"; then
command_id=test-mapper-directory-not-symlink
elif task5a_argv_equals 4 \
/usr/bin/test ! -e /dev/mapper/k3s-recovery "$@"; then
command_id=test-main-mapping-absent
elif task5a_argv_equals 4 \
/usr/bin/test ! -L /dev/mapper/k3s-recovery "$@"; then
command_id=test-main-mapping-not-symlink
elif task5a_argv_equals 4 \
/usr/bin/test ! -e /dev/mapper/k3s-recovery-proof "$@"; then
command_id=test-proof-mapping-absent
elif task5a_argv_equals 4 \
/usr/bin/test ! -L /dev/mapper/k3s-recovery-proof "$@"; then
command_id=test-proof-mapping-not-symlink
else
:
fi
;;
*) : ;;
esac
printf '%s\n' "$command_id" >>"$log" || return 97
printf 'command|%s\n' "$command_id" >>"$event_log" || return 97
[[ "$command_id" != rejected-command ]] || return 97
case "$command_id" in
readlink-recovery-partition)
case "${system_case:-}" in
capture_stdout_no_lf) printf '/dev/sda3' ;;
capture_stderr) printf '/dev/sda3\n'; printf 'unexpected stderr\n' >&2 ;;
capture_status) printf '/dev/sda3\n'; return 7 ;;
*) printf '/dev/sda3\n' ;;
esac
;;
readlink-recovery-disk)
if [[ "${system_case:-}" == wrong_identity ]]; then printf '/dev/sdz\n'; else printf '/dev/sda\n'; fi
;;
readlink-k3s-partition) printf '/dev/sdb2\n' ;;
readlink-k3s-disk) printf '/dev/sdb\n' ;;
lsblk-recovery-partition-parent) printf '/dev/sda\n' ;;
lsblk-recovery-partition-majmin) printf '8:3\n' ;;
lsblk-recovery-model) printf '%s\n' "$K3SLR_RECOVERY_MODEL" ;;
lsblk-recovery-serial) printf '%s\n' "$K3SLR_RECOVERY_SERIAL" ;;
lsblk-recovery-wwn) printf '%s\n' "$K3SLR_RECOVERY_WWN" ;;
lsblk-recovery-disk-majmin) printf '8:0\n' ;;
lsblk-k3s-partition-parent) printf '/dev/sdb\n' ;;
lsblk-k3s-partition-majmin) printf '8:34\n' ;;
lsblk-k3s-model) printf '%s\n' "$K3SLR_K3S_MODEL" ;;
lsblk-k3s-serial) printf '%s\n' "$K3SLR_K3S_SERIAL" ;;
lsblk-k3s-wwn) printf '%s\n' "$K3SLR_K3S_WWN" ;;
lsblk-k3s-disk-majmin) printf '8:32\n' ;;
blkid-recovery-uuid) printf '%s\n' "$K3SLR_RECOVERY_FS_UUID" ;;
blkid-recovery-partuuid) printf '%s\n' "$K3SLR_RECOVERY_PARTUUID" ;;
blkid-recovery-type) printf 'ntfs\n' ;;
blkid-k3s-uuid) printf '%s\n' "$K3SLR_K3S_FS_UUID" ;;
blkid-k3s-partuuid) printf '%s\n' "$K3SLR_K3S_PARTUUID" ;;
blkid-k3s-type) printf 'ext4\n' ;;
smartctl-recovery-health-attributes)
if [[ "${system_case:-}" == smart ]]; then
printf 'SMART overall-health self-assessment test result: PASSED\n 5 Reallocated_Sector_Ct 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1\n197 Current_Pending_Sector 0\n198 Offline_Uncorrectable 0\n'
else
printf 'SMART overall-health self-assessment test result: PASSED\n 5 Reallocated_Sector_Ct 0\n197 Current_Pending_Sector 0\n198 Offline_Uncorrectable 0\n'
fi
;;
findmnt-outer-mountpoint)
if [[ "${system_case:-}" == mounted ]]; then printf '/dev/sda3 ntfs3 rw 1 8:3\n'; else return 1; fi
;;
findmnt-inner-mountpoint|findmnt-recovery-source) return 1 ;;
losetup-all)
if [[ "${system_case:-}" == loop ]]; then printf '/dev/loop7 /mnt/file 42 8:3 7:7 0 0\n'; fi
;;
stat-mapper-directory) printf 'directory|0|0|755\n' ;;
test-mapper-directory-not-symlink)
if [[ "${system_case:-}" == mapper_dir_symlink ]]; then return 1; fi
;;
test-main-mapping-absent|test-main-mapping-not-symlink|\
test-proof-mapping-absent|test-proof-mapping-not-symlink) ;;
lsblk-all-names-types)
if [[ "${system_case:-}" == mapping ]]; then printf 'sda disk\nk3s-recovery crypt\n'; else printf 'sda disk\nsda3 part\nsdb disk\nsdb2 part\n'; fi
;;
ntfs-probe-readwrite)
if [[ "${system_case:-}" == probe ]]; then return 1; fi
;;
esac
return 0
}
assert_succeeds task5a_unowned_capture_cleanup_is_rejected \
'pre-package system capture fake'
assert_succeeds task5a_capture_cleanup_phase_and_identity_is_enforced \
'pre-package system capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
assert_succeeds task5a_cross_ledger_coherence_is_enforced \
"$ownership_log" "$event_log" "$path_log" 'pre-package system capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
: >"$log"
system_case=''
assert_succeeds task5a_command_append_failure_is_rejected \
"$log" "$ownership_log" "$event_log" "$path_log" \
'pre-package fake command-log append failure' \
/usr/bin/readlink -f -- "$K3SLR_RECOVERY_PARTITION_BY_ID"
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
: >"$log"
assert_succeeds task5a_command_append_failure_is_rejected \
"$event_log" "$ownership_log" "$event_log" "$path_log" \
'pre-package fake command-event append failure' \
/usr/bin/readlink -f -- "$K3SLR_RECOVERY_PARTITION_BY_ID"
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
: >"$log"
_k3slr_command /fixture/unknown >/dev/null 2>&1 || unknown_rc=$?
assert_eq 97 "$unknown_rc" 'pre-package fake rejects unknown argv with rc 97'
_k3slr_command /usr/bin/readlink \
"-f -- ${K3SLR_RECOVERY_PARTITION_BY_ID}" >/dev/null 2>&1 || wrong_group_rc=$?
assert_eq 97 "$wrong_group_rc" \
'pre-package fake rejects same executable and flattened text with wrong argv grouping'
_k3slr_command /usr/bin/readlink -f -- \
"$K3SLR_RECOVERY_PARTITION_BY_ID" unexpected >/dev/null 2>&1 || extra_arg_rc=$?
assert_eq 97 "$extra_arg_rc" \
'pre-package fake rejects same executable with an extra argv element'
_k3slr_command /usr/bin/readlink -f -- >/dev/null 2>&1 || missing_arg_rc=$?
assert_eq 97 "$missing_arg_rc" \
'pre-package fake rejects same executable with a missing argv element'
: >"$log"
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
: >"$cryptsetup_log"
system_case=''
assert_succeeds _k3slr_collect_prepackage_snapshot snapshot
assert_succeeds _k3slr_validate_prepackage_snapshot "$snapshot"
expected="$(printf '%s\n' \
readlink-recovery-partition \
readlink-recovery-disk \
readlink-k3s-partition \
readlink-k3s-disk \
lsblk-recovery-partition-parent \
lsblk-recovery-partition-majmin \
lsblk-recovery-model \
lsblk-recovery-serial \
lsblk-recovery-wwn \
lsblk-recovery-disk-majmin \
lsblk-k3s-partition-parent \
lsblk-k3s-partition-majmin \
lsblk-k3s-model \
lsblk-k3s-serial \
lsblk-k3s-wwn \
lsblk-k3s-disk-majmin \
blkid-recovery-uuid \
blkid-recovery-partuuid \
blkid-recovery-type \
blkid-k3s-uuid \
blkid-k3s-partuuid \
blkid-k3s-type \
smartctl-recovery-health-attributes \
findmnt-outer-mountpoint \
findmnt-inner-mountpoint \
findmnt-recovery-source \
losetup-all \
stat-mapper-directory \
test-mapper-directory-not-symlink \
test-main-mapping-absent \
test-main-mapping-not-symlink \
test-proof-mapping-absent \
test-proof-mapping-not-symlink \
lsblk-all-names-types \
ntfs-probe-readwrite)"
assert_eq "$expected" "$(<"$log")" \
'pre-package collector uses the independently hard-coded canonical command order'
assert_succeeds task5a_assert_capture_lineage_and_cleanup "$expected" \
'successful pre-package composition'
[[ ! -s "$cryptsetup_log" ]] || return 1
for system_case in capture_stdout_no_lf capture_stderr capture_status \
wrong_identity smart mounted loop mapping mapper_dir_symlink probe; do
: >"$log"
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
: >"$cryptsetup_log"
assert_fails _k3slr_collect_prepackage_snapshot snapshot
assert_succeeds task5a_assert_capture_lineage_and_cleanup "$(<"$log")" \
"failing pre-package composition (${system_case})"
[[ ! -s "$cryptsetup_log" ]] || return 1
done
)
task5a_ntfsinfo_pipeline_contract() (
local hex expected_hex pipeline_case='' invalid_rc=0 expected_mawk
local event_log="${fixture_root}/task5a-ntfs-pipeline-events.log"
local path_log="${fixture_root}/task5a-ntfs-pipeline-paths.log"
local ownership_log="${fixture_root}/task5a-ntfs-pipeline-ownership.log"
local stage_log="${fixture_root}/task5a-ntfs-pipeline-stages.log"
local sudo_status_log="${fixture_root}/task5a-ntfs-pipeline-sudo-status.log"
local od_status_log="${fixture_root}/task5a-ntfs-pipeline-od-status.log"
local mawk_status_log="${fixture_root}/task5a-ntfs-pipeline-mawk-status.log"
task5a_ntfs_argv_equals() {
local expected_count="${1-}" argument_index
local -a compared_arguments=()
shift || return 1
[[ "$expected_count" =~ ^[1-9][0-9]*$ ]] || return 1
compared_arguments=("$@")
(( ${#compared_arguments[@]} == 2 * expected_count )) || return 1
for (( argument_index=0; argument_index<expected_count; argument_index++ )); do
[[ "${compared_arguments[argument_index]}" == \
"${compared_arguments[expected_count + argument_index]}" ]] || return 1
done
}
task5a_reset_ntfs_stage_logs() {
: >"$stage_log"
: >"$sudo_status_log"
: >"$od_status_log"
: >"$mawk_status_log"
}
task5a_assert_ntfs_stage_run() {
local description="$1" expected_sudo="$2" expected_od="$3" expected_mawk_status="$4"
local stage sudo_count=0 od_count=0 mawk_count=0
local -a stages=() sudo_statuses=() od_statuses=() mawk_statuses=()
mapfile -t stages <"$stage_log"
mapfile -t sudo_statuses <"$sudo_status_log"
mapfile -t od_statuses <"$od_status_log"
mapfile -t mawk_statuses <"$mawk_status_log"
for stage in "${stages[@]}"; do
case "$stage" in
sudo) sudo_count=$((sudo_count + 1)) ;;
od) od_count=$((od_count + 1)) ;;
mawk) mawk_count=$((mawk_count + 1)) ;;
*) return 1 ;;
esac
done
assert_eq 3 "${#stages[@]}" "$description runs exactly three pipeline stages"
assert_eq 1 "$sudo_count" "$description runs the producer exactly once"
assert_eq 1 "$od_count" "$description runs od exactly once"
assert_eq 1 "$mawk_count" "$description runs mawk exactly once"
assert_eq 1 "${#sudo_statuses[@]}" "$description records one producer status"
assert_eq 1 "${#od_statuses[@]}" "$description records one od status"
assert_eq 1 "${#mawk_statuses[@]}" "$description records one mawk status"
assert_eq "$expected_sudo" "${sudo_statuses[0]}" "$description preserves producer status"
assert_eq "$expected_od" "${od_statuses[0]}" "$description preserves od status"
assert_eq "$expected_mawk_status" "${mawk_statuses[0]}" \
"$description preserves mawk status"
}
task5a_assert_ntfs_capture_lineage() {
local expected_count="$1" description="$2"
local event_index capture_path
local -a events=() capture_paths=()
mapfile -t events <"$event_log"
mapfile -t capture_paths <"$path_log"
assert_eq "$expected_count" "${#capture_paths[@]}" \
"$description creates one capture directory per pipeline"
assert_eq "$((expected_count * 3))" "${#events[@]}" \
"$description records complete capture cleanup groups"
for (( event_index=0; event_index<expected_count; event_index++ )); do
capture_path="${capture_paths[event_index]}"
[[ "$capture_path" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ ]] || return 1
assert_eq "mktemp|${capture_path}" "${events[event_index * 3]}" \
"$description starts capture with its exact created path"
assert_eq "rm|${capture_path}" "${events[event_index * 3 + 1]}" \
"$description removes exactly stdout and stderr"
assert_eq "rmdir|${capture_path}" "${events[event_index * 3 + 2]}" \
"$description removes the same capture directory"
[[ ! -e "$capture_path" && ! -L "$capture_path" ]] || return 1
done
task5a_assert_capture_ownership_complete \
"$ownership_log" "$event_log" "$path_log" "$description"
}
task5a_fixture_ascii_hex() {
local input="$1" index character encoded
for (( index=0; index<${#input}; index++ )); do
character="${input:index:1}"
printf -v encoded '%02x' "'$character"
printf '%s' "$encoded"
done
}
expected_mawk=$'{\n for (i = 1; i <= NF; i++) {\n if ($i !~ /^[0-9a-f]{2}$/ || ++bytes > 65536) {\n bad = 1\n exit\n }\n hex = hex $i\n }\n}\nEND {\n if (bad || bytes == 0) exit 1\n print hex\n}'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
task5a_reset_ntfs_stage_logs
_k3slr_command() {
local created capture_directory command_rc=0
case "${1-}" in
/usr/bin/mktemp)
task5a_ntfs_argv_equals 4 /usr/bin/mktemp --directory --tmpdir=/tmp \
k3slr-capture.XXXXXXXX "$@" || return 97
task5a_capture_ledgers_require_coherent \
"$ownership_log" "$event_log" '' complete || return 97
created="$("$@")" || return 1
[[ "$created" =~ ^/tmp/k3slr-capture\.[A-Za-z0-9]{8}$ &&
-d "$created" && ! -L "$created" ]] || return 97
if ! task5a_capture_ledger_register \
"$ownership_log" "$event_log" "$created"; then
task5a_cleanup_capture_probe_path "$created" || return 1
return 97
fi
printf '%s\n' "$created" >>"$path_log"
printf '%s\n' "$created"
return 0
;;
/usr/bin/rm)
task5a_remove_owned_capture_records "$ownership_log" "$event_log" "$@"
return
;;
/usr/bin/rmdir)
task5a_remove_owned_capture_directory "$ownership_log" "$event_log" "$@"
return
;;
/usr/bin/sudo)
task5a_ntfs_argv_equals 7 /usr/bin/sudo --non-interactive -- \
/usr/bin/ntfsinfo --mft --notime /dev/sda3 "$@" || return 97
printf 'sudo\n' >>"$stage_log"
case "$pipeline_case" in
boundary_65536)
if /usr/bin/head --bytes=65536 /dev/zero; then command_rc=0; else command_rc=$?; fi
;;
boundary_65537)
if /usr/bin/head --bytes=65537 /dev/zero; then command_rc=0; else command_rc=$?; fi
;;
*)
printf 'Cluster Size: 4096\nVolume Size in Clusters: 1000000\nAllocated clusters 250000 (25.0%%)\nFree Clusters: 750000 (75.0%%)\n'
command_rc=0
;;
esac
if [[ "$pipeline_case" == producer_stderr ]]; then
printf 'unexpected stderr\n' >&2
elif [[ "$pipeline_case" == producer_failure ]]; then
command_rc=7
fi
printf '%s\n' "$command_rc" >>"$sudo_status_log"
return "$command_rc"
;;
/usr/bin/od)
task5a_ntfs_argv_equals 4 /usr/bin/od -An -v -tx1 "$@" || return 97
printf 'od\n' >>"$stage_log"
if "$@"; then command_rc=0; else command_rc=$?; fi
[[ "$pipeline_case" != od_failure || "$command_rc" != 0 ]] || command_rc=8
printf '%s\n' "$command_rc" >>"$od_status_log"
return "$command_rc"
;;
/usr/bin/mawk)
task5a_ntfs_argv_equals 2 /usr/bin/mawk "$expected_mawk" "$@" || return 97
printf 'mawk\n' >>"$stage_log"
if "$@"; then command_rc=0; else command_rc=$?; fi
[[ "$pipeline_case" != mawk_failure || "$command_rc" != 0 ]] || command_rc=9
printf '%s\n' "$command_rc" >>"$mawk_status_log"
return "$command_rc"
;;
*) return 97 ;;
esac
}
assert_succeeds task5a_unowned_capture_cleanup_is_rejected \
'ntfsinfo pipeline capture fake'
assert_succeeds task5a_capture_cleanup_phase_and_identity_is_enforced \
'ntfsinfo pipeline capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
assert_succeeds task5a_cross_ledger_coherence_is_enforced \
"$ownership_log" "$event_log" "$path_log" 'ntfsinfo pipeline capture fake'
: >"$event_log"
: >"$path_log"
: >"$ownership_log"
_k3slr_command /fixture/unknown >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects unknown argv with rc 97'
invalid_rc=0
_k3slr_command /usr/bin/sudo \
'--non-interactive -- /usr/bin/ntfsinfo --mft --notime /dev/sda3' \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" \
'ntfsinfo fake rejects grouped sudo arguments that flatten to the expected text'
invalid_rc=0
_k3slr_command /usr/bin/sudo --non-interactive -- /usr/bin/ntfsinfo --mft \
--notime /dev/sda3 unexpected >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects an extra sudo argv element'
invalid_rc=0
_k3slr_command /usr/bin/sudo --non-interactive -- /usr/bin/ntfsinfo --mft \
--notime >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects a missing sudo argv element'
invalid_rc=0
_k3slr_command /usr/bin/sudo --version >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects unknown same-sudo argv'
invalid_rc=0
_k3slr_command /usr/bin/od '-An -v -tx1' >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects grouped od argv'
invalid_rc=0
_k3slr_command /usr/bin/od -An -v -tx1 unexpected \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects an extra od argv element'
invalid_rc=0
_k3slr_command /usr/bin/od -An -v >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects a missing od argv element'
invalid_rc=0
_k3slr_command /usr/bin/od --version >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects unknown same-od argv'
invalid_rc=0
_k3slr_command /usr/bin/mawk "$expected_mawk" unexpected \
>/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects an extra mawk argv element'
invalid_rc=0
_k3slr_command /usr/bin/mawk >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects a missing mawk argv element'
invalid_rc=0
_k3slr_command /usr/bin/mawk '{ print }' >/dev/null 2>&1 || invalid_rc=$?
assert_eq 97 "$invalid_rc" 'ntfsinfo fake rejects unknown same-mawk argv'
[[ ! -s "$event_log" && ! -s "$path_log" && ! -s "$stage_log" ]] || return 1
pipeline_case=''
assert_succeeds _k3slr_capture_ntfsinfo_hex hex /dev/sda3
expected_hex="$(task5a_fixture_ascii_hex $'Cluster Size: 4096\nVolume Size in Clusters: 1000000\nAllocated clusters 250000 (25.0%)\nFree Clusters: 750000 (75.0%)\n')"$'\n'
assert_eq "$expected_hex" "$hex" 'ntfsinfo pipeline returns only normalized bounded hex'
assert_succeeds task5a_assert_ntfs_stage_run 'successful ntfsinfo pipeline' 0 0 0
for pipeline_case in producer_failure od_failure mawk_failure producer_stderr; do
task5a_reset_ntfs_stage_logs
assert_fails _k3slr_capture_ntfsinfo_hex hex /dev/sda3
case "$pipeline_case" in
producer_failure)
assert_succeeds task5a_assert_ntfs_stage_run \
'producer-only failure pipeline' 7 0 0
;;
od_failure)
assert_succeeds task5a_assert_ntfs_stage_run \
'od-only failure pipeline' 0 8 0
;;
mawk_failure)
assert_succeeds task5a_assert_ntfs_stage_run \
'mawk-only failure pipeline' 0 0 9
;;
producer_stderr)
assert_succeeds task5a_assert_ntfs_stage_run \
'stderr-rejection pipeline' 0 0 0
;;
esac
done
pipeline_case=boundary_65536
task5a_reset_ntfs_stage_logs
assert_succeeds _k3slr_capture_ntfsinfo_hex hex /dev/sda3
assert_eq 131073 "${#hex}" 'ntfsinfo pipeline accepts exactly 65,536 bytes plus output LF'
[[ "$hex" =~ ^0+$'\n'$ ]] || return 1
assert_succeeds task5a_assert_ntfs_stage_run '65,536-byte pipeline boundary' 0 0 0
pipeline_case=boundary_65537
task5a_reset_ntfs_stage_logs
assert_fails _k3slr_capture_ntfsinfo_hex hex /dev/sda3
assert_succeeds task5a_assert_ntfs_stage_run '65,537-byte pipeline rejection' 0 0 1
assert_succeeds task5a_assert_ntfs_capture_lineage 7 \
'success, status, stderr, and byte-boundary pipelines'
)
task5a_dry_run_and_prepackage_action_binding() (
local mode wrapper main_name log output action_rc=0 probe_rc=0 record=''
local command_calls=0
for mode in prepare open close; do
case "$mode" in
prepare) wrapper="$PREPARE_WRAPPER_PATH" ;;
open) wrapper="$OPEN_WRAPPER_PATH" ;;
close) wrapper="$CLOSE_WRAPPER_PATH" ;;
esac
main_name="_k3slr_${mode}_main"
log="${fixture_root}/task5a-${mode}-dry-run.log"
: >"$log"
# shellcheck source=/dev/null
source "$wrapper"
_k3slr_load_contract() {
printf 'contract\n' >>"$log"
}
_k3slr_prepackage_device_preflight() {
printf 'prepackage\n' >>"$log"
}
_k3slr_require_execute_tty() {
printf 'unexpected-tty\n' >>"$log"
return 97
}
_k3slr_command() {
(( command_calls += 1 ))
printf 'unexpected-command:%s\n' "$*" >>"$log"
return 97
}
output="$("$main_name")" || return 1
[[ "$output" == "DRY RUN: ${mode} lifecycle; no changes made." ]] || return 1
assert_eq $'contract\nprepackage' "$(<"$log")" \
"$mode dry-run uses only the non-interactive pre-package evidence seam"
: >"$log"
_k3slr_prepackage_device_preflight() {
printf 'prepackage\n' >>"$log"
return 1
}
assert_fails "$main_name"
assert_eq $'contract\nprepackage' "$(<"$log")" \
"$mode dry-run fails closed when cached read-only evidence is unavailable"
done
log="${fixture_root}/task5a-device-validator-binding.log"
: >"$log"
_k3slr_prepackage_device_preflight() {
printf 'prepackage\n' >>"$log"
}
_k3slr_command() {
(( command_calls += 1 ))
return 97
}
task5b_legacy_binding_probe_payload() {
local binding_case="${1-}" action_rc=0 command_calls=0
(( $# == 1 )) || return 1
case "$binding_case" in
prepare-device)
_k3slr_lifecycle_action prepare device-validator \
_k3slr_prepare_invocation || action_rc=$?
;;
prepare-sudo)
_k3slr_lifecycle_action prepare sudo _k3slr_prepare_invocation ||
action_rc=$?
;;
open-device)
_k3slr_lifecycle_action open 'device-validator(--expect-device-ready)' ||
action_rc=$?
;;
*) return 1 ;;
esac
task5b_production_probe_emit_ok legacy_binding "$action_rc" "$command_calls"
}
task5b_production_probe_adversary_matrix || return 1
task5b_production_probe_static_graph_is_closed legacy_binding || return 1
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm legacy_binding || return 1
task5b_legacy_binding_probe_payload prepare-device
)" || probe_rc=$?
assert_eq 0 "$probe_rc" 'prepare device-validator binding probe completes safely'
assert_eq 'TASK5B_PROBE_OK|legacy_binding|1|0' "$record" \
'prepare device-validator binding is fail-closed before commands'
assert_eq '' "$(<"$log")" \
'prepare action integration remains fail-closed until its later checkpoint'
record=''
probe_rc=0
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm legacy_binding || return 1
task5b_legacy_binding_probe_payload prepare-sudo
)" || probe_rc=$?
assert_eq 0 "$probe_rc" 'prepare sudo binding probe completes safely'
assert_eq 'TASK5B_PROBE_OK|legacy_binding|97|1' "$record" \
'prepare sudo binding reaches only the modeled command failure'
record=''
probe_rc=0
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm legacy_binding || return 1
task5b_legacy_binding_probe_payload open-device
)" || probe_rc=$?
assert_eq 0 "$probe_rc" 'open device-validator binding probe completes safely'
assert_eq 'TASK5B_PROBE_OK|legacy_binding|1|0' "$record" \
'open device-validator binding is fail-closed before commands'
)
# The production break this catches is a Task 5B fake command boundary that
# accepts a hazardous near-match or reaches a real executable. Expected argv
# below are independent test literals; none are derived from production arrays.
task5b_gate0_route_equals() {
local route_id="${1-}" argument_index
local -a actual_arguments=() expected_arguments=()
shift || return 1
actual_arguments=("$@")
case "$route_id" in
sudo-validate)
expected_arguments=(/usr/bin/sudo --validate)
;;
sudo-cached-validate)
expected_arguments=(/usr/bin/sudo --non-interactive --validate)
;;
apt-update)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/apt-get update)
;;
apt-install)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/apt-get install --yes
keepassxc=2.7.6+dfsg.1-1build3 cryptsetup-bin=2:2.7.0-1ubuntu4.2)
;;
feasibility-execute)
expected_arguments=(/usr/bin/bash
/home/donghyeon/workspace/platform/scripts/validate/k3s-local-recovery-feasibility.sh
--execute)
;;
outer-scaffold-mkdir)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/mkdir
--mode=0700 -- /mnt/k3s-recovery-ssd)
;;
outer-mount)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/mount -t ntfs3
-o rw,uid=1000,gid=1000,dmask=0077,fmask=0177,nodev,nosuid,noexec
/dev/disk/by-id/wwn-0x500a0751e6aa6254-part3 /mnt/k3s-recovery-ssd)
;;
layout-root-mkdir)
expected_arguments=(/usr/bin/mkdir --mode=0700 --
/mnt/k3s-recovery-ssd/HyeonworksRecovery)
;;
layout-vault-mkdir)
expected_arguments=(/usr/bin/mkdir --mode=0700 --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault)
;;
layout-backups-mkdir)
expected_arguments=(/usr/bin/mkdir --mode=0700 --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault/backups)
;;
layout-containers-mkdir)
expected_arguments=(/usr/bin/mkdir --mode=0700 --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers)
;;
probe-mktemp)
expected_arguments=(/usr/bin/mktemp
--tmpdir=/mnt/k3s-recovery-ssd/HyeonworksRecovery
.k3slr-hardlink-probe.XXXXXXXX)
;;
probe-link)
expected_arguments=(/usr/bin/ln --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1.link)
;;
probe-unlink-link)
expected_arguments=(/usr/bin/unlink --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1.link)
;;
probe-unlink-source)
expected_arguments=(/usr/bin/unlink --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1)
;;
probe-sync)
expected_arguments=(/usr/bin/sync --file-system
/mnt/k3s-recovery-ssd/HyeonworksRecovery)
;;
keepass-db-create)
expected_arguments=(/usr/bin/keepassxc-cli db-create --quiet --set-password
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault/hyeonworks-recovery.kdbx)
;;
keepass-add)
expected_arguments=(/usr/bin/keepassxc-cli add --quiet --generate --length 40
--lower --upper --numeric --every-group
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault/hyeonworks-recovery.kdbx
'K3s Recovery LUKS')
;;
container-dd)
expected_arguments=(/usr/bin/dd if=/dev/zero
of=/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks
bs=16M count=2048 conv=excl,fsync status=progress)
;;
outer-umount)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/umount --
/mnt/k3s-recovery-ssd)
;;
loop-create)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/sbin/losetup
--find --show --nooverlap --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks)
;;
luks-format)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/sbin/cryptsetup
luksFormat --batch-mode --type luks2 --key-file=- /dev/loop37)
;;
luks-open)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/sbin/cryptsetup
open --type luks2 --key-file=- /dev/loop37 k3s-recovery)
;;
mkfs-ext4)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/sbin/mkfs.ext4
-F -L K3S_RECOVERY /dev/mapper/k3s-recovery)
;;
inner-parent-mkdir)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/mkdir
--mode=0700 -- /srv/recovery)
;;
inner-mountpoint-mkdir)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/mkdir
--mode=0700 -- /srv/recovery/k3s)
;;
inner-mount)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/mount -t ext4
-o rw,nodev,nosuid,noexec /dev/mapper/k3s-recovery /srv/recovery/k3s)
;;
inner-chown)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/chown
root:root /srv/recovery/k3s)
;;
inner-chmod)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/chmod
0700 /srv/recovery/k3s)
;;
marker-mktemp)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/mktemp
--tmpdir=/srv/recovery/k3s .k3slr-marker.tmp.XXXXXXXX)
;;
marker-dd)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/dd
of=/srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1 bs=37 count=1
iflag=fullblock conv=notrunc,fsync status=none)
;;
outer-metadata-mktemp)
expected_arguments=(/usr/bin/mktemp
--tmpdir=/mnt/k3s-recovery-ssd/HyeonworksRecovery
.k3slr-volume.tmp.XXXXXXXX)
;;
inner-metadata-mktemp)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/mktemp
--tmpdir=/srv/recovery/k3s .k3slr-volume.tmp.XXXXXXXX)
;;
outer-metadata-dd)
expected_arguments=(/usr/bin/dd
of=/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1
bs=4096 count=1 iflag=fullblock conv=notrunc,fsync status=none)
;;
inner-metadata-dd)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/dd
if=/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1
of=/srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1 bs=4096 count=1
iflag=fullblock conv=notrunc,fsync status=none)
;;
marker-stage-sync)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/sync
--file-system /srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1)
;;
marker-publish)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/ln --
/srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1
/srv/recovery/k3s/.recovery-marker)
;;
outer-stage-sync)
expected_arguments=(/usr/bin/sync --file-system
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1)
;;
inner-stage-sync)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/sync
--file-system /srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1)
;;
outer-metadata-publish)
expected_arguments=(/usr/bin/ln --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1
/mnt/k3s-recovery-ssd/HyeonworksRecovery/volume.env)
;;
inner-metadata-publish)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/ln --
/srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1
/srv/recovery/k3s/.recovery-volume.env)
;;
marker-stage-unlink)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/unlink --
/srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1)
;;
outer-stage-unlink)
expected_arguments=(/usr/bin/unlink --
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1)
;;
inner-stage-unlink)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/unlink --
/srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1)
;;
inner-parent-sync)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/sync
--file-system /srv/recovery/k3s)
;;
inner-umount)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/bin/umount --
/srv/recovery/k3s)
;;
mapping-close)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/sbin/cryptsetup
close k3s-recovery)
;;
loop-detach)
expected_arguments=(/usr/bin/sudo --non-interactive -- /usr/sbin/losetup
--detach /dev/loop37)
;;
*) return 1 ;;
esac
(( ${#actual_arguments[@]} == ${#expected_arguments[@]} )) || return 1
for (( argument_index=0; argument_index<${#expected_arguments[@]}; argument_index++ )); do
[[ "${actual_arguments[argument_index]}" == "${expected_arguments[argument_index]}" ]] ||
return 1
done
}
task5b_gate0_fake_command() {
local route_id
local -a route_ids=(
sudo-validate sudo-cached-validate apt-update apt-install feasibility-execute
outer-scaffold-mkdir outer-mount layout-root-mkdir layout-vault-mkdir
layout-backups-mkdir layout-containers-mkdir probe-mktemp probe-link
probe-unlink-link probe-unlink-source probe-sync keepass-db-create keepass-add
container-dd outer-umount loop-create luks-format luks-open mkfs-ext4
inner-parent-mkdir inner-mountpoint-mkdir inner-mount inner-chown inner-chmod
marker-mktemp marker-dd outer-metadata-mktemp inner-metadata-mktemp
outer-metadata-dd inner-metadata-dd marker-stage-sync marker-publish
outer-stage-sync inner-stage-sync outer-metadata-publish inner-metadata-publish
marker-stage-unlink outer-stage-unlink inner-stage-unlink inner-parent-sync
inner-umount mapping-close loop-detach
)
for route_id in "${route_ids[@]}"; do
if task5b_gate0_route_equals "$route_id" "$@"; then
(( TASK5B_GATE0_MODEL_DISPATCH_COUNT += 1 ))
(( TASK5B_GATE0_MODEL_EFFECT_COUNT += 1 ))
TASK5B_GATE0_LAST_ROUTE="$route_id"
TASK5B_GATE0_LAST_OUTPUT="modeled:${route_id}"
return 0
fi
done
return 97
}
readonly -a TASK5B_GATE0_ACTIVE_GRAPH_FUNCTIONS=(
_k3slr_command assert_eq fail task5b_gate0_active_matrix
task5b_gate0_assert_rejected task5b_gate0_assert_route
task5b_gate0_fake_command task5b_gate0_reset_route_record
task5b_gate0_route_equals task5b_gate0_assignment_only_status
task5b_gate0_real_exec_fuse
)
task5b_gate0_assignment_only_status() {
local candidate="${1-}" lhs rhs character next_character quote='' mode=scalar
local index=0 length depth=0 escaped=0 word_ended=0 append_assignment=0
(( $# == 1 )) || return 1
candidate="${candidate#"${candidate%%[![:space:]]*}"}"
candidate="${candidate%"${candidate##*[![:space:]]}"}"
[[ "$candidate" == *=* ]] || return 1
lhs="${candidate%%=*}"
rhs="${candidate#*=}"
if [[ "$lhs" == *+ ]]; then
lhs="${lhs%+}"
append_assignment=1
fi
[[ "$lhs" =~ ^[a-zA-Z_][a-zA-Z0-9_]*(\[([a-zA-Z0-9_]+|[a-zA-Z0-9_]+[[:space:]]*[-+][[:space:]]*[0-9]+)\])?$ ]] ||
return 1
length="${#rhs}"
(( length > 0 )) || return 0
(( append_assignment == 0 )) || [[ "${rhs:0:1}" == '(' ]] || return 1
if [[ "${rhs:0:1}" == '(' ]]; then
mode=compound
depth=1
index=1
fi
while (( index < length )); do
character="${rhs:index:1}"
next_character=''
(( index + 1 >= length )) || next_character="${rhs:index + 1:1}"
if (( escaped == 1 )); then
escaped=0
(( index += 1 ))
continue
fi
case "$quote" in
single)
[[ "$character" != "'" ]] || quote=''
(( index += 1 ))
continue
;;
double)
case "$character" in
'\\') escaped=1 ;;
'"') quote='' ;;
'`') return 1 ;;
'$')
[[ "$next_character" != '(' ]] || return 1
;;
esac
(( index += 1 ))
continue
;;
esac
if (( depth > 0 )) && [[ "$mode" == arithmetic ]]; then
case "$character" in
'`') return 1 ;;
'$')
[[ "$next_character" != '(' ]] || return 1
;;
'(') (( depth += 1 )) ;;
')') (( depth -= 1 )) ;;
esac
(( index += 1 ))
continue
fi
case "$character" in
"'") quote=single ;;
'"') quote=double ;;
'\\') escaped=1 ;;
'`'|';'|'&'|'|'|'<'|'>') return 1 ;;
'$')
if [[ "${rhs:index:3}" == '$((' ]]; then
mode=arithmetic
depth=2
(( index += 3 ))
continue
fi
[[ "$next_character" != '(' ]] || return 1
;;
'(')
[[ "$mode" == compound ]] || return 1
(( depth += 1 ))
;;
')')
[[ "$mode" == compound ]] || return 1
(( depth -= 1 ))
(( depth >= 0 )) || return 1
if (( depth == 0 )); then
(( index += 1 ))
while (( index < length )); do
[[ "${rhs:index:1}" =~ [[:space:]] ]] || return 1
(( index += 1 ))
done
return 0
fi
;;
$'\n'|$'\r')
[[ "$mode" == compound ]] || return 1
;;
[[:space:]])
if [[ "$mode" != compound ]]; then
word_ended=1
fi
;;
*)
(( word_ended == 0 )) || return 1
;;
esac
(( index += 1 ))
done
[[ -z "$quote" && "$escaped" == 0 ]] || return 1
if [[ "$mode" == compound && "$depth" -gt 0 ]]; then
return 2
fi
(( depth == 0 ))
}
task5b_gate0_real_exec_fuse() {
local candidate="${BASH_COMMAND-}" execution_word='' assignment_rc=0
candidate="${candidate#"${candidate%%[![:space:]]*}"}"
while [[ "$candidate" =~ ^(if|elif|while|until|then|\!|time)[[:space:]]+(.+)$ ]]; do
candidate="${BASH_REMATCH[2]}"
done
case "$candidate" in
''|'{'|'}'|'[['*|'(('*|for\ *|case\ *|do|done|esac) return 0 ;;
esac
if [[ "$candidate" =~ ^[a-zA-Z_][a-zA-Z0-9_]*(\[[^]]+\])?\+?= ]]; then
task5b_gate0_assignment_only_status "$candidate" || assignment_rc=$?
(( assignment_rc == 0 )) && return 0
(( TASK5B_GATE0_ACTUAL_EXEC_COUNT += 1 ))
if [[ "${TASK5B_GATE0_FUSE_PROBE_SILENT:-0}" != 1 ]]; then
printf 'Task 5B Gate 0 blocked assignment-prefixed execution\n' >&9
fi
exit 96
fi
read -r execution_word _ <<<"$candidate"
execution_word="${execution_word%;}"
case "$execution_word" in
\$*|\"\$*|\'\$*|/*|\"/*|\'/*|command|builtin|exec|eval|source|.)
(( TASK5B_GATE0_ACTUAL_EXEC_COUNT += 1 ))
if [[ "${TASK5B_GATE0_FUSE_PROBE_SILENT:-0}" != 1 ]]; then
printf 'Task 5B Gate 0 blocked dynamic/external execution word: %s\n' \
"$execution_word" >&9
fi
exit 96
;;
esac
case "$execution_word" in
_k3slr_command|assert_eq|fail|task5b_gate0_active_matrix|\
task5b_gate0_assert_rejected|task5b_gate0_assert_route|\
task5b_gate0_fake_command|task5b_gate0_reset_route_record|\
task5b_gate0_route_equals|exit|local|shift|return|printf|read|:)
return 0
;;
esac
(( TASK5B_GATE0_ACTUAL_EXEC_COUNT += 1 ))
if [[ "${TASK5B_GATE0_FUSE_PROBE_SILENT:-0}" != 1 ]]; then
printf 'Task 5B Gate 0 blocked non-builtin execution word: %s\n' \
"$execution_word" >&9
fi
exit 96
}
task5b_gate0_adversary_direct() {
/fixture/task5b-unlisted-direct
}
task5b_gate0_adversary_indented() {
"$@"
}
task5b_gate0_adversary_control_word() {
if command "$@"; then
:
fi
}
task5b_gate0_adversary_array() {
local -a task5b_runner=(/fixture/task5b-unlisted-array)
"${task5b_runner[@]}"
}
task5b_gate0_adversary_variable() {
local task5b_runner=/fixture/task5b-unlisted-variable
"$task5b_runner"
}
task5b_gate0_adversary_shell_c() {
/fixture/unlisted/bash -c ':'
}
task5b_gate0_adversary_helper_root() {
task5b_gate0_adversary_helper_leaf
}
task5b_gate0_adversary_helper_leaf() {
/fixture/task5b-unlisted-helper
}
task5b_gate0_adversary_trap_disable() {
trap - DEBUG
set +T
shopt -u extdebug
}
task5b_gate0_adversary_assignment_bare() {
TASK5B_ASSIGNMENT_PREFIX= trap - DEBUG
}
task5b_gate0_adversary_assignment_simple() {
TASK5B_ASSIGNMENT_PREFIX=1 /usr/bin/printf TASK5B_ASSIGNMENT_PREFIX_CANARY
}
task5b_gate0_adversary_assignment_quoted() {
TASK5B_ASSIGNMENT_PREFIX="a b" trap - DEBUG
}
task5b_gate0_adversary_assignment_indexed() {
TASK5B_ASSIGNMENT_PREFIX[0]=1 trap - DEBUG
}
task5b_gate0_static_safe_sample() {
local value=''
task5b_gate0_route_equals sudo-validate /usr/bin/sudo --validate || return 1
value=modeled
[[ "$value" == modeled ]]
}
task5b_gate0_static_assignment_prefixes_are_closed() {
local body="${1-}" line candidate='' compound_candidate=''
local assignment_rc=0
(( $# == 1 )) || return 1
while IFS= read -r line; do
candidate="${line#"${line%%[![:space:]]*}"}"
candidate="${candidate%"${candidate##*[![:space:]]}"}"
while [[ "$candidate" =~ ^(if|elif|while|until|then|\!|time)[[:space:]]+(.+)$ ]]; do
candidate="${BASH_REMATCH[2]}"
done
[[ "$candidate" != *';' ]] || candidate="${candidate%;}"
if [[ -n "$compound_candidate" ]]; then
compound_candidate+=$'\n'"$candidate"
assignment_rc=0
task5b_gate0_assignment_only_status "$compound_candidate" ||
assignment_rc=$?
case "$assignment_rc" in
0) compound_candidate='' ;;
2) ;;
*) return 1 ;;
esac
continue
fi
[[ "$candidate" =~ ^[a-zA-Z_][a-zA-Z0-9_]*(\[[^]]+\])?\+?= ]] ||
continue
assignment_rc=0
task5b_gate0_assignment_only_status "$candidate" || assignment_rc=$?
case "$assignment_rc" in
0) ;;
2) compound_candidate="$candidate" ;;
*) return 1 ;;
esac
done <<<"$body"
if [[ -n "$compound_candidate" ]]; then
return 1
fi
}
task5b_gate0_static_execution_positions_are_closed() {
local body="${1-}"
(( $# == 1 )) || return 1
[[ -n "$body" ]] || return 1
if /usr/bin/grep -En \
'^[[:space:]]*((if|elif|while|until|then|!|time)[[:space:]]+)*("?/?[^[:space:]]*/(ba|z|k|da)?sh|bash|sh)[[:space:]].*(-c|--command)([[:space:]]|$)|^[[:space:]]*((if|elif|while|until|then|!|time)[[:space:]]+)*("?\$|\$\{|/|"/|command([[:space:]]|$)|builtin[[:space:]]+command([[:space:]]|$)|exec([[:space:]]|$)|eval([[:space:]]|$)|source([[:space:]]|$)|\.[[:space:]])|\$\*|\$\{[^}]+\[\*\][^}]*\}|`|\$\(|^[[:space:]]*(trap([[:space:]]|$)|set[[:space:]]+\+T([[:space:]]|$)|shopt[[:space:]].*extdebug)' \
<<<"$body" >/dev/null; then
return 1
fi
}
task5b_gate0_static_body_is_closed() {
local body="${1-}"
(( $# == 1 )) || return 1
task5b_gate0_static_assignment_prefixes_are_closed "$body" || return 1
task5b_gate0_static_execution_positions_are_closed "$body"
}
task5b_gate0_definition_hash() {
local destination_name="${1-}" function_name="${2-}" body='' hash_record=''
(( $# == 2 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
body="$(declare -f "$function_name")" || return 1
hash_record="$(printf '%s\n' "$body" | /usr/bin/sha256sum)" || return 1
[[ "$hash_record" =~ ^([0-9a-f]{64})[[:space:]]+-$ ]] || return 1
printf -v "$destination_name" '%s' "${BASH_REMATCH[1]}"
}
task5b_gate0_normalize_static_body_data_lines() {
local body="${1-}" destination_name="${2-}" function_name="${3-}"
local line normalized='' previous_continues=0
(( $# == 3 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
while IFS= read -r line; do
if (( previous_continues == 1 )); then
normalized+=' task5b_static_argument '
elif [[ "$function_name" == task5b_gate0_route_equals &&
"$line" =~ ^[[:space:]]+/ ]]; then
normalized+=' task5b_static_array_value '
fi
normalized+="$line"$'\n'
previous_continues=0
[[ "$line" != *'\' ]] || previous_continues=1
done <<<"$body"
if [[ "$function_name" == fail ]]; then
normalized="${normalized//\"\$*\"/task5b_static_message}"
fi
normalized="${normalized//\$\(\(/task5b_static_arithmetic_(}"
printf -v "$destination_name" '%s' "$normalized"
}
task5b_gate0_fuse_body_is_closed() {
local body="${1-}" exact_allowlist
(( $# == 1 )) || return 1
[[ -n "$body" ]] || return 1
[[ "$body" != *'trap - DEBUG'* && "$body" != *'set +T'* &&
"$body" != *'shopt -u extdebug'* ]] || return 1
exact_allowlist='_k3slr_command | assert_eq | fail | task5b_gate0_active_matrix | task5b_gate0_assert_rejected | task5b_gate0_assert_route | task5b_gate0_fake_command | task5b_gate0_reset_route_record | task5b_gate0_route_equals | exit | local | shift | return | printf | read | :)'
[[ "$body" == *"$exact_allowlist"* ]] || return 1
[[ "$body" != *'task5b_gate0_dynamic_external_helper'* ]] || return 1
[[ "$body" != *'*=*)'* ]] || return 1
}
task5b_gate0_assignment_parser_body_is_closed() {
local body="${1-}"
(( $# == 1 )) || return 1
[[ -n "$body" ]] || return 1
if /usr/bin/grep -En \
'^[[:space:]]*(trap([[:space:]]|$)|set[[:space:]]+\+T([[:space:]]|$)|shopt[[:space:]].*extdebug|/|"/|command([[:space:]]|$)|builtin[[:space:]]+command([[:space:]]|$)|exec([[:space:]]|$)|eval([[:space:]]|$)|source([[:space:]]|$)|\.[[:space:]])' \
<<<"$body" >/dev/null; then
return 1
fi
}
task5b_gate0_active_graph_is_closed() {
local function_name body='' normalized_body=''
for function_name in "${TASK5B_GATE0_ACTIVE_GRAPH_FUNCTIONS[@]}"; do
body="$(declare -f "$function_name")" || return 1
if [[ "$function_name" == task5b_gate0_real_exec_fuse ]]; then
task5b_gate0_fuse_body_is_closed "$body" || {
printf 'Task 5B Gate 0 static fuse scan rejected: %s\n' \
"$function_name" >&2
return 1
}
continue
fi
if [[ "$function_name" == task5b_gate0_assignment_only_status ]]; then
task5b_gate0_assignment_parser_body_is_closed "$body" || {
printf 'Task 5B Gate 0 assignment parser scan rejected: %s\n' \
"$function_name" >&2
return 1
}
continue
fi
task5b_gate0_normalize_static_body_data_lines "$body" normalized_body \
"$function_name" || return 1
task5b_gate0_static_assignment_prefixes_are_closed "$body" || {
printf 'Task 5B Gate 0 assignment-prefix scan rejected: %s\n' \
"$function_name" >&2
return 1
}
task5b_gate0_static_execution_positions_are_closed "$normalized_body" || {
printf 'Task 5B Gate 0 active graph scan rejected: %s\n' \
"$function_name" >&2
return 1
}
done
}
task5b_gate0_active_graph_hash() {
local destination_name="${1-}" function_name body='' graph_record=''
(( $# == 1 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
for function_name in "${TASK5B_GATE0_ACTIVE_GRAPH_FUNCTIONS[@]}"; do
body="$(declare -f "$function_name")" || return 1
graph_record+="${function_name}"$'\n'"${body}"$'\n'
done
body="$(printf '%s' "$graph_record" | /usr/bin/sha256sum)" || return 1
[[ "$body" =~ ^([0-9a-f]{64})[[:space:]]+-$ ]] || return 1
printf -v "$destination_name" '%s' "${BASH_REMATCH[1]}"
}
task5b_gate0_static_adversarial_contract() {
local body=''
body="$(declare -f task5b_gate0_static_safe_sample)" || return 1
assert_succeeds task5b_gate0_static_body_is_closed "$body"
for adversary in task5b_gate0_adversary_direct task5b_gate0_adversary_indented \
task5b_gate0_adversary_control_word task5b_gate0_adversary_array \
task5b_gate0_adversary_variable task5b_gate0_adversary_shell_c \
task5b_gate0_adversary_trap_disable \
task5b_gate0_adversary_assignment_bare \
task5b_gate0_adversary_assignment_simple \
task5b_gate0_adversary_assignment_quoted \
task5b_gate0_adversary_assignment_indexed; do
body="$(declare -f "$adversary")" || return 1
assert_fails task5b_gate0_static_body_is_closed "$body"
done
body="$(declare -f task5b_gate0_adversary_helper_root)"$'\n'\
"$(declare -f task5b_gate0_adversary_helper_leaf)" || return 1
assert_fails task5b_gate0_static_body_is_closed "$body"
}
task5b_gate0_full_graph_static_adversarial_contract() (
local scan_rc=0
task5b_gate0_assert_rejected() {
TASK5B_ASSIGNMENT_PREFIX=1 trap - DEBUG
/usr/bin/printf 'TASK5B_FULL_GRAPH_SCAN_CANARY'
}
task5b_gate0_active_graph_is_closed 2>/dev/null || scan_rc=$?
assert_eq 1 "$scan_rc" \
'actual full-graph scanner rejects a mutated allowlisted helper'
)
task5b_gate0_dynamic_external_helper() {
/usr/bin/printf 'TASK5B_REAL_EXEC_CANARY'
}
task5b_gate0_dynamic_fuse_contract() {
local probe_rc=0 probe_output=''
probe_output="$(
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_FUSE_PROBE_SILENT=1
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
/usr/bin/true
)" || probe_rc=$?
assert_eq 96 "$probe_rc" 'generic fuse blocks an unlisted absolute executable'
assert_eq '' "$probe_output" 'unlisted absolute executable produces no output'
probe_rc=0
probe_output="$(
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_FUSE_PROBE_SILENT=1
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
task5b_gate0_dynamic_external_helper
)" || probe_rc=$?
assert_eq 96 "$probe_rc" 'generic fuse blocks helper-mediated external execution'
assert_eq '' "$probe_output" 'helper-mediated external canary never executes'
probe_rc=0
probe_output="$(
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_FUSE_PROBE_SILENT=1
task5b_gate0_assert_rejected() {
trap - DEBUG
/usr/bin/printf 'TASK5B_TRAP_DISABLE_CANARY'
}
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
task5b_gate0_assert_rejected
)" || probe_rc=$?
assert_eq 96 "$probe_rc" 'generic fuse blocks trap removal in an allowlisted helper'
assert_eq '' "$probe_output" 'trap-removal helper canary never executes'
probe_rc=0
probe_output="$(
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_FUSE_PROBE_SILENT=1
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
TASK5B_ASSIGNMENT_PREFIX= trap - DEBUG
/usr/bin/printf 'TASK5B_BARE_ASSIGNMENT_CANARY'
)" || probe_rc=$?
assert_eq 96 "$probe_rc" 'generic fuse blocks a bare assignment prefix'
assert_eq '' "$probe_output" 'bare assignment prefix leaves trap and canary closed'
probe_rc=0
probe_output="$(
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_FUSE_PROBE_SILENT=1
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
TASK5B_ASSIGNMENT_PREFIX=1 /usr/bin/printf 'TASK5B_SIMPLE_ASSIGNMENT_CANARY'
)" || probe_rc=$?
assert_eq 96 "$probe_rc" 'generic fuse blocks a simple assignment prefix'
assert_eq '' "$probe_output" 'simple assignment prefix external effect stays zero'
probe_rc=0
probe_output="$(
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_FUSE_PROBE_SILENT=1
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
TASK5B_ASSIGNMENT_PREFIX="a b" trap - DEBUG
/usr/bin/printf 'TASK5B_QUOTED_ASSIGNMENT_CANARY'
)" || probe_rc=$?
assert_eq 96 "$probe_rc" 'generic fuse blocks a quoted assignment prefix'
assert_eq '' "$probe_output" 'quoted assignment prefix leaves trap and canary closed'
probe_rc=0
probe_output="$(
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_FUSE_PROBE_SILENT=1
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
TASK5B_ASSIGNMENT_PREFIX[0]=1 trap - DEBUG
/usr/bin/printf 'TASK5B_INDEXED_ASSIGNMENT_CANARY'
)" || probe_rc=$?
assert_eq 96 "$probe_rc" 'generic fuse blocks an indexed assignment prefix'
assert_eq '' "$probe_output" 'indexed assignment prefix leaves trap and canary closed'
}
task5b_gate0_reset_route_record() {
TASK5B_GATE0_MODEL_DISPATCH_COUNT=0
TASK5B_GATE0_MODEL_EFFECT_COUNT=0
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_LAST_ROUTE=''
TASK5B_GATE0_LAST_OUTPUT=''
: >"$TASK5B_GATE0_STDOUT_PATH"
: >"$TASK5B_GATE0_STDERR_PATH"
}
task5b_gate0_assert_rejected() {
local description="$1" rejected_rc=0
shift
task5b_gate0_reset_route_record
_k3slr_command "$@" >"$TASK5B_GATE0_STDOUT_PATH" \
2>"$TASK5B_GATE0_STDERR_PATH" || rejected_rc=$?
assert_eq 97 "$rejected_rc" "$description returns exact rc 97"
assert_eq 0 "$TASK5B_GATE0_MODEL_DISPATCH_COUNT" "$description has model dispatch 0"
assert_eq 0 "$TASK5B_GATE0_MODEL_EFFECT_COUNT" "$description has model effect 0"
assert_eq 0 "$TASK5B_GATE0_ACTUAL_EXEC_COUNT" "$description has actual exec 0"
[[ ! -s "$TASK5B_GATE0_STDOUT_PATH" && ! -s "$TASK5B_GATE0_STDERR_PATH" ]] ||
fail "$description emitted stdout or stderr"
}
task5b_gate0_assert_route() {
local route_id="$1" route_rc=0 last_index swap
local -a exact_arguments=() near_arguments=()
shift
exact_arguments=("$@")
task5b_gate0_reset_route_record
_k3slr_command "${exact_arguments[@]}" >"$TASK5B_GATE0_STDOUT_PATH" \
2>"$TASK5B_GATE0_STDERR_PATH" || route_rc=$?
assert_eq 0 "$route_rc" "$route_id accepts its exact argv"
assert_eq 1 "$TASK5B_GATE0_MODEL_DISPATCH_COUNT" "$route_id dispatches exactly once"
assert_eq 1 "$TASK5B_GATE0_MODEL_EFFECT_COUNT" "$route_id records exactly one model effect"
assert_eq 0 "$TASK5B_GATE0_ACTUAL_EXEC_COUNT" "$route_id has actual exec 0"
assert_eq "$route_id" "$TASK5B_GATE0_LAST_ROUTE" "$route_id records its route"
assert_eq "modeled:${route_id}" "$TASK5B_GATE0_LAST_OUTPUT" "$route_id records output"
[[ ! -s "$TASK5B_GATE0_STDOUT_PATH" && ! -s "$TASK5B_GATE0_STDERR_PATH" ]] ||
fail "$route_id exact route emitted stdout or stderr"
near_arguments=("${exact_arguments[@]}" task5b-extra)
task5b_gate0_assert_rejected "$route_id extra-argument near-match" \
"${near_arguments[@]}"
last_index=$(( ${#exact_arguments[@]} - 1 ))
near_arguments=("${exact_arguments[@]:0:last_index}")
task5b_gate0_assert_rejected "$route_id missing-argument near-match" \
"${near_arguments[@]}"
near_arguments=("${exact_arguments[@]}")
near_arguments[last_index]=task5b-wrong-value
task5b_gate0_assert_rejected "$route_id wrong-value near-match" \
"${near_arguments[@]}"
if (( ${#exact_arguments[@]} >= 3 )); then
near_arguments=("${exact_arguments[@]}")
swap="${near_arguments[last_index - 1]}"
near_arguments[last_index - 1]="${near_arguments[last_index]}"
near_arguments[last_index]="$swap"
task5b_gate0_assert_rejected "$route_id wrong-order near-match" \
"${near_arguments[@]}"
near_arguments=("${exact_arguments[@]:0:last_index - 1}")
near_arguments+=("${exact_arguments[last_index - 1]} ${exact_arguments[last_index]}")
task5b_gate0_assert_rejected "$route_id wrong-grouping near-match" \
"${near_arguments[@]}"
fi
}
task5b_gate0_static_fake_scan() {
local fake_body route_body catchall_body fake_hash='' route_hash='' catchall_hash=''
local active_graph_hash=''
fake_body="$(declare -f task5b_gate0_fake_command)" || return 1
route_body="$(declare -f task5b_gate0_route_equals)" || return 1
catchall_body="$(declare -f _k3slr_command)" || return 1
task5b_gate0_static_body_is_closed "$fake_body" || return 1
task5b_gate0_static_body_is_closed "$catchall_body" || return 1
task5b_gate0_definition_hash fake_hash task5b_gate0_fake_command || return 1
task5b_gate0_definition_hash route_hash task5b_gate0_route_equals || return 1
task5b_gate0_definition_hash catchall_hash _k3slr_command || return 1
task5b_gate0_active_graph_is_closed || return 1
task5b_gate0_full_graph_static_adversarial_contract || return 1
task5b_gate0_active_graph_hash active_graph_hash || return 1
[[ "$fake_hash" == ef2cfc5af6fff12713b20f8734fd28114312d695a7df7d478f9bddd155398d09 ]] ||
return 1
[[ "$route_hash" == 53f6a8cdb6726626a1db2136d7c38e4f2c52c4eaaeb3671506fb504475586a19 ]] ||
return 1
[[ "$catchall_hash" == 76448cb3a82bf60c7904b7d6217762b2769c65069a5e54f086b960d5a63ca3ce ]] ||
return 1
[[ "$active_graph_hash" == 5b3cbe49958bfabe8556d2ec60cdfaf2fdcf586e41953e2681b1720399332293 ]] ||
return 1
[[ "$fake_body" == *'return 0'* && "$fake_body" == *'return 97'* ]] || return 1
[[ "$fake_body" != *'task5b_gate0_real_exec_fuse'* ]] || return 1
task5b_gate0_static_adversarial_contract
}
task5b_gate0_contract() (
local gate0_stdout="${fixture_root}/task5b-gate0.stdout"
local gate0_stderr="${fixture_root}/task5b-gate0.stderr"
local matrix_rc=0
# Foreground source-loaded fixture: production code is loaded, then only its
# lowest command seam is replaced. No child/background fixture is started.
# shellcheck source=/dev/null
source "$LIBRARY_PATH"
_k3slr_command() { task5b_gate0_fake_command "$@"; }
exec 9>&2
TASK5B_GATE0_STDOUT_PATH="$gate0_stdout"
TASK5B_GATE0_STDERR_PATH="$gate0_stderr"
TASK5B_GATE0_MODEL_DISPATCH_COUNT=0
TASK5B_GATE0_MODEL_EFFECT_COUNT=0
TASK5B_GATE0_ACTUAL_EXEC_COUNT=0
TASK5B_GATE0_LAST_ROUTE=''
TASK5B_GATE0_LAST_OUTPUT=''
: >"$TASK5B_GATE0_STDOUT_PATH"
: >"$TASK5B_GATE0_STDERR_PATH"
task5b_gate0_active_matrix() {
task5b_gate0_assert_route sudo-validate /usr/bin/sudo --validate
task5b_gate0_assert_route sudo-cached-validate \
/usr/bin/sudo --non-interactive --validate
task5b_gate0_assert_route apt-update \
/usr/bin/sudo --non-interactive -- /usr/bin/apt-get update
task5b_gate0_assert_route apt-install \
/usr/bin/sudo --non-interactive -- /usr/bin/apt-get install --yes \
keepassxc=2.7.6+dfsg.1-1build3 cryptsetup-bin=2:2.7.0-1ubuntu4.2
task5b_gate0_assert_route feasibility-execute /usr/bin/bash \
/home/donghyeon/workspace/platform/scripts/validate/k3s-local-recovery-feasibility.sh \
--execute
task5b_gate0_assert_route outer-scaffold-mkdir \
/usr/bin/sudo --non-interactive -- /usr/bin/mkdir --mode=0700 -- \
/mnt/k3s-recovery-ssd
task5b_gate0_assert_route outer-mount \
/usr/bin/sudo --non-interactive -- /usr/bin/mount -t ntfs3 -o \
rw,uid=1000,gid=1000,dmask=0077,fmask=0177,nodev,nosuid,noexec \
/dev/disk/by-id/wwn-0x500a0751e6aa6254-part3 /mnt/k3s-recovery-ssd
task5b_gate0_assert_route layout-root-mkdir /usr/bin/mkdir --mode=0700 -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery
task5b_gate0_assert_route layout-vault-mkdir /usr/bin/mkdir --mode=0700 -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault
task5b_gate0_assert_route layout-backups-mkdir /usr/bin/mkdir --mode=0700 -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault/backups
task5b_gate0_assert_route layout-containers-mkdir /usr/bin/mkdir --mode=0700 -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers
task5b_gate0_assert_route probe-mktemp /usr/bin/mktemp \
--tmpdir=/mnt/k3s-recovery-ssd/HyeonworksRecovery \
.k3slr-hardlink-probe.XXXXXXXX
task5b_gate0_assert_route probe-link /usr/bin/ln -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1 \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1.link
task5b_gate0_assert_route probe-unlink-link /usr/bin/unlink -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1.link
task5b_gate0_assert_route probe-unlink-source /usr/bin/unlink -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-hardlink-probe.FIXTURE1
task5b_gate0_assert_route probe-sync /usr/bin/sync --file-system \
/mnt/k3s-recovery-ssd/HyeonworksRecovery
task5b_gate0_assert_route keepass-db-create /usr/bin/keepassxc-cli db-create \
--quiet --set-password \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault/hyeonworks-recovery.kdbx
task5b_gate0_assert_route keepass-add /usr/bin/keepassxc-cli add --quiet \
--generate --length 40 --lower --upper --numeric --every-group \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/vault/hyeonworks-recovery.kdbx \
'K3s Recovery LUKS'
task5b_gate0_assert_route container-dd /usr/bin/dd if=/dev/zero \
of=/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks \
bs=16M count=2048 conv=excl,fsync status=progress
task5b_gate0_assert_route outer-umount /usr/bin/sudo --non-interactive -- \
/usr/bin/umount -- /mnt/k3s-recovery-ssd
task5b_gate0_assert_route loop-create /usr/bin/sudo --non-interactive -- \
/usr/sbin/losetup --find --show --nooverlap -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks
task5b_gate0_assert_route luks-format /usr/bin/sudo --non-interactive -- \
/usr/sbin/cryptsetup luksFormat --batch-mode --type luks2 --key-file=- \
/dev/loop37
task5b_gate0_assert_route luks-open /usr/bin/sudo --non-interactive -- \
/usr/sbin/cryptsetup open --type luks2 --key-file=- /dev/loop37 k3s-recovery
task5b_gate0_assert_route mkfs-ext4 /usr/bin/sudo --non-interactive -- \
/usr/sbin/mkfs.ext4 -F -L K3S_RECOVERY /dev/mapper/k3s-recovery
task5b_gate0_assert_route inner-parent-mkdir /usr/bin/sudo --non-interactive -- \
/usr/bin/mkdir --mode=0700 -- /srv/recovery
task5b_gate0_assert_route inner-mountpoint-mkdir \
/usr/bin/sudo --non-interactive -- /usr/bin/mkdir --mode=0700 -- \
/srv/recovery/k3s
task5b_gate0_assert_route inner-mount /usr/bin/sudo --non-interactive -- \
/usr/bin/mount -t ext4 -o rw,nodev,nosuid,noexec \
/dev/mapper/k3s-recovery /srv/recovery/k3s
task5b_gate0_assert_route inner-chown /usr/bin/sudo --non-interactive -- \
/usr/bin/chown root:root /srv/recovery/k3s
task5b_gate0_assert_route inner-chmod /usr/bin/sudo --non-interactive -- \
/usr/bin/chmod 0700 /srv/recovery/k3s
task5b_gate0_assert_route marker-mktemp /usr/bin/sudo --non-interactive -- \
/usr/bin/mktemp --tmpdir=/srv/recovery/k3s .k3slr-marker.tmp.XXXXXXXX
task5b_gate0_assert_route marker-dd /usr/bin/sudo --non-interactive -- \
/usr/bin/dd of=/srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1 bs=37 count=1 \
iflag=fullblock conv=notrunc,fsync status=none
task5b_gate0_assert_route outer-metadata-mktemp /usr/bin/mktemp \
--tmpdir=/mnt/k3s-recovery-ssd/HyeonworksRecovery \
.k3slr-volume.tmp.XXXXXXXX
task5b_gate0_assert_route inner-metadata-mktemp \
/usr/bin/sudo --non-interactive -- /usr/bin/mktemp \
--tmpdir=/srv/recovery/k3s .k3slr-volume.tmp.XXXXXXXX
task5b_gate0_assert_route outer-metadata-dd /usr/bin/dd \
of=/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1 \
bs=4096 count=1 iflag=fullblock conv=notrunc,fsync status=none
task5b_gate0_assert_route inner-metadata-dd \
/usr/bin/sudo --non-interactive -- /usr/bin/dd \
if=/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1 \
of=/srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1 bs=4096 count=1 \
iflag=fullblock conv=notrunc,fsync status=none
task5b_gate0_assert_route marker-stage-sync /usr/bin/sudo --non-interactive -- \
/usr/bin/sync --file-system /srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1
task5b_gate0_assert_route marker-publish /usr/bin/sudo --non-interactive -- \
/usr/bin/ln -- /srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1 \
/srv/recovery/k3s/.recovery-marker
task5b_gate0_assert_route outer-stage-sync /usr/bin/sync --file-system \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1
task5b_gate0_assert_route inner-stage-sync /usr/bin/sudo --non-interactive -- \
/usr/bin/sync --file-system /srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1
task5b_gate0_assert_route outer-metadata-publish /usr/bin/ln -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1 \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/volume.env
task5b_gate0_assert_route inner-metadata-publish \
/usr/bin/sudo --non-interactive -- /usr/bin/ln -- \
/srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1 \
/srv/recovery/k3s/.recovery-volume.env
task5b_gate0_assert_route marker-stage-unlink \
/usr/bin/sudo --non-interactive -- /usr/bin/unlink -- \
/srv/recovery/k3s/.k3slr-marker.tmp.FIXTURE1
task5b_gate0_assert_route outer-stage-unlink /usr/bin/unlink -- \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/.k3slr-volume.tmp.FIXTURE1
task5b_gate0_assert_route inner-stage-unlink \
/usr/bin/sudo --non-interactive -- /usr/bin/unlink -- \
/srv/recovery/k3s/.k3slr-volume.tmp.FIXTURE1
task5b_gate0_assert_route inner-parent-sync /usr/bin/sudo --non-interactive -- \
/usr/bin/sync --file-system /srv/recovery/k3s
task5b_gate0_assert_route inner-umount /usr/bin/sudo --non-interactive -- \
/usr/bin/umount -- /srv/recovery/k3s
task5b_gate0_assert_route mapping-close /usr/bin/sudo --non-interactive -- \
/usr/sbin/cryptsetup close k3s-recovery
task5b_gate0_assert_route loop-detach /usr/bin/sudo --non-interactive -- \
/usr/sbin/losetup --detach /dev/loop37
task5b_gate0_assert_rejected 'unknown executable' \
/fixture/task5b-unknown --execute
}
# This scan must pass before the first exact hazardous argv is presented to
# the fake. It is independent of every dynamic route assertion below.
task5b_gate0_static_fake_scan || return 1
task5b_gate0_dynamic_fuse_contract || return 1
# The active DEBUG trap exists only inside this foreground nested subshell.
# Returning from the subshell removes it without an allowlisted teardown word.
(
shopt -s extdebug
set -T
trap 'task5b_gate0_real_exec_fuse' DEBUG
task5b_gate0_active_matrix
) || matrix_rc=$?
(( matrix_rc == 0 )) || return "$matrix_rc"
exec 9>&-
/usr/bin/rm -- "$TASK5B_GATE0_STDOUT_PATH" "$TASK5B_GATE0_STDERR_PATH"
)
# Production lifecycle probes need a second boundary beyond their command
# seam doubles: if production regresses to a direct executable, the DEBUG trap
# must reject that simple command before Bash executes it. Runtime membership
# and the pre-trap static scan share this exact, profile-scoped function set.
readonly -A TASK5B_PRODUCTION_PROBE_ALLOWED_FUNCTIONS=(
[unavailable/_k3slr_lifecycle_action]=1
[unavailable/_k3slr_command]=1
[unavailable/task5b_unavailable_probe_payload]=1
[unavailable/task5b_production_probe_emit_ok]=1
[legacy_binding/_k3slr_lifecycle_action]=1
[legacy_binding/_k3slr_command]=1
[legacy_binding/task5b_legacy_binding_probe_payload]=1
[legacy_binding/task5b_production_probe_emit_ok]=1
[api_dispatch/_k3slr_lifecycle_dispatch]=1
[api_dispatch/_k3slr_lifecycle_step]=1
[api_dispatch/_k3slr_lifecycle_action]=1
[api_dispatch/_k3slr_command]=1
[api_dispatch/task5b_api_dispatch_probe_payload]=1
[api_dispatch/task5b_production_probe_emit_ok]=1
[api_direct/_k3slr_lifecycle_action]=1
[api_direct/_k3slr_command]=1
[api_direct/task5b_api_direct_probe_payload]=1
[api_direct/task5b_production_probe_emit_ok]=1
[api_main_arity/_k3slr_lifecycle_main]=1
[api_main_arity/task5b_api_main_arity_probe_payload]=1
[api_main_arity/task5b_production_probe_emit_ok]=1
[action1_main/_k3slr_lifecycle_main]=1
[action1_main/_k3slr_load_contract]=1
[action1_main/_k3slr_parse_contract]=1
[action1_main/_k3slr_contract_value_is_valid]=1
[action1_main/_k3slr_uint]=1
[action1_main/_k3slr_require_execute_tty]=1
[action1_main/_k3slr_tty_capability]=1
[action1_main/_k3slr_lifecycle_dispatch]=1
[action1_main/_k3slr_lifecycle_step]=1
[action1_main/_k3slr_lifecycle_action]=1
[action1_main/_k3slr_command]=1
[action1_main/task5b_gate0_route_equals]=1
[action1_main/task5b_action1_main_probe_payload]=1
[action1_main/task5b_production_probe_emit_ok]=1
[action1_alternate/_k3slr_lifecycle_action]=1
[action1_alternate/_k3slr_command]=1
[action1_alternate/task5b_gate0_route_equals]=1
[action1_alternate/task5b_action1_alternate_probe_payload]=1
[action1_alternate/task5b_production_probe_emit_ok]=1
[canary/task5b_production_probe_canary_helper_root]=1
[canary/task5b_production_probe_canary_helper_leaf]=1
)
readonly -A TASK5B_PRODUCTION_PROBE_EXPECTED_HASHES=(
[core/task5b_production_probe_profile_is_known]=ed04cce41523a02acbebea479607e696dc5ab7ec1486a4914230b74572cd85af
[core/task5b_production_probe_function_is_allowed]=559b5481974916559ae305975aaab82f88a542dc53547dee4575d599d62903ea
[core/task5b_production_probe_builtin_is_allowed]=58cb03fd47554c9cf9ef4b52372e7125b86c86cd88b23364ff31c41e41e1c84b
[core/task5b_production_probe_debug]=662c89fd5213726938630c16b475d894f5a19ec922a8c09dc7563312c55e38a7
[core/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[core/task5b_production_probe_canary_graph_is_pinned]=c3c0724de2a4d7da8d20739d5f66d46dc54bde69c2e791d3171d2e4c72392ac5
[core/task5b_production_probe_body_is_closed]=bc1d27a01debe57be58252f402ddd958fbd9c4fe73da5a4a2c7cfef7851e067d
[core/task5b_production_probe_arm]=e391120fe268bbfb2b67c8d6cb23591fa9f5635dc1549a9f066c4aba711d5c34
[core/task5b_gate0_assignment_only_status]=075118e7edcd50ef5722f01874d2b138b4ab6cc483a6c17be517bc9a45df7af8
[unavailable/_k3slr_command]=8634e9ebe9c34a48eff4381729ce6883b95a69a69c6c7218498b6db8a7806248
[unavailable/task5b_unavailable_probe_payload]=e83567872c19a73a08baa434eb80e449f493505d6945c4d4f4cae2ffedaa2435
[unavailable/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[unavailable/_k3slr_lifecycle_action]=a499b7694c449633f3ce538e8296b1d543d04565c046508587d18b677aa12384
[legacy_binding/task5b_legacy_binding_probe_payload]=46d73155080f35fdd4c9ec1d531ed50f6ac77447b6389549d934f862e4430316
[legacy_binding/_k3slr_lifecycle_action]=a499b7694c449633f3ce538e8296b1d543d04565c046508587d18b677aa12384
[legacy_binding/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[legacy_binding/_k3slr_command]=8634e9ebe9c34a48eff4381729ce6883b95a69a69c6c7218498b6db8a7806248
[api_dispatch/_k3slr_command]=8634e9ebe9c34a48eff4381729ce6883b95a69a69c6c7218498b6db8a7806248
[api_dispatch/_k3slr_lifecycle_step]=1cf91eba1179fbb35f58ca20dbf0e62f2ff3b754cc64f6e4c6547c1a1193b212
[api_dispatch/task5b_api_dispatch_probe_payload]=341b9a6990134bc9f95fb881e4596a4db94fbfbc4303e8afe61720f1684958e4
[api_dispatch/_k3slr_lifecycle_action]=a499b7694c449633f3ce538e8296b1d543d04565c046508587d18b677aa12384
[api_dispatch/_k3slr_lifecycle_dispatch]=e407e698b9a69c47c9969db009211e47614c9753f701fa99f82a6d9e84c82477
[api_dispatch/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[api_dispatch/task5b_api_state_debug_observer]=c9bec0cfb5204ee77ac32fab9eb31b00cf033d6fd3ac897a98878991ca13897f
[api_direct/_k3slr_lifecycle_action]=a499b7694c449633f3ce538e8296b1d543d04565c046508587d18b677aa12384
[api_direct/_k3slr_command]=8634e9ebe9c34a48eff4381729ce6883b95a69a69c6c7218498b6db8a7806248
[api_direct/task5b_api_direct_probe_payload]=c4723fd1f9a04b2095a1935c21c5cebca01f2e13c75f9ceda59d58ad1a909332
[api_direct/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[api_main_arity/task5b_api_main_arity_probe_payload]=5c23d4c2810c534318d210de7c35a6ac7f0fbf52ab88edbd421c30d2c534ff8a
[api_main_arity/_k3slr_lifecycle_main]=677f7f482840af5c5a7dc37c4f7abe6a470a24ed108030cb14fe1f3b1c8ae53f
[api_main_arity/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[action1_main/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[action1_main/_k3slr_contract_value_is_valid]=b8eab6a6688c3aac2456bf9a4987eeeefec673a1dccf5e80e785cc9dcbddc7dd
[action1_main/_k3slr_lifecycle_action]=a499b7694c449633f3ce538e8296b1d543d04565c046508587d18b677aa12384
[action1_main/task5b_action1_main_probe_payload]=ac0431e91d90989ae9eb49dd7ebcecbc12a87f99f50771db0fce62ce9960aa7c
[action1_main/_k3slr_parse_contract]=a27e7c60983ecb4792349c69bc6020ee51dfdd6f0adefbf4359b54b20cf64b0d
[action1_main/_k3slr_uint]=e60445f9cc78399afebda8d2010fbd64ceae23da4cfa15373c60122127df6945
[action1_main/task5b_gate0_route_equals]=53f6a8cdb6726626a1db2136d7c38e4f2c52c4eaaeb3671506fb504475586a19
[action1_main/_k3slr_tty_capability]=c59e220dbcc6f3d9af8707cc365c456ee404d20deed8150171a8fb37d87a7497
[action1_main/_k3slr_load_contract]=71855162c947b01b145e79aa4e30a448c21690efa183c56ccafe97b776912cce
[action1_main/_k3slr_require_execute_tty]=9d194fe3ba36a7bd25aaa5b53eefeafa4d23fe8f307995be7a9039472b42a838
[action1_main/_k3slr_command]=6e84784f49cc5e8072cdd4c8c0cccbd20ef15e5886228946d1c7c670967a3ec2
[action1_main/_k3slr_lifecycle_main]=677f7f482840af5c5a7dc37c4f7abe6a470a24ed108030cb14fe1f3b1c8ae53f
[action1_main/_k3slr_lifecycle_dispatch]=e407e698b9a69c47c9969db009211e47614c9753f701fa99f82a6d9e84c82477
[action1_main/_k3slr_lifecycle_step]=1cf91eba1179fbb35f58ca20dbf0e62f2ff3b754cc64f6e4c6547c1a1193b212
[action1_main/task5b_action1_debug_observer]=ecaf3a7e1ee93fd5f330301b8fc21c0300834bd5ec376c766af704de83271cee
[action1_alternate/task5b_gate0_route_equals]=53f6a8cdb6726626a1db2136d7c38e4f2c52c4eaaeb3671506fb504475586a19
[action1_alternate/_k3slr_lifecycle_action]=a499b7694c449633f3ce538e8296b1d543d04565c046508587d18b677aa12384
[action1_alternate/_k3slr_command]=6e84784f49cc5e8072cdd4c8c0cccbd20ef15e5886228946d1c7c670967a3ec2
[action1_alternate/task5b_action1_alternate_probe_payload]=aa5693a60539d54eda18fdefef47c7ad20891d94d0478df41a0afa46860565cc
[action1_alternate/task5b_production_probe_emit_ok]=278abe2c9fb6b744caa3a5517ebc8b504bee1409df4819e6fb9be1129b22fbd0
[canary/task5b_production_probe_canary_helper_root]=d14b146f01b0a6ef5a36be1d613994901683a503661348195b5c2eeca7b9aa9b
[canary/task5b_production_probe_canary_helper_leaf]=a94ebf016f987960b669e5a7c9b776e95b7d305675dc74868b46bc25460810a9
)
task5b_production_probe_profile_is_known() {
(( $# == 1 )) || return 1
case "$1" in
unavailable|legacy_binding|api_dispatch|api_direct|api_main_arity|\
action1_main|action1_alternate|canary) return 0 ;;
*) return 1 ;;
esac
}
task5b_production_probe_function_is_allowed() {
local profile="${1-}" function_name="${2-}"
(( $# == 2 )) || return 1
task5b_production_probe_profile_is_known "$profile" || return 1
[[ "${TASK5B_PRODUCTION_PROBE_ALLOWED_FUNCTIONS["$profile/$function_name"]+present}" == present ]]
}
task5b_production_probe_builtin_is_allowed() {
local profile="${1-}" builtin_name="${2-}"
(( $# == 2 )) || return 1
task5b_production_probe_profile_is_known "$profile" || return 1
case "$profile" in
canary)
[[ "$builtin_name" == printf ]]
;;
action1_main)
case "$builtin_name" in
declare|local|printf|return|shift) return 0 ;;
*) return 1 ;;
esac
;;
*)
case "$builtin_name" in
local|printf|return|shift) return 0 ;;
*) return 1 ;;
esac
;;
esac
}
task5b_production_probe_debug() {
local -a saved_bash_rematch=("${BASH_REMATCH[@]}")
local candidate="${1-}" observed_step="${2-}" execution_word=''
local assignment_rc=0
(( $# == 2 )) || exit 96
task5b_production_probe_profile_is_known "$task5b_probe_profile" || exit 96
case "$task5b_probe_profile" in
api_dispatch)
task5b_api_state_debug_observer "$candidate" || {
printf 'TASK5B_PROBE_BLOCK|%s|1\n' "$task5b_probe_profile" >&9
exit 96
}
;;
action1_main)
task5b_action1_debug_observer "$candidate" "$observed_step" || {
printf 'TASK5B_PROBE_BLOCK|%s|1\n' "$task5b_probe_profile" >&9
exit 96
}
;;
esac
candidate="${candidate#"${candidate%%[![:space:]]*}"}"
while [[ "$candidate" =~ ^(if|elif|while|until|then|\!|time)[[:space:]]+(.+)$ ]]; do
candidate="${BASH_REMATCH[2]}"
done
case "$candidate" in
''|'{'|'}'|'[['*|'(('*|for\ *|case\ *|do|done|esac)
BASH_REMATCH=("${saved_bash_rematch[@]}")
return 0
;;
esac
if [[ "$task5b_probe_profile" == action1_main ]]; then
case "$candidate" in
'IFS= read -r line'|'seen["$key"]=1'|'result["$key"]="$value"')
BASH_REMATCH=("${saved_bash_rematch[@]}")
return 0
;;
esac
fi
if [[ "$candidate" =~ ^[a-zA-Z_][a-zA-Z0-9_]*(\[[^]]+\])?\+?= ]]; then
task5b_gate0_assignment_only_status "$candidate" || assignment_rc=$?
if (( assignment_rc == 0 )); then
BASH_REMATCH=("${saved_bash_rematch[@]}")
return 0
fi
printf 'TASK5B_PROBE_BLOCK|%s|1\n' "$task5b_probe_profile" >&9
exit 96
fi
read -r execution_word _ <<<"$candidate"
execution_word="${execution_word%;}"
case "$execution_word" in
\$*|\"\$*|\'\$*|/*|\"/*|\'/*|sudo|command|builtin|exec|eval|source|.|\
trap|set|shopt|unset|enable|function)
printf 'TASK5B_PROBE_BLOCK|%s|1\n' "$task5b_probe_profile" >&9
exit 96
;;
esac
if task5b_production_probe_builtin_is_allowed "$task5b_probe_profile" \
"$execution_word" ||
task5b_production_probe_function_is_allowed "$task5b_probe_profile" \
"$execution_word"; then
BASH_REMATCH=("${saved_bash_rematch[@]}")
return 0
fi
printf 'TASK5B_PROBE_BLOCK|%s|1\n' "$task5b_probe_profile" >&9
exit 96
}
task5b_production_probe_emit_ok() {
(( $# >= 1 )) || return 1
printf 'TASK5B_PROBE_OK|%s' "$1" >&9
shift
printf '|%s' "$@" >&9
printf '\n' >&9
}
task5b_production_probe_canary_graph_is_pinned() {
local entry function_name actual_hash='' expected_hash='' function_count=0
(( $# == 0 )) || return 1
for entry in "${!TASK5B_PRODUCTION_PROBE_ALLOWED_FUNCTIONS[@]}"; do
[[ "$entry" == canary/* ]] || continue
function_name="${entry#*/}"
(( function_count += 1 ))
expected_hash="${TASK5B_PRODUCTION_PROBE_EXPECTED_HASHES[$entry]-}"
[[ "$expected_hash" =~ ^[0-9a-f]{64}$ ]] || return 1
task5b_gate0_definition_hash actual_hash "$function_name" || return 1
[[ "$actual_hash" == "$expected_hash" ]] || return 1
done
(( function_count == 2 ))
}
task5b_production_probe_body_is_closed() {
local profile="${1-}" function_name="${2-}" body="${3-}"
local line='' scan_body='' character='' next_character='' quote=''
local mode=normal escaped=0 index=0 length=0 line_number=0
local allowed_redirection_count=0 expected_redirection_count=0
(( $# == 3 )) || return 1
task5b_production_probe_profile_is_known "$profile" || return 1
[[ "$function_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ && -n "$body" ]] ||
return 1
case "$profile/$function_name" in
*/task5b_production_probe_emit_ok) expected_redirection_count=3 ;;
action1_main/_k3slr_parse_contract|*/_k3slr_lifecycle_main)
expected_redirection_count=1
;;
esac
while IFS= read -r line; do
if [[ "$function_name" == task5b_production_probe_emit_ok ]]; then
case "$line" in
" printf 'TASK5B_PROBE_OK|%s' \"\$1\" 1>&9;"|\
" printf '|%s' \"\$@\" 1>&9;"|" printf '\\n' 1>&9")
(( allowed_redirection_count += 1 ))
continue
;;
esac
elif [[ "$profile/$function_name" == action1_main/_k3slr_parse_contract &&
"$line" == " done < \"\$file\";" ]]; then
(( allowed_redirection_count += 1 ))
continue
elif [[ "$function_name" == _k3slr_lifecycle_main &&
"$line" == ' declare -F _k3slr_prove_header_restore > /dev/null || return 1;' ]]; then
(( allowed_redirection_count += 1 ))
continue
fi
scan_body+="$line"$'\n'
done <<<"$body"
(( allowed_redirection_count == expected_redirection_count )) || return 1
[[ "$scan_body" != *'<('* && "$scan_body" != *'>('* &&
"$scan_body" != *'trap - DEBUG'* && "$scan_body" != *'set +T'* &&
"$scan_body" != *'shopt -u extdebug'* && "$scan_body" != *'unset '* &&
"$scan_body" != *'enable '* ]] || return 1
while IFS= read -r line; do
(( line_number += 1 ))
if (( line_number > 1 )) &&
[[ "$line" =~ ^[[:space:]]*(function[[:space:]]+|[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(\)) ]]; then
return 1
fi
quote=''
mode=normal
escaped=0
index=0
length="${#line}"
while (( index < length )); do
character="${line:index:1}"
next_character=''
(( index + 1 >= length )) || next_character="${line:index + 1:1}"
if (( escaped == 1 )); then
escaped=0
(( index += 1 ))
continue
fi
case "$quote" in
single)
[[ "$character" != "'" ]] || quote=''
(( index += 1 ))
continue
;;
double)
case "$character" in
'\\') escaped=1 ;;
'"') quote='' ;;
esac
(( index += 1 ))
continue
;;
esac
case "$mode" in
conditional)
if [[ "$character$next_character" == ']]' ]]; then
mode=normal
(( index += 2 ))
else
(( index += 1 ))
fi
continue
;;
arithmetic)
if [[ "$character$next_character" == '))' ]]; then
mode=normal
(( index += 2 ))
else
(( index += 1 ))
fi
continue
;;
esac
case "$character" in
"'") quote=single ;;
'"') quote=double ;;
'\\') escaped=1 ;;
'[')
if [[ "$next_character" == '[' ]]; then
mode=conditional
(( index += 2 ))
continue
fi
;;
'(')
if [[ "$next_character" == '(' ]]; then
mode=arithmetic
(( index += 2 ))
continue
fi
;;
'<'|'>') return 1 ;;
esac
(( index += 1 ))
done
done <<<"$scan_body"
}
task5b_production_probe_core_is_closed() {
local function_name actual_hash='' expected_hash=''
local -a core_functions=(
task5b_production_probe_profile_is_known
task5b_production_probe_function_is_allowed
task5b_production_probe_builtin_is_allowed
task5b_production_probe_debug
task5b_production_probe_emit_ok
task5b_production_probe_canary_graph_is_pinned
task5b_production_probe_body_is_closed
task5b_production_probe_arm
task5b_gate0_assignment_only_status
)
(( $# == 0 )) || return 1
for function_name in "${core_functions[@]}"; do
expected_hash="${TASK5B_PRODUCTION_PROBE_EXPECTED_HASHES["core/$function_name"]-}"
[[ "$expected_hash" =~ ^[0-9a-f]{64}$ ]] || return 1
task5b_gate0_definition_hash actual_hash "$function_name" || return 1
[[ "$actual_hash" == "$expected_hash" ]] || {
printf 'Task 5B production probe core hash mismatch: %s expected=%s actual=%s\n' \
"$function_name" "$expected_hash" "$actual_hash" >&2
return 1
}
done
}
task5b_production_probe_static_graph_is_closed() {
local profile="${1-}" entry function_name body='' normalized_body=''
local actual_hash='' expected_hash='' function_count=0 line='' line_number=0
(( $# == 1 )) || return 1
task5b_production_probe_profile_is_known "$profile" || return 1
[[ "$profile" != canary ]] || return 1
task5b_production_probe_core_is_closed || return 1
if [[ "$profile" == api_dispatch ]]; then
function_name=task5b_api_state_debug_observer
entry="$profile/$function_name"
expected_hash="${TASK5B_PRODUCTION_PROBE_EXPECTED_HASHES[$entry]-}"
[[ "$expected_hash" =~ ^[0-9a-f]{64}$ ]] || return 1
task5b_gate0_definition_hash actual_hash "$function_name" || return 1
[[ "$actual_hash" == "$expected_hash" ]] || {
printf 'Task 5B production probe observer hash mismatch: %s expected=%s actual=%s\n' \
"$function_name" "$expected_hash" "$actual_hash" >&2
return 1
}
elif [[ "$profile" == action1_main ]]; then
function_name=task5b_action1_debug_observer
entry="$profile/$function_name"
expected_hash="${TASK5B_PRODUCTION_PROBE_EXPECTED_HASHES[$entry]-}"
[[ "$expected_hash" =~ ^[0-9a-f]{64}$ ]] || return 1
task5b_gate0_definition_hash actual_hash "$function_name" || return 1
[[ "$actual_hash" == "$expected_hash" ]] || {
printf 'Task 5B production probe observer hash mismatch: %s expected=%s actual=%s\n' \
"$function_name" "$expected_hash" "$actual_hash" >&2
return 1
}
fi
for entry in "${!TASK5B_PRODUCTION_PROBE_ALLOWED_FUNCTIONS[@]}"; do
[[ "$entry" == "$profile/"* ]] || continue
function_name="${entry#*/}"
(( function_count += 1 ))
expected_hash="${TASK5B_PRODUCTION_PROBE_EXPECTED_HASHES[$entry]-}"
[[ "$expected_hash" =~ ^[0-9a-f]{64}$ ]] || return 1
task5b_gate0_definition_hash actual_hash "$function_name" || return 1
[[ "$actual_hash" == "$expected_hash" ]] || {
printf 'Task 5B production probe profile hash mismatch: %s expected=%s actual=%s\n' \
"$entry" "$expected_hash" "$actual_hash" >&2
return 1
}
body="$(declare -f "$function_name")" || return 1
task5b_production_probe_body_is_closed "$profile" "$function_name" \
"$body" || {
printf 'Task 5B production probe body scan failed: %s\n' "$entry" >&2
return 1
}
[[ "$body" != *'<('* && "$body" != *'>('* &&
"$body" != *'trap - DEBUG'* && "$body" != *'set +T'* &&
"$body" != *'shopt -u extdebug'* && "$body" != *'unset '* &&
"$body" != *'enable '* ]] || {
printf 'Task 5B production probe forbidden token: %s\n' "$entry" >&2
return 1
}
line_number=0
while IFS= read -r line; do
(( line_number += 1 ))
if (( line_number > 1 )) &&
[[ "$line" =~ ^[[:space:]]*(function[[:space:]]+|[a-zA-Z_][a-zA-Z0-9_]*[[:space:]]*\(\)) ]]; then
printf 'Task 5B production probe nested definition: %s\n' "$entry" >&2
return 1
fi
done <<<"$body"
task5b_gate0_normalize_static_body_data_lines "$body" normalized_body \
"$function_name" || {
printf 'Task 5B production probe normalization failed: %s\n' "$entry" >&2
return 1
}
if [[ "$function_name" != _k3slr_parse_contract ]]; then
task5b_gate0_static_assignment_prefixes_are_closed "$body" || {
printf 'Task 5B production probe assignment scan failed: %s\n' "$entry" >&2
return 1
}
fi
task5b_gate0_static_execution_positions_are_closed "$normalized_body" ||
{
printf 'Task 5B production probe execution scan failed: %s\n' "$entry" >&2
return 1
}
done
(( function_count > 1 )) || return 1
}
task5b_production_probe_arm() {
(( $# == 1 )) || return 1
task5b_production_probe_profile_is_known "$1" || return 1
task5b_probe_profile="$1"
shopt -s extdebug
set -T
trap 'task5b_production_probe_debug "${BASH_COMMAND-}" "${step-}"' DEBUG
}
task5b_production_probe_canary_helper_root() {
task5b_production_probe_canary_helper_leaf
}
task5b_production_probe_canary_helper_leaf() {
/usr/bin/printf 'TASK5B_ACTION1_HELPER_EXEC_CANARY'
}
task5b_production_probe_canary_unexpected_helper() {
/usr/bin/printf 'TASK5B_UNEXPECTED_HELPER_EXEC_CANARY'
}
task5b_production_probe_static_redirection_adversary() {
printf 'TASK5B_REDIRECTION_CANARY' >"$fixture_root/task5b-redirection-canary"
}
task5b_production_probe_static_process_substitution_adversary() {
: < <(/usr/bin/printf 'TASK5B_PROCESS_SUBSTITUTION_CANARY')
}
task5b_production_probe_static_redefinition_adversary() {
task5b_nested_redefinition() { :; }
}
task5b_production_probe_adversary_matrix() (
local adversary='' record='' probe_rc=0 body='' graph_body=''
local -a adversaries=(
direct helper variable array command-wrapper builtin-wrapper exec-wrapper
eval-wrapper source-wrapper unexpected-helper trap-disable functrace-disable
extdebug-disable unset-wrapper enable-wrapper
)
task5b_production_probe_core_is_closed || return 1
task5b_production_probe_canary_graph_is_pinned || return 1
graph_body="$(declare -f task5b_production_probe_static_graph_is_closed)" ||
return 1
[[ "$graph_body" == *'task5b_production_probe_body_is_closed "$profile" "$function_name" "$body"'* ]] ||
fail 'production graph closure uses the same body scanner as static adversaries'
for adversary in "${adversaries[@]}"; do
record=''
probe_rc=0
record="$(
local runner=''
local -a runners=()
exec 9>&1
exec 2>&1
task5b_production_probe_arm canary || return 1
case "$adversary" in
direct) /usr/bin/printf 'TASK5B_DIRECT_EXEC_CANARY' ;;
helper) task5b_production_probe_canary_helper_root ;;
variable)
runner=/usr/bin/printf
"$runner" 'TASK5B_VARIABLE_EXEC_CANARY'
;;
array)
runners=(/usr/bin/printf TASK5B_ARRAY_EXEC_CANARY)
"${runners[@]}"
;;
command-wrapper) command /usr/bin/printf 'TASK5B_COMMAND_EXEC_CANARY' ;;
builtin-wrapper)
builtin command /usr/bin/printf 'TASK5B_BUILTIN_EXEC_CANARY'
;;
exec-wrapper) exec /usr/bin/printf 'TASK5B_EXEC_CANARY' ;;
eval-wrapper) eval '/usr/bin/printf TASK5B_EVAL_CANARY' ;;
source-wrapper) source /fixture/task5b-source-canary ;;
unexpected-helper) task5b_production_probe_canary_unexpected_helper ;;
trap-disable) trap - DEBUG ;;
functrace-disable) set +T ;;
extdebug-disable) shopt -u extdebug ;;
unset-wrapper) unset task5b_probe_profile ;;
enable-wrapper) enable -n printf ;;
*) return 1 ;;
esac
printf 'TASK5B_CANARY_ESCAPED'
)" || probe_rc=$?
assert_eq 96 "$probe_rc" "production probe fuse blocks adversary: $adversary"
assert_eq 'TASK5B_PROBE_BLOCK|canary|1' "$record" \
"production probe adversary has one block record and no effect: $adversary"
done
body="$(declare -f task5b_production_probe_static_redirection_adversary)" ||
return 1
assert_fails task5b_production_probe_body_is_closed canary \
task5b_production_probe_static_redirection_adversary "$body"
body="$(declare -f task5b_production_probe_static_process_substitution_adversary)" ||
return 1
assert_fails task5b_production_probe_body_is_closed canary \
task5b_production_probe_static_process_substitution_adversary "$body"
body="$(declare -f task5b_production_probe_static_redefinition_adversary)" ||
return 1
assert_fails task5b_production_probe_body_is_closed canary \
task5b_production_probe_static_redefinition_adversary "$body"
)
task5b_mountinfo_counts_record() {
local record="${1-}" source_destination="${2-}" outer_destination="${3-}"
local inner_destination="${4-}" name line target source field_index
local source_count=0 outer_count=0 inner_count=0
local -a fields=()
(( $# == 4 )) || return 1
for name in "$source_destination" "$outer_destination" "$inner_destination"; do
[[ "$name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
done
while IFS= read -r line; do
[[ -n "$line" ]] || continue
read -r -a fields <<<"$line"
(( ${#fields[@]} >= 10 )) || return 1
target="${fields[4]}"
source=''
for (( field_index=0; field_index<${#fields[@]}; field_index++ )); do
if [[ "${fields[field_index]}" == - ]]; then
(( field_index + 2 < ${#fields[@]} )) || return 1
source="${fields[field_index + 2]}"
break
fi
done
[[ -n "$source" ]] || return 1
[[ "$source" != /dev/sda3 ]] || (( source_count += 1 ))
[[ "$target" != /mnt/k3s-recovery-ssd ]] || (( outer_count += 1 ))
[[ "$target" != /srv/recovery/k3s ]] || (( inner_count += 1 ))
done <<<"$record"
printf -v "$source_destination" '%s' "$source_count"
printf -v "$outer_destination" '%s' "$outer_count"
printf -v "$inner_destination" '%s' "$inner_count"
}
task5b_loop_backing_is_recovery() {
(( $# == 1 )) || return 1
case "$1" in
/fixture/recovery-partition|\
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks)
return 0
;;
*) return 1 ;;
esac
}
task5b_read_exact_single_line() {
local destination_name="${1-}" source_path="${2-}"
local -a source_lines=()
(( $# == 2 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
[[ -r "$source_path" ]] || return 1
mapfile -t source_lines <"$source_path" || return 1
(( ${#source_lines[@]} == 1 )) || return 1
[[ -n "${source_lines[0]}" ]] || return 1
printf -v "$destination_name" '%s' "${source_lines[0]}"
}
task5b_loop_identity_count_from_record() {
local record="${1-}" recovery_major_minor="${2-}" destination_name="${3-}"
local line loop_name loop_minor back_inode back_major_minor loop_major_minor
local offset sizelimit diskseq_before cached_diskseq diskseq_after
local encoded_filename extra identity_match_count=0
local -A seen_loop_names=()
(( $# == 3 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
[[ "$recovery_major_minor" =~ ^[0-9]+:[0-9]+$ ]] || return 1
while IFS= read -r line; do
[[ -n "$line" ]] || continue
IFS='|' read -r loop_name back_inode back_major_minor loop_major_minor \
offset sizelimit diskseq_before cached_diskseq diskseq_after \
encoded_filename extra <<<"$line"
[[ -z "${extra:-}" ]] || return 1
[[ "$loop_name" =~ ^/dev/loop([0-9]+)$ ]] || return 1
loop_minor="${BASH_REMATCH[1]}"
[[ -z "${seen_loop_names[$loop_name]+present}" ]] || return 1
seen_loop_names["$loop_name"]=1
[[ "$back_inode" =~ ^[1-9][0-9]*$ ]] || return 1
[[ "$back_major_minor" =~ ^[0-9]+:[0-9]+$ ]] || return 1
[[ "$loop_major_minor" == "7:${loop_minor}" ]] || return 1
[[ "$offset" =~ ^[0-9]+$ && "$sizelimit" =~ ^[0-9]+$ ]] || return 1
[[ "$diskseq_before" =~ ^[1-9][0-9]*$ ]] || return 1
[[ "$cached_diskseq" == "$diskseq_before" ]] || return 1
[[ "$diskseq_after" == "$diskseq_before" ]] || return 1
[[ "$encoded_filename" =~ ^([a-zA-Z0-9._/:+-]|\\x[0-9a-fA-F]{2})+$ ]] || return 1
[[ "$back_major_minor" != "$recovery_major_minor" ]] ||
(( identity_match_count += 1 ))
done <<<"$record"
printf -v "$destination_name" '%s' "$identity_match_count"
}
task5b_loop_live_identity_line() {
local loop_name="${1-}" recovery_major_minor="${2-}" destination_name="${3-}"
local loop_minor
local sysfs_root loop_major_minor_before='' loop_major_minor_after=''
local diskseq_before='' diskseq_after='' backing_before='' backing_after=''
local offset_before='' offset_after='' sizelimit_before='' sizelimit_after=''
local udev_path line value back_major_minor='' back_inode=''
local encoded_filename='' cached_diskseq='' field_name
local back_major_minor_count=0 back_inode_count=0 encoded_filename_count=0
local cached_diskseq_count=0
(( $# == 3 )) || return 1
[[ "$loop_name" =~ ^loop([0-9]+)$ ]] || return 1
loop_minor="${BASH_REMATCH[1]}"
[[ "$recovery_major_minor" =~ ^[0-9]+:[0-9]+$ ]] || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
sysfs_root="/sys/class/block/${loop_name}"
task5b_read_exact_single_line loop_major_minor_before "${sysfs_root}/dev" || return 1
task5b_read_exact_single_line diskseq_before "${sysfs_root}/diskseq" || return 1
task5b_read_exact_single_line backing_before \
"${sysfs_root}/loop/backing_file" || return 1
task5b_read_exact_single_line offset_before "${sysfs_root}/loop/offset" || return 1
task5b_read_exact_single_line sizelimit_before \
"${sysfs_root}/loop/sizelimit" || return 1
[[ "$loop_major_minor_before" == "7:${loop_minor}" ]] || return 1
[[ "$diskseq_before" =~ ^[1-9][0-9]*$ ]] || return 1
[[ "$offset_before" =~ ^[0-9]+$ && "$sizelimit_before" =~ ^[0-9]+$ ]] ||
return 1
udev_path="/run/udev/data/b${loop_major_minor_before}"
[[ -r "$udev_path" ]] || return 1
while IFS= read -r line; do
case "$line" in
E:ID_LOOP_BACKING_DEVICE=*)
value="${line#E:ID_LOOP_BACKING_DEVICE=}"
(( back_major_minor_count += 1 ))
back_major_minor="$value"
;;
E:ID_LOOP_BACKING_INODE=*)
value="${line#E:ID_LOOP_BACKING_INODE=}"
(( back_inode_count += 1 ))
back_inode="$value"
;;
E:ID_LOOP_BACKING_FILENAME_ENC=*)
value="${line#E:ID_LOOP_BACKING_FILENAME_ENC=}"
(( encoded_filename_count += 1 ))
encoded_filename="$value"
;;
S:disk/by-diskseq/*)
value="${line#S:disk/by-diskseq/}"
(( cached_diskseq_count += 1 ))
cached_diskseq="$value"
;;
esac
done <"$udev_path" || return 1
for field_name in back_major_minor back_inode encoded_filename cached_diskseq; do
[[ -n "${!field_name}" ]] || return 1
done
(( back_major_minor_count == 1 && back_inode_count == 1 &&
encoded_filename_count == 1 && cached_diskseq_count == 1 )) || return 1
[[ "$back_major_minor" =~ ^[0-9]+:[0-9]+$ ]] || return 1
[[ "$back_inode" =~ ^[1-9][0-9]*$ ]] || return 1
[[ "$encoded_filename" =~ ^([a-zA-Z0-9._/:+-]|\\x[0-9a-fA-F]{2})+$ ]] || return 1
[[ "$cached_diskseq" == "$diskseq_before" ]] || return 1
task5b_read_exact_single_line loop_major_minor_after "${sysfs_root}/dev" || return 1
task5b_read_exact_single_line diskseq_after "${sysfs_root}/diskseq" || return 1
task5b_read_exact_single_line backing_after \
"${sysfs_root}/loop/backing_file" || return 1
task5b_read_exact_single_line offset_after "${sysfs_root}/loop/offset" || return 1
task5b_read_exact_single_line sizelimit_after \
"${sysfs_root}/loop/sizelimit" || return 1
[[ "$loop_major_minor_after" == "$loop_major_minor_before" ]] || return 1
[[ "$diskseq_after" == "$diskseq_before" ]] || return 1
[[ "$backing_after" == "$backing_before" ]] || return 1
[[ "$offset_after" == "$offset_before" ]] || return 1
[[ "$sizelimit_after" == "$sizelimit_before" ]] || return 1
if task5b_loop_backing_is_recovery "$backing_before"; then
[[ "$back_major_minor" == "$recovery_major_minor" ]] || return 1
fi
printf -v "$destination_name" '%s' \
"/dev/${loop_name}|${back_inode}|${back_major_minor}|${loop_major_minor_before}|${offset_before}|${sizelimit_before}|${diskseq_before}|${cached_diskseq}|${diskseq_after}|${encoded_filename}"
}
task5b_recovery_loop_count() {
local destination_name="${1-}" recovery_major_minor=''
local loop_path loop_name identity_line identity_record='' loop_index
local -a before_paths=() after_paths=()
(( $# == 1 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
task5b_read_exact_single_line recovery_major_minor /sys/class/block/sda3/dev ||
return 1
[[ "$recovery_major_minor" =~ ^[0-9]+:[0-9]+$ ]] || return 1
shopt -s nullglob
before_paths=(/sys/class/block/loop*/loop/backing_file)
for loop_path in "${before_paths[@]}"; do
loop_name="${loop_path#/sys/class/block/}"
loop_name="${loop_name%%/*}"
task5b_loop_live_identity_line "$loop_name" "$recovery_major_minor" \
identity_line || return 1
identity_record+="$identity_line"$'\n'
done
after_paths=(/sys/class/block/loop*/loop/backing_file)
shopt -u nullglob
(( ${#after_paths[@]} == ${#before_paths[@]} )) || return 1
for (( loop_index=0; loop_index<${#before_paths[@]}; loop_index++ )); do
[[ "${after_paths[loop_index]}" == "${before_paths[loop_index]}" ]] || return 1
done
task5b_loop_identity_count_from_record "$identity_record" \
"$recovery_major_minor" "$destination_name"
}
task5b_path_state_from_observation() {
local destination_name="${1-}" exists="${2-}" symlink="${3-}" state=absent
(( $# == 3 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
[[ "$exists" == 0 || "$exists" == 1 ]] || return 1
[[ "$symlink" == 0 || "$symlink" == 1 ]] || return 1
(( exists == 0 && symlink == 0 )) || state=present
printf -v "$destination_name" '%s' "$state"
}
task5b_path_state() {
local destination_name="${1-}" path="${2-}" exists=0 symlink=0
(( $# == 2 )) || return 1
[[ -e "$path" ]] && exists=1
[[ -L "$path" ]] && symlink=1
task5b_path_state_from_observation "$destination_name" "$exists" "$symlink"
}
task5b_fixture_process_argv_matches() {
local current_pid="${1-}" candidate_pid="${2-}" executable='' argument
local test_script_match=0 fixture_marker_match=0
shift 2 || return 1
[[ "$current_pid" =~ ^[1-9][0-9]*$ && "$candidate_pid" =~ ^[1-9][0-9]*$ ]] || return 1
[[ "$candidate_pid" != "$current_pid" ]] || return 1
(( $# >= 1 )) || return 1
executable="$1"
[[ "$executable" == bash || "$executable" == /bin/bash ||
"$executable" == /usr/bin/bash ]] || return 1
for argument in "$@"; do
case "$argument" in
"$REPOSITORY_ROOT/scripts/validate/test-k3s-local-recovery.sh"|\
scripts/validate/test-k3s-local-recovery.sh)
test_script_match=1
;;
task5b-fixture|task5b-fixture:*) fixture_marker_match=1 ;;
esac
done
(( test_script_match == 1 || fixture_marker_match == 1 ))
}
task5b_expected_safe_live_state_record() {
local destination_name="${1-}"
(( $# == 1 )) || return 1
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
printf -v "$destination_name" '%s' \
$'mounts_sda3=0\nmounts_outer=0\nmounts_inner=0\nouter_mount_path=absent\ninner_mount_path=absent\nrecovery_loops=0\nmapper_main=absent\nmapper_proof=absent\nreserved_root=absent\ntask5b_temp_residue=0\ntask5b_fixture_processes=0'
}
task5b_live_state_record() {
local destination_name="${1-}" line mountinfo_record=''
local mounts_sda3=0 mounts_outer=0 mounts_inner=0 recovery_loops=0
local outer_mount_path=absent inner_mount_path=absent
local mapper_main=absent mapper_proof=absent reserved_root=absent
local task5b_temp_residue=0 task5b_fixture_processes=0 pid_path candidate_pid
local -a residue_paths=() process_arguments=()
[[ "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || return 1
while IFS= read -r line; do
mountinfo_record+="$line"$'\n'
done </proc/self/mountinfo
task5b_mountinfo_counts_record "$mountinfo_record" mounts_sda3 mounts_outer \
mounts_inner || return 1
task5b_recovery_loop_count recovery_loops || return 1
task5b_path_state outer_mount_path /mnt/k3s-recovery-ssd || return 1
task5b_path_state inner_mount_path /srv/recovery/k3s || return 1
task5b_path_state mapper_main /dev/mapper/k3s-recovery || return 1
task5b_path_state mapper_proof /dev/mapper/k3s-recovery-proof || return 1
task5b_path_state reserved_root \
/mnt/k3s-recovery-ssd/HyeonworksRecovery || return 1
shopt -s nullglob
residue_paths=("${fixture_root}"/task5b-* "${fixture_root}"/k3slr-capture.*
/tmp/k3slr-task5b.* /tmp/k3slr-capture.* /tmp/task5b-fixture.*)
task5b_temp_residue="${#residue_paths[@]}"
shopt -u nullglob
for pid_path in /proc/[0-9]*/cmdline; do
candidate_pid="${pid_path#/proc/}"
candidate_pid="${candidate_pid%/cmdline}"
process_arguments=()
while IFS= read -r -d '' line; do
process_arguments+=("$line")
done <"$pid_path" 2>/dev/null || :
task5b_fixture_process_argv_matches "$BASHPID" "$candidate_pid" \
"${process_arguments[@]}" || continue
(( task5b_fixture_processes += 1 ))
done
printf -v "$destination_name" \
'mounts_sda3=%s\nmounts_outer=%s\nmounts_inner=%s\nouter_mount_path=%s\ninner_mount_path=%s\nrecovery_loops=%s\nmapper_main=%s\nmapper_proof=%s\nreserved_root=%s\ntask5b_temp_residue=%s\ntask5b_fixture_processes=%s' \
"$mounts_sda3" "$mounts_outer" "$mounts_inner" "$outer_mount_path" \
"$inner_mount_path" "$recovery_loops" "$mapper_main" "$mapper_proof" \
"$reserved_root" "$task5b_temp_residue" "$task5b_fixture_processes"
}
task5b_gate0_review_round1_adversarial_assertions() {
local mount_record mounts_sda3=0 mounts_outer=0 mounts_inner=0 path_state=''
local selector_output='' selector_rc=0
mount_record=$'36 25 8:3 / /mnt/k3s-recovery-ssd rw - ntfs3 /dev/sda3 rw\n37 25 253:0 / /srv/recovery/k3s rw - ext4 /dev/mapper/k3s-recovery rw\n'
assert_succeeds task5b_mountinfo_counts_record "$mount_record" mounts_sda3 \
mounts_outer mounts_inner
assert_eq 1 "$mounts_sda3" 'collector parses exact /dev/sda3 source'
assert_eq 1 "$mounts_outer" 'collector parses exact outer mountpoint'
assert_eq 1 "$mounts_inner" 'collector parses exact inner mountpoint'
assert_succeeds task5b_loop_backing_is_recovery /fixture/recovery-partition
assert_succeeds task5b_loop_backing_is_recovery \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks
assert_fails task5b_loop_backing_is_recovery /dev/sda3
assert_fails task5b_loop_backing_is_recovery /other/recovery-partition
assert_fails task5b_loop_backing_is_recovery \
/mnt/k3s-recovery-ssd/HyeonworksRecovery/containers/k3s-recovery.luks.bak
assert_succeeds task5b_path_state_from_observation path_state 0 0
assert_eq absent "$path_state" 'collector treats only nonexistence and nonsymlink as absent'
assert_succeeds task5b_path_state_from_observation path_state 0 1
assert_eq present "$path_state" 'collector treats a dangling symlink as present'
assert_succeeds task5b_path_state_from_observation path_state 1 0
assert_eq present "$path_state" 'collector treats an existing path as present'
assert_fails task5b_fixture_process_argv_matches 100 100 /usr/bin/bash \
scripts/validate/test-k3s-local-recovery.sh
assert_succeeds task5b_fixture_process_argv_matches 100 101 /usr/bin/bash \
scripts/validate/test-k3s-local-recovery.sh
assert_succeeds task5b_fixture_process_argv_matches 100 102 /bin/bash -c : \
task5b-fixture:gate0
assert_fails task5b_fixture_process_argv_matches 100 103 /usr/bin/bash -c :
assert_fails task5b_fixture_process_argv_matches 100 104 /usr/bin/sleep 10 \
task5b-fixture:gate0
selector_output="$(
TASK5B_FOCUS=not-a-task5b-focus TASK5A_FOCUS=dispatcher \
/usr/bin/timeout 10s /usr/bin/bash "${BASH_SOURCE[0]}" 2>&1
)" || selector_rc=$?
assert_eq 2 "$selector_rc" 'unknown Task 5B focus is rejected before legacy selection'
assert_eq 'TEST FAILURE: unsupported TASK5B_FOCUS: not-a-task5b-focus' \
"$selector_output" 'unknown Task 5B focus emits only the early selector diagnostic'
}
task5b_gate0_review_round2_adversarial_assertions() {
local record='' match_count=0 mutated_helper_body='' fuse_body=''
record='/dev/loop37|4242|8:3|7:37|0|0|99|99|99|\x2ffixture\x2frecovery.luks'
assert_succeeds task5b_loop_identity_count_from_record "$record" 8:3 \
match_count
assert_eq 1 "$match_count" 'loop identity parser counts exact backing MAJ:MIN'
record='/dev/loop37|4242|8:33|7:37|0|0|99|99|99|\x2froot\x2fsnap'
assert_succeeds task5b_loop_identity_count_from_record "$record" 8:3 \
match_count
assert_eq 0 "$match_count" 'loop identity parser rejects a nonmatching device'
record='/dev/loop37|4242|8:3|7:37|0|0|99|99|99|\x2fdeleted\x20backing'
assert_succeeds task5b_loop_identity_count_from_record "$record" 8:3 \
match_count
assert_eq 1 "$match_count" \
'deleted or missing backing pathname cannot hide matching device identity'
assert_fails task5b_loop_identity_count_from_record \
'/dev/loop37|4242|8:3|7:37|0|0|99|99' 8:3 match_count
assert_fails task5b_loop_identity_count_from_record \
'/dev/loop37|4242|8:3|7:37|0|0|99|98|99|\x2fstale' 8:3 match_count
record=$'/dev/loop37|4242|8:3|7:37|0|0|99|99|99|\\x2fone\n/dev/loop37|4243|8:33|7:37|0|0|99|99|99|\\x2ftwo'
assert_fails task5b_loop_identity_count_from_record "$record" 8:3 match_count
mutated_helper_body="$(declare -f task5b_gate0_assert_rejected)"$'\n'\
'trap - DEBUG
/usr/bin/printf TASK5B_TRAP_DISABLE_CANARY' || return 1
assert_fails task5b_gate0_static_body_is_closed "$mutated_helper_body"
fuse_body="$(declare -f task5b_gate0_real_exec_fuse)" || return 1
[[ "$fuse_body" != *'|trap)'* ]] ||
fail 'active fuse must not allowlist DEBUG-trap teardown'
}
task5b_api_state_trimmed_line_count() {
local body="${1-}" expected_line="${2-}" line count=0
(( $# == 2 )) || return 1
while IFS= read -r line; do
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ "$line" != "$expected_line" ]] || (( count += 1 ))
done <<<"$body"
printf '%s\n' "$count"
}
task5b_api_state_dispatcher_has_obsolete_provider_flow() {
local body="${1-}" line
local provider_token_re='("\$header_provider"|\$header_provider|"\$\{header_provider\}"|\$\{header_provider\})'
local direct_start_re='' control_provider_re='' separator_provider_re=''
(( $# == 1 )) || return 1
direct_start_re="^[[:space:]]*${provider_token_re}([[:space:];|&]|$)"
control_provider_re="(^|[[:space:]])(if|then|elif|while|until|!)[[:space:]]+${provider_token_re}([[:space:];|&]|$)"
separator_provider_re="(&&|\|\||;)[[:space:]]*${provider_token_re}([[:space:];|&]|$)"
while IFS= read -r line; do
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ "$line" != *'${@:3}'* && "$line" != *provider_arguments* ]] ||
return 0
[[ ! "$line" =~ $direct_start_re &&
! "$line" =~ $control_provider_re &&
! "$line" =~ $separator_provider_re ]] || return 0
if [[ "$line" == *'$header_provider'* ||
"$line" == *'${header_provider}'* ]]; then
case "$line" in
action_arguments=*|action_arguments+=*|_k3slr_lifecycle_action[[:space:]]*)
return 0
;;
esac
[[ "$line" =~ ^[[:alnum:]_]*arguments(\+)?= ]] && return 0
fi
done <<<"$body"
return 1
}
# The production breaks this catches are the obsolete caller-provider argv
# forwarding API, a prepare action that still accepts two arguments, or state
# created anywhere except one empty dispatcher-owned associative declaration.
# The production dispatcher/action are observed directly and are never
# replaced by a fixture success stub.
task5b_api_state_contract() (
local dispatch_body='' action_body='' main_body='' library_source=''
local state_declaration_count=0 state_append_count=0 action_call_count=0
local main_dispatch_count=0 state_reference_count=0
local hook_calls=0 action_calls=0 hook_calls_exact=0 action_calls_exact=0
local command_calls=0 command_calls_exact=0 command_calls_direct=0
local tty_calls=0 provider_calls=0 action_rc=0
local dispatch_exact_actual_exec=0 command_direct_actual_exec=0
local main_arity_actual_exec=0 wrong_action_actual_exec=0
local dispatch_exact_rc=0 dispatch_invalid_rc=0 dispatch_case='' index=0
local main_wrong_arity_successes=0 main_contract_sentinel=not-loaded
local probe_rc=0 record='' record_tag='' record_profile='' record_extra=''
local caller_value=''
local -a invalid_dispatch_cases=(zero one prepare-third open-third close-four)
local -a invalid_dispatch_rcs=() invalid_dispatch_hook_calls=()
local -a invalid_dispatch_action_calls=() invalid_dispatch_command_calls=()
local -a invalid_dispatch_actual_exec=()
local -a wrong_action_rcs=() record_fields=()
local -A caller_seed=([caller_provider_argument]=must-not-be-read)
# shellcheck source=/dev/null
source "$LIBRARY_PATH"
_k3slr_command() { (( command_calls += 1 )); return 97; }
_k3slr_tty_capability() { (( tty_calls += 1 )); return 1; }
_k3slr_read_tty_line() { (( tty_calls += 1 )); return 1; }
task5b_api_state_header_provider() { (( provider_calls += 1 )); return 97; }
task5b_api_state_debug_observer() {
local observed_command="${1-}"
(( $# == 1 )) || return 1
case "$observed_command" in
_k3slr_lifecycle_step\ *) (( hook_calls += 1 )) ;;
_k3slr_lifecycle_action\ *) (( action_calls += 1 )) ;;
esac
}
task5b_api_dispatch_probe_payload() {
local dispatch_case="${1-}" dispatch_rc=0 hook_calls=0 action_calls=0
local command_calls=0 provider_calls=0
local -A caller_seed=([caller_provider_argument]=must-not-be-read)
(( $# == 1 )) || return 1
case "$dispatch_case" in
exact)
_k3slr_lifecycle_dispatch prepare task5b_api_state_header_provider ||
dispatch_rc=$?
;;
zero) _k3slr_lifecycle_dispatch || dispatch_rc=$? ;;
one) _k3slr_lifecycle_dispatch prepare || dispatch_rc=$? ;;
prepare-third)
_k3slr_lifecycle_dispatch prepare task5b_api_state_header_provider \
caller_seed || dispatch_rc=$?
;;
open-third)
_k3slr_lifecycle_dispatch open task5b_api_state_header_provider \
caller_seed || dispatch_rc=$?
;;
close-four)
_k3slr_lifecycle_dispatch close task5b_api_state_header_provider \
caller_seed extra || dispatch_rc=$?
;;
*) return 1 ;;
esac
task5b_production_probe_emit_ok api_dispatch "$dispatch_rc" "$hook_calls" \
"$action_calls" "$command_calls" "$provider_calls" \
"${caller_seed[caller_provider_argument]}"
}
task5b_api_direct_probe_payload() {
local direct_case="${1-}" action_rc=0 command_calls=0
local -a wrong_action_rcs=()
(( $# == 1 )) || return 1
case "$direct_case" in
exact)
_k3slr_lifecycle_action prepare sudo _k3slr_prepare_invocation ||
action_rc=$?
task5b_production_probe_emit_ok api_direct "$action_rc" "$command_calls"
;;
wrong-arities)
_k3slr_lifecycle_action || action_rc=$?
wrong_action_rcs+=("$action_rc")
action_rc=0
_k3slr_lifecycle_action prepare || action_rc=$?
wrong_action_rcs+=("$action_rc")
action_rc=0
_k3slr_lifecycle_action prepare sudo || action_rc=$?
wrong_action_rcs+=("$action_rc")
action_rc=0
_k3slr_lifecycle_action prepare sudo _k3slr_prepare_invocation extra ||
action_rc=$?
wrong_action_rcs+=("$action_rc")
action_rc=0
_k3slr_lifecycle_action open || action_rc=$?
wrong_action_rcs+=("$action_rc")
action_rc=0
_k3slr_lifecycle_action open sudo extra || action_rc=$?
wrong_action_rcs+=("$action_rc")
action_rc=0
_k3slr_lifecycle_action close || action_rc=$?
wrong_action_rcs+=("$action_rc")
action_rc=0
_k3slr_lifecycle_action close sudo extra || action_rc=$?
wrong_action_rcs+=("$action_rc")
task5b_production_probe_emit_ok api_direct "$command_calls" \
"${wrong_action_rcs[@]}"
;;
*) return 1 ;;
esac
}
task5b_api_main_arity_probe_payload() {
local main_wrong_arity_successes=0
(( $# == 0 )) || return 1
_k3slr_lifecycle_main && (( main_wrong_arity_successes += 1 ))
_k3slr_lifecycle_main prepare && (( main_wrong_arity_successes += 1 ))
_k3slr_lifecycle_main prepare execute caller_seed &&
(( main_wrong_arity_successes += 1 ))
task5b_production_probe_emit_ok api_main_arity \
"$main_wrong_arity_successes" "$K3SLR_SCHEMA_VERSION"
}
task5b_production_probe_adversary_matrix || return 1
task5b_production_probe_static_graph_is_closed api_dispatch || return 1
task5b_production_probe_static_graph_is_closed api_direct || return 1
task5b_production_probe_static_graph_is_closed api_main_arity || return 1
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm api_dispatch || return 1
task5b_api_dispatch_probe_payload exact
)" || probe_rc=$?
assert_eq 0 "$probe_rc" 'exact dispatcher probe completes without a fuse block'
IFS='|' read -r record_tag record_profile dispatch_exact_rc hook_calls_exact \
action_calls_exact command_calls_exact provider_calls caller_value \
record_extra <<<"$record" || return 1
[[ "$record_tag" == TASK5B_PROBE_OK && "$record_profile" == api_dispatch &&
-z "$record_extra" ]] || return 1
assert_eq 0 "$provider_calls" 'exact dispatcher does not call its provider'
assert_eq must-not-be-read "$caller_value" \
'exact dispatcher leaves caller-owned state unchanged'
for dispatch_case in "${invalid_dispatch_cases[@]}"; do
record=''
probe_rc=0
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm api_dispatch || return 1
task5b_api_dispatch_probe_payload "$dispatch_case"
)" || probe_rc=$?
assert_eq 0 "$probe_rc" \
"wrong dispatcher arity probe completes without a fuse block: $dispatch_case"
IFS='|' read -r record_tag record_profile dispatch_invalid_rc hook_calls \
action_calls command_calls provider_calls caller_value record_extra \
<<<"$record" || return 1
[[ "$record_tag" == TASK5B_PROBE_OK && "$record_profile" == api_dispatch &&
-z "$record_extra" ]] || return 1
invalid_dispatch_rcs+=("$dispatch_invalid_rc")
invalid_dispatch_hook_calls+=("$hook_calls")
invalid_dispatch_action_calls+=("$action_calls")
invalid_dispatch_command_calls+=("$command_calls")
invalid_dispatch_actual_exec+=(0)
assert_eq 0 "$provider_calls" \
"wrong dispatcher arity calls no provider: $dispatch_case"
assert_eq must-not-be-read "$caller_value" \
"wrong dispatcher arity leaves caller state unchanged: $dispatch_case"
done
record=''
probe_rc=0
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm api_direct || return 1
task5b_api_direct_probe_payload exact
)" || probe_rc=$?
assert_eq 0 "$probe_rc" 'exact direct Action 1 probe completes without a fuse block'
IFS='|' read -r record_tag record_profile action_rc command_calls_direct \
record_extra <<<"$record" || return 1
[[ "$record_tag" == TASK5B_PROBE_OK && "$record_profile" == api_direct &&
-z "$record_extra" ]] || return 1
K3SLR_WRAPPER_CONTRACT="$CONTRACT_PATH"
K3SLR_SCHEMA_VERSION="$main_contract_sentinel"
record=''
probe_rc=0
record="$(
K3SLR_WRAPPER_CONTRACT="$CONTRACT_PATH"
K3SLR_SCHEMA_VERSION="$main_contract_sentinel"
exec 9>&1
exec 2>&1
task5b_production_probe_arm api_main_arity || return 1
task5b_api_main_arity_probe_payload
)" || probe_rc=$?
assert_eq 0 "$probe_rc" 'wrong main arity probe completes without a fuse block'
IFS='|' read -r record_tag record_profile main_wrong_arity_successes \
K3SLR_SCHEMA_VERSION record_extra <<<"$record" || return 1
[[ "$record_tag" == TASK5B_PROBE_OK &&
"$record_profile" == api_main_arity && -z "$record_extra" ]] || return 1
dispatch_body="$(declare -f _k3slr_lifecycle_dispatch)" || return 1
action_body="$(declare -f _k3slr_lifecycle_action)" || return 1
main_body="$(declare -f _k3slr_lifecycle_main)" || return 1
library_source="$(<"$LIBRARY_PATH")" || return 1
state_declaration_count="$(task5b_api_state_trimmed_line_count "$dispatch_body" \
'local -A _k3slr_prepare_invocation=();')" || return 1
state_append_count="$(task5b_api_state_trimmed_line_count "$dispatch_body" \
'action_arguments+=(_k3slr_prepare_invocation);')" || return 1
action_call_count="$(task5b_api_state_trimmed_line_count "$dispatch_body" \
'_k3slr_lifecycle_action "${action_arguments[@]}" || return 1;')" || return 1
main_dispatch_count="$(task5b_api_state_trimmed_line_count "$main_body" \
'_k3slr_lifecycle_dispatch "$lifecycle" _k3slr_prove_header_restore')" || return 1
while IFS= read -r line; do
[[ "$line" != *'_k3slr_prepare_invocation'* ]] ||
(( state_reference_count += 1 ))
done <<<"$library_source"
(( hook_calls_exact > 0 )) ||
fail 'exact two-argument prepare dispatcher did not reach its production hook'
(( action_calls_exact > 0 )) ||
fail 'exact two-argument prepare dispatcher did not reach its production action'
(( dispatch_exact_rc != 0 )) ||
fail 'API/state checkpoint production dispatcher is not fail-closed'
for index in "${!invalid_dispatch_cases[@]}"; do
dispatch_case="${invalid_dispatch_cases[$index]}"
(( invalid_dispatch_rcs[index] != 0 )) ||
fail "wrong dispatcher arity succeeded: $dispatch_case"
assert_eq 0 "${invalid_dispatch_hook_calls[$index]}" \
"wrong dispatcher arity reached production hook: $dispatch_case"
assert_eq 0 "${invalid_dispatch_action_calls[$index]}" \
"wrong dispatcher arity reached production action: $dispatch_case"
assert_eq 0 "${invalid_dispatch_command_calls[$index]}" \
"wrong dispatcher arity reached the command seam: $dispatch_case"
assert_eq 0 "${invalid_dispatch_actual_exec[$index]}" \
"wrong dispatcher arity executes no real command: $dispatch_case"
done
assert_eq must-not-be-read "${caller_seed[caller_provider_argument]}" \
'rejected caller state/provider argument remains unchanged'
assert_eq 1 "$command_calls_exact" \
'exact dispatcher reaches the safe Action 1 command fuse once'
assert_eq 1 "$command_calls_direct" \
'exact direct Action 1 reaches the safe command fuse once'
assert_eq 0 "$dispatch_exact_actual_exec" \
'exact dispatcher executes no real command'
assert_eq 0 "$command_direct_actual_exec" \
'exact direct Action 1 executes no real command'
assert_eq 0 "$main_arity_actual_exec" \
'wrong main arity executes no real command'
assert_eq 0 "$tty_calls" 'API/state arity matrix has TTY effect 0'
assert_eq 0 "$provider_calls" 'API/state arity matrix has provider effect 0'
assert_eq 97 "$action_rc" \
'exact three-argument Action 1 preserves the safe command-fuse failure'
assert_eq 0 "$main_wrong_arity_successes" \
'main rejects zero, one, and three arguments'
assert_eq not-loaded "$K3SLR_SCHEMA_VERSION" \
'wrong main arity fails before contract load effects'
assert_eq 1 "$state_declaration_count" \
'dispatcher has one direct empty prepare associative-state declaration'
assert_eq 1 "$state_append_count" \
'dispatcher appends the fixed prepare-state name once to action argv'
assert_eq 1 "$action_call_count" \
'dispatcher has one production action invocation site'
assert_eq 1 "$main_dispatch_count" \
'main has one exact two-argument dispatcher invocation site'
assert_eq 3 "$state_reference_count" \
'production has only the prepare-state declaration, fixed action carrier, and Action 1 literal guard'
assert_fails task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''${@:3}'
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''provider_arguments=("$header_provider")'
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''"$header_provider" restore'
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''if "$header_provider"; then :; fi'
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n'': && "$header_provider" restore'
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''! "$header_provider" restore'
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n'': || "$header_provider" restore'
assert_succeeds task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''action_arguments+=("$header_provider")'
assert_fails task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''[[ "$header_provider" == _k3slr_prove_header_restore ]] || return 1'
assert_fails task5b_api_state_dispatcher_has_obsolete_provider_flow \
"$dispatch_body"$'\n''declare -F "$header_provider" >/dev/null || return 1'
[[ "$dispatch_body" == *'header_provider="${2-}"'* ]] ||
fail 'dispatcher invocation-local provider identity binding is absent'
[[ "$dispatch_body" != *'${@:3}'* && "$dispatch_body" != *provider_arguments* ]] ||
fail 'dispatcher retains obsolete caller provider argument compatibility'
[[ "$dispatch_body" == *'(( $# == 2 )) || return 1'* ]] ||
fail 'dispatcher exact two-argument arity guard is absent'
[[ "$action_body" == *'prepare)'*'(( $# == 3 )) || return 1'* &&
"$action_body" == *'open | close)'*'(( $# == 2 )) || return 1'* ]] ||
fail 'production action handler retains obsolete lifecycle arity'
[[ "$main_body" != *'_k3slr_prepare_invocation'* &&
"$main_body" != *'${@:3}'* ]] ||
fail 'main creates or forwards prepare invocation state'
[[ "$main_body" == *'(( $# == 2 )) || return 1'* ]] ||
fail 'main exact two-argument arity guard is absent'
[[ "${main_body%%_k3slr_load_contract*}" == *'(( $# == 2 )) || return 1'* ]] ||
fail 'main arity guard does not precede contract effects'
record=''
probe_rc=0
record="$(
exec 9>&1
exec 2>&1
task5b_production_probe_arm api_direct || return 1
task5b_api_direct_probe_payload wrong-arities
)" || probe_rc=$?
assert_eq 0 "$probe_rc" 'wrong action arity probe completes without a fuse block'
IFS='|' read -r -a record_fields <<<"$record" || return 1
record_tag="${record_fields[0]-}"
record_profile="${record_fields[1]-}"
command_calls="${record_fields[2]-}"
wrong_action_rcs=("${record_fields[@]:3}")
[[ "$record_tag" == TASK5B_PROBE_OK && "$record_profile" == api_direct &&
${#wrong_action_rcs[@]} == 8 ]] || return 1
for action_rc in "${wrong_action_rcs[@]}"; do
(( action_rc != 0 )) || fail 'wrong action arity unexpectedly succeeded'
done
assert_eq 0 "$command_calls" 'wrong action/dispatcher arity has command effect 0'
assert_eq 0 "$wrong_action_actual_exec" \
'wrong action/dispatcher arity has no fuse block'
assert_eq 0 "$tty_calls" 'wrong action/dispatcher arity has TTY effect 0'
assert_eq 0 "$provider_calls" 'wrong action/dispatcher arity has provider effect 0'
)
# The production breaks this catches are a high-level TTY gate that bypasses
# the low capability seam, fails to preserve its status, or reaches it after a
# wrong-arity call. Only the lowest seam is replaced in this source-loaded
# foreground subshell.
task5b_tty_seam_contract() (
local tty_capability_calls=0 tty_capability_rc=0 high_rc=0
# shellcheck source=/dev/null
source "$LIBRARY_PATH"
_k3slr_tty_capability() {
(( tty_capability_calls += 1 ))
return "$tty_capability_rc"
}
high_rc=0
_k3slr_require_execute_tty || high_rc=$?
assert_eq 1 "$tty_capability_calls" \
'TTY high helper delegates exactly once on low-capability success'
assert_eq 0 "$high_rc" \
'TTY high helper preserves low-capability success'
tty_capability_calls=0
tty_capability_rc=73
high_rc=0
_k3slr_require_execute_tty || high_rc=$?
assert_eq 1 "$tty_capability_calls" \
'TTY high helper delegates exactly once on low-capability failure'
assert_eq 73 "$high_rc" \
'TTY high helper preserves low-capability failure'
tty_capability_calls=0
tty_capability_rc=0
high_rc=0
_k3slr_require_execute_tty unexpected || high_rc=$?
assert_eq 1 "$high_rc" 'TTY high helper rejects wrong arity'
assert_eq 0 "$tty_capability_calls" \
'TTY high helper rejects wrong arity before the low seam'
)
task5b_action1_literal_line_count() {
local body="${1-}" needle="${2-}" destination_name="${3-}" line count=0
(( $# == 3 )) || return 1
[[ -n "$needle" && "$destination_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] ||
return 1
while IFS= read -r line; do
[[ "$line" != *"$needle"* ]] || (( count += 1 ))
done <<<"$body"
printf -v "$destination_name" '%s' "$count"
}
# Every literal direct prepare/sudo test call must live in a fixture whose
# command seam fails closed or in the Action 1 exact matcher. The historical
# unavailable-action loop is indirect, so it has its own command fuse and must
# no longer enumerate the now-available prepare/sudo action.
task5b_action1_nonfocused_direct_sudo_paths_are_fused() {
local direct_needle='' test_source='' safe_bodies=''
local dryrun_body='' api_state_body='' action1_body='' unavailable_body=''
local source_count=0 safe_count=0
direct_needle='_k3slr_lifecycle_action prepare '"sudo"
test_source="$(<"${BASH_SOURCE[0]}")" || return 1
dryrun_body="$(declare -f task5a_dry_run_and_prepackage_action_binding)" ||
return 1
api_state_body="$(declare -f task5b_api_state_contract)" || return 1
action1_body="$(declare -f task5b_action1_command_binding_contract)" ||
return 1
unavailable_body="$(declare -f task5a_production_actions_fail_closed)" ||
return 1
safe_bodies="$dryrun_body"$'\n'"$api_state_body"$'\n'"$action1_body"
task5b_action1_literal_line_count "$test_source" "$direct_needle" \
source_count || return 1
task5b_action1_literal_line_count "$safe_bodies" "$direct_needle" \
safe_count || return 1
(( source_count > 0 && source_count == safe_count )) || return 1
[[ "$dryrun_body" == *'_k3slr_command ()'* &&
"$dryrun_body" == *'return 97'* &&
"$dryrun_body" == *'task5b_production_probe_arm legacy_binding'* ]] ||
return 1
[[ "$api_state_body" == *'_k3slr_command ()'* &&
"$api_state_body" == *'return 97'* &&
"$api_state_body" == *'task5b_production_probe_arm api_direct'* ]] ||
return 1
[[ "$action1_body" == *'task5b_gate0_route_equals sudo-validate "$@"'* &&
"$action1_body" == *'return 97'* &&
"$action1_body" == *'task5b_production_probe_arm action1_main'* &&
"$action1_body" == *'task5b_production_probe_arm action1_alternate'* ]] ||
return 1
[[ "$unavailable_body" == *'_k3slr_command ()'* &&
"$unavailable_body" == *'return 97'* &&
"$unavailable_body" == *'task5b_production_probe_arm unavailable'* &&
"$unavailable_body" != *'prepare_unavailable=(sudo'* ]] || return 1
}
# The production breaks this catches are a prepare/sudo handler that omits or
# misframes the exact sudo validation argv, reaches a command after sudo, calls
# the header provider early, or accepts any state name except the dispatcher-
# owned literal. The real main, dispatcher, hook, action, and invocation state
# remain untouched; only the lowest TTY and command seams are replaced.
task5b_action1_command_binding_contract() (
local tty_calls=0 tty_rc=0 sudo_dispatches=0 sudo_rc=0
local unexpected_commands=0 actual_exec=0 provider_calls=0 main_rc=0
local hook_sudo_entries=0 hook_context_entries=0 hook_suffix_entries=0
local action_sudo_entries=0 action_context_entries=0 action_suffix_entries=0
local action_rc=0 command_body=''
local probe_record='' record_tag='' record_profile='' record_extra=''
# shellcheck source=/dev/null
source "$LIBRARY_PATH"
K3SLR_WRAPPER_CONTRACT="$CONTRACT_PATH"
_k3slr_prove_header_restore() {
(( provider_calls += 1 ))
return 97
}
_k3slr_tty_capability() {
(( tty_calls += 1 ))
return "$tty_rc"
}
_k3slr_command() {
if task5b_gate0_route_equals sudo-validate "$@"; then
(( sudo_dispatches += 1 ))
return "$sudo_rc"
fi
(( unexpected_commands += 1 ))
return 97
}
task5b_action1_debug_observer() {
local observed_command="${1-}" observed_step="${2-}"
(( $# == 2 )) || return 1
case "$observed_command" in
_k3slr_lifecycle_step\ *)
case "$observed_step" in
sudo) (( hook_sudo_entries += 1 )) ;;
context) (( hook_context_entries += 1 )) ;;
*) (( hook_suffix_entries += 1 )) ;;
esac
;;
_k3slr_lifecycle_action\ *)
case "$observed_step" in
sudo) (( action_sudo_entries += 1 )) ;;
context) (( action_context_entries += 1 )) ;;
*) (( action_suffix_entries += 1 )) ;;
esac
;;
esac
}
task5b_action1_main_probe_payload() {
local probe_rc=0 tty_calls=0 sudo_dispatches=0 unexpected_commands=0
local provider_calls=0 hook_sudo_entries=0 hook_context_entries=0
local hook_suffix_entries=0 action_sudo_entries=0
local action_context_entries=0 action_suffix_entries=0
(( $# == 0 )) || return 1
_k3slr_lifecycle_main prepare execute || probe_rc=$?
task5b_production_probe_emit_ok action1_main "$probe_rc" "$tty_calls" \
"$sudo_dispatches" "$unexpected_commands" "$provider_calls" \
"$hook_sudo_entries" "$hook_context_entries" "$hook_suffix_entries" \
"$action_sudo_entries" "$action_context_entries" \
"$action_suffix_entries"
}
task5b_action1_alternate_probe_payload() {
local probe_rc=0 tty_calls=0 sudo_dispatches=0 unexpected_commands=0
local provider_calls=0 hook_sudo_entries=0 hook_context_entries=0
local hook_suffix_entries=0 action_sudo_entries=0
local action_context_entries=0 action_suffix_entries=0
(( $# == 0 )) || return 1
_k3slr_lifecycle_action prepare sudo task5b_alternate_state || probe_rc=$?
task5b_production_probe_emit_ok action1_alternate "$probe_rc" \
"$tty_calls" "$sudo_dispatches" "$unexpected_commands" \
"$provider_calls" "$hook_sudo_entries" "$hook_context_entries" \
"$hook_suffix_entries" "$action_sudo_entries" \
"$action_context_entries" "$action_suffix_entries"
}
task5b_action1_run_probe() {
local probe_kind="${1-}" probe_profile=''
local guarded_rc=0
(( $# == 1 )) || return 1
case "$probe_kind" in
main) probe_profile=action1_main ;;
alternate) probe_profile=action1_alternate ;;
*) return 1 ;;
esac
case "$probe_kind" in
main)
probe_record="$(
local task5b_probe_profile=''
exec 9>&1
exec 2>&1
task5b_production_probe_arm action1_main || return 1
task5b_action1_main_probe_payload
)" || guarded_rc=$?
;;
alternate)
probe_record="$(
local task5b_probe_profile=''
exec 9>&1
exec 2>&1
task5b_production_probe_arm action1_alternate || return 1
task5b_action1_alternate_probe_payload
)" || guarded_rc=$?
;;
esac
if (( guarded_rc != 0 )); then
return "$guarded_rc"
fi
record_tag=''
record_extra=''
IFS='|' read -r record_tag record_profile action_rc tty_calls \
sudo_dispatches unexpected_commands provider_calls hook_sudo_entries \
hook_context_entries hook_suffix_entries action_sudo_entries \
action_context_entries action_suffix_entries record_extra \
<<<"$probe_record" || return 1
[[ "$record_tag" == TASK5B_PROBE_OK &&
"$record_profile" == "$probe_profile" && -z "$record_extra" ]] ||
fail "Action 1 $probe_kind model keeps combined stdout and stderr empty"
actual_exec=0
}
task5b_action1_run_main() {
task5b_action1_run_probe main || return 1
main_rc="$action_rc"
}
task5b_production_probe_adversary_matrix || {
printf 'TEST FAILURE: Action 1 production-probe adversary matrix failed\n' >&2
return 1
}
task5b_production_probe_static_graph_is_closed action1_main || {
printf 'TEST FAILURE: Action 1 main exact graph scan failed\n' >&2
return 1
}
task5b_production_probe_static_graph_is_closed action1_alternate || {
printf 'TEST FAILURE: Action 1 alternate exact graph scan failed\n' >&2
return 1
}
task5b_action1_run_main || {
printf 'TEST FAILURE: Action 1 main guarded probe did not emit an OK record\n' >&2
return 1
}
(( main_rc != 0 )) || fail 'Action 1 success model stops at pending context'
assert_eq 1 "$tty_calls" 'Action 1 full main reaches the low TTY seam once'
(( action_sudo_entries > 0 )) ||
fail 'Action 1 full main reaches the production prepare/sudo handler'
assert_eq 1 "$sudo_dispatches" \
'Action 1 production handler dispatches exact sudo validation once'
(( hook_sudo_entries > 0 )) ||
fail 'Action 1 success model records the sudo lifecycle hook entry'
(( hook_context_entries > 0 )) ||
fail 'Action 1 success model reaches the pending context hook entry'
(( action_context_entries > 0 )) ||
fail 'Action 1 success model reaches the pending context action entry'
assert_eq 0 "$hook_suffix_entries" \
'Action 1 success model reaches no hook after pending context'
assert_eq 0 "$action_suffix_entries" \
'Action 1 success model reaches no action after pending context'
assert_eq 0 "$unexpected_commands" \
'Action 1 success model dispatches no command after sudo validation'
assert_eq 0 "$actual_exec" 'Action 1 success model executes no real command'
assert_eq 0 "$provider_calls" 'Action 1 success model does not call the provider'
command_body="$(declare -f _k3slr_command)" || return 1
assert_succeeds task5b_gate0_static_body_is_closed "$command_body"
assert_succeeds task5b_action1_nonfocused_direct_sudo_paths_are_fused
tty_calls=0
tty_rc=71
sudo_dispatches=0
sudo_rc=0
unexpected_commands=0
provider_calls=0
hook_sudo_entries=0
hook_context_entries=0
hook_suffix_entries=0
action_sudo_entries=0
action_context_entries=0
action_suffix_entries=0
task5b_action1_run_main
(( main_rc != 0 )) || fail 'Action 1 rejects a failed low TTY capability'
assert_eq 1 "$tty_calls" 'Action 1 TTY failure calls the low seam once'
assert_eq 0 "$hook_sudo_entries" 'Action 1 TTY failure reaches no dispatcher hook'
assert_eq 0 "$action_sudo_entries" 'Action 1 TTY failure reaches no action'
assert_eq 0 "$sudo_dispatches" 'Action 1 TTY failure dispatches no sudo command'
assert_eq 0 "$unexpected_commands" 'Action 1 TTY failure dispatches no command'
assert_eq 0 "$actual_exec" 'Action 1 TTY failure executes no real command'
assert_eq 0 "$provider_calls" 'Action 1 TTY failure calls no provider'
tty_calls=0
tty_rc=0
sudo_dispatches=0
sudo_rc=97
unexpected_commands=0
provider_calls=0
hook_sudo_entries=0
hook_context_entries=0
hook_suffix_entries=0
action_sudo_entries=0
action_context_entries=0
action_suffix_entries=0
task5b_action1_run_main
(( main_rc != 0 )) || fail 'Action 1 propagates modeled sudo failure'
assert_eq 1 "$tty_calls" 'Action 1 sudo failure calls the low TTY seam once'
(( hook_sudo_entries > 0 )) ||
fail 'Action 1 sudo failure reaches the sudo hook entry'
(( action_sudo_entries > 0 )) ||
fail 'Action 1 sudo failure reaches the production sudo action entry'
assert_eq 1 "$sudo_dispatches" \
'Action 1 sudo failure reaches the exact sudo model once'
assert_eq 0 "$hook_context_entries" \
'Action 1 sudo failure reaches no context hook'
assert_eq 0 "$action_context_entries" \
'Action 1 sudo failure reaches no context action'
assert_eq 0 "$hook_suffix_entries" 'Action 1 sudo failure reaches no suffix hook'
assert_eq 0 "$action_suffix_entries" \
'Action 1 sudo failure reaches no suffix action'
assert_eq 0 "$unexpected_commands" \
'Action 1 sudo failure dispatches no unexpected command'
assert_eq 0 "$actual_exec" 'Action 1 sudo failure executes no real command'
assert_eq 0 "$provider_calls" 'Action 1 sudo failure calls no provider'
task5b_action1_run_probe alternate
assert_eq 1 "$action_rc" 'Action 1 rejects an alternate exact-three state name'
assert_eq 0 "$sudo_dispatches" \
'Action 1 alternate state name dispatches no sudo command'
assert_eq 0 "$unexpected_commands" \
'Action 1 alternate state name dispatches no unexpected command'
assert_eq 0 "$actual_exec" 'Action 1 alternate state executes no real command'
assert_eq 0 "$provider_calls" 'Action 1 alternate state calls no provider'
)
task5b_run_focus() {
local focus="${1-}" expected_record before_record after_record focus_rc=0
(( $# == 1 )) || return 1
task5b_expected_safe_live_state_record expected_record || return 1
task5b_live_state_record before_record || return 1
assert_eq "$expected_record" "$before_record" \
"Task 5B $focus pre-snapshot is independently all-zero/all-absent" || return 1
task5b_gate0_contract || return 1
case "$focus" in
gate0)
task5b_gate0_review_round1_adversarial_assertions || focus_rc=$?
if (( focus_rc == 0 )); then
task5b_gate0_review_round2_adversarial_assertions || focus_rc=$?
fi
;;
api_state)
task5b_api_state_contract || focus_rc=$?
;;
tty_seam)
task5b_tty_seam_contract || focus_rc=$?
;;
action1)
task5b_action1_command_binding_contract || focus_rc=$?
;;
*) focus_rc=1 ;;
esac
task5b_gate0_contract || return 1
task5b_live_state_record after_record || return 1
assert_eq "$expected_record" "$after_record" \
"Task 5B $focus post-snapshot is independently all-zero/all-absent" || return 1
assert_eq "$before_record" "$after_record" \
"Task 5B $focus focus preserves promptless live state/process/temp record" || return 1
(( focus_rc == 0 )) || return "$focus_rc"
}
if [[ "${TASK5B_FOCUS:-}" == gate0 || "${TASK5B_FOCUS:-}" == api_state ||
"${TASK5B_FOCUS:-}" == tty_seam || "${TASK5B_FOCUS:-}" == action1 ]]; then
assert_succeeds task5b_run_focus "$TASK5B_FOCUS"
elif [[ "${TASK5A_FOCUS:-}" == wrappers ]]; then
assert_succeeds task5a_wrapper_and_guard_contract
elif [[ "${TASK5A_FOCUS:-}" == capabilities ]]; then
assert_succeeds task5a_prepare_provider_capability_gate
assert_succeeds task5a_open_close_capability_gate
elif [[ "${TASK5A_FOCUS:-}" == dispatcher ]]; then
assert_succeeds task5a_lifecycle_dispatch_contract
assert_succeeds task5a_production_actions_fail_closed
elif [[ "${TASK5A_FOCUS:-}" == records ]]; then
assert_succeeds task5a_exact_record_and_pure_parser_contract
elif [[ "${TASK5A_FOCUS:-}" == prepackage ]]; then
assert_succeeds task5a_ntfs_capacity_and_prepackage_seams
elif [[ "${TASK5A_FOCUS:-}" == capture ]]; then
assert_succeeds task5a_exact_process_capture_contract
elif [[ "${TASK5A_FOCUS:-}" == prepackage_system ]]; then
assert_succeeds task5a_prepackage_system_command_matrix
assert_succeeds task5a_ntfsinfo_pipeline_contract
elif [[ "${TASK5A_FOCUS:-}" == dryrun ]]; then
assert_succeeds task5a_dry_run_and_prepackage_action_binding
elif [[ "${TASK4_REVIEW5_FOCUS:-}" == priority ]]; then
assert_succeeds task4_review5_pending_priority
elif [[ "${TASK4_REVIEW5_FOCUS:-}" == feas_cleanup ]]; then
assert_succeeds task4_review5_feasibility_cleanup_failure_is_not_signal_success
elif [[ "${TASK4_REVIEW5_FOCUS:-}" == unpinned ]]; then
assert_succeeds task4_review5_published_unpinned_cleanup
elif [[ "${TASK4_REVIEW5_FOCUS:-}" == parser ]]; then
assert_succeeds task4_review5_exact_proc_record_contract
elif [[ "${TASK4_REVIEW5_FOCUS:-}" == anchor ]]; then
assert_succeeds task4_review5_default_supervisor_anchor
assert_succeeds task4_review5_feasibility_default_supervisor_anchor
elif [[ "${TASK4_REVIEW5_FOCUS:-}" == phase_fixture_cleanup ]]; then
assert_succeeds task4_review5_phase_fixture_failure_cleanup_contract
elif [[ "${TASK4_REVIEW4_FOCUS:-}" == phases ]]; then
assert_succeeds task4_review4_signal_phase_matrix
elif [[ "${TASK4_REVIEW4_FOCUS:-}" == boundaries ]]; then
assert_succeeds task4_review4_stale_cached_group_boundary
assert_succeeds task4_review4_direct_fallback_boundaries
assert_succeeds task4_review4_term_ignoring_group_escalates
assert_succeeds task4_review4_wait_clear_is_atomic
elif [[ "${TASK4_REVIEW4_FOCUS:-}" == stale_group ]]; then
assert_succeeds task4_review4_stale_cached_group_boundary
elif [[ "${TASK4_REVIEW3_FOCUS:-}" == signal ]]; then
assert_succeeds task4_review3_nested_pipeline_signal_cleanup
elif [[ "${TASK4_REVIEW3_FOCUS:-}" == early_signal ]]; then
assert_succeeds task4_review3_launch_window_signal_cleanup
elif [[ "${TASK4_REVIEW2_FOCUS:-}" == critical ]]; then
assert_succeeds task4_review2_tty_and_bounded_validated_pipe
elif [[ "${TASK4_REVIEW2_FOCUS:-}" == show_protected ]]; then
assert_succeeds task4_review2_show_protected_and_readonly
elif [[ "${TASK4_REVIEW2_FOCUS:-}" == feas_show_protected ]]; then
assert_succeeds task4_review2_feasibility_show_protected
elif [[ "${TASK4_REVIEW2_FOCUS:-}" == feas_attachment_listing ]]; then
assert_succeeds task4_review2_feasibility_attachment_listing
assert_succeeds task4_review2_attachment_parser_contract
elif [[ "${TASK4_REVIEW2_FOCUS:-}" == capture ]]; then
assert_succeeds task4_review2_capture_one_line_exact_record
elif [[ "${TASK4_REVIEW2_FOCUS:-}" == atomic ]]; then
assert_succeeds task4_review2_atomic_save_transition
elif [[ "${TASK4_REVIEW1_FOCUS:-}" == critical ]]; then
assert_succeeds task4_review1_producer_must_finish_before_consumer
elif [[ "${TASK4_REVIEW1_FOCUS:-}" == lineage ]]; then
assert_succeeds task4_review1_preflight_and_lineage_gate
elif [[ "${TASK4_REVIEW1_FOCUS:-}" == constants_lf ]]; then
assert_succeeds task4_review1_exact_constants_become_readonly
assert_succeeds task4_review1_generated_password_exact_lf
elif [[ "${TASK4_REVIEW1_FOCUS:-}" == signal ]]; then
assert_succeeds task4_review1_blocking_child_is_reaped
elif [[ "${TASK4_REVIEW1_FOCUS:-}" == noclobber ]]; then
assert_succeeds task4_review1_noclobber_lifecycle
elif [[ "${TASK4_REVIEW1_FOCUS:-}" == parser ]]; then
assert_succeeds task4_review1_raw_package_record
elif [[ "${TASK4_REVIEW1_FOCUS:-}" == leak ]]; then
assert_succeeds task4_review1_two_sentinel_leak_contract
else
assert_succeeds task5a_wrapper_and_guard_contract
assert_succeeds task5a_prepare_provider_capability_gate
assert_succeeds task5a_open_close_capability_gate
assert_succeeds task5a_lifecycle_dispatch_contract
assert_succeeds task5a_production_actions_fail_closed
assert_succeeds task5a_exact_record_and_pure_parser_contract
assert_succeeds task5a_ntfs_capacity_and_prepackage_seams
assert_succeeds task5a_exact_process_capture_contract
assert_succeeds task5a_prepackage_system_command_matrix
assert_succeeds task5a_ntfsinfo_pipeline_contract
assert_succeeds task5a_dry_run_and_prepackage_action_binding
assert_succeeds task4_pipeline_contract
assert_succeeds task4_tty_and_secret_api_contract
assert_succeeds task4_feasibility_cleanup_and_leak_contract
assert_succeeds task4_review1_producer_must_finish_before_consumer
assert_succeeds task4_review1_preflight_and_lineage_gate
assert_succeeds task4_review1_exact_constants_become_readonly
assert_succeeds task4_review1_generated_password_exact_lf
assert_succeeds task4_review1_blocking_child_is_reaped
assert_succeeds task4_review1_noclobber_lifecycle
assert_succeeds task4_review1_raw_package_record
assert_succeeds task4_review1_two_sentinel_leak_contract
assert_succeeds task4_review2_tty_and_bounded_validated_pipe
assert_succeeds task4_review2_show_protected_and_readonly
assert_succeeds task4_review2_feasibility_show_protected
assert_succeeds task4_review2_feasibility_attachment_listing
assert_succeeds task4_review2_attachment_parser_contract
assert_succeeds task4_review2_capture_one_line_exact_record
assert_succeeds task4_review2_atomic_save_transition
assert_succeeds task4_review3_nested_pipeline_signal_cleanup
assert_succeeds task4_review3_launch_window_signal_cleanup
assert_succeeds task4_review4_stale_cached_group_boundary
assert_succeeds task4_review4_direct_fallback_boundaries
assert_succeeds task4_review4_term_ignoring_group_escalates
assert_succeeds task4_review4_wait_clear_is_atomic
assert_succeeds task4_review4_signal_phase_matrix
assert_succeeds task4_review5_exact_proc_record_contract
assert_succeeds task4_review5_default_supervisor_anchor
assert_succeeds task4_review5_feasibility_default_supervisor_anchor
assert_succeeds task4_review5_phase_fixture_failure_cleanup_contract
assert_succeeds task4_review5_published_unpinned_cleanup
assert_succeeds task4_review5_pending_priority
assert_succeeds task4_review5_feasibility_cleanup_failure_is_not_signal_success
fi
printf 'PASS: local recovery contract, capacity, and validator fixtures\n'