From 9d187ed6d3494e742fbf5458987a88db10070896 Mon Sep 17 00:00:00 2001 From: LucaLin <78164141+LucaLin233@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:02:10 +0000 Subject: [PATCH 1/5] fix(xanmod): reject unterminated proof tail --- tools/xanmod-install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/xanmod-install.sh b/tools/xanmod-install.sh index 9c7839e..a7fa655 100755 --- a/tools/xanmod-install.sh +++ b/tools/xanmod-install.sh @@ -724,7 +724,8 @@ xanmod_pending_allocation_proof_trusted() { { IFS= read -r proof_token || return 1 IFS= read -r proof_identity || return 1 - if IFS= read -r extra_line; then + # read returns failure at EOF even after consuming an unterminated tail. + if IFS= read -r extra_line || [[ -n "$extra_line" ]]; then return 1 fi } < "$proof_path" From 487ff4b74aa93ca30e9af053405c5183e4bf8557 Mon Sep 17 00:00:00 2001 From: LucaLin <78164141+LucaLin233@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:02:24 +0000 Subject: [PATCH 2/5] fix(xanmod): mirror strict proof tail validation in module --- modules/system-customize.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/system-customize.sh b/modules/system-customize.sh index 31d4a39..c290ed6 100644 --- a/modules/system-customize.sh +++ b/modules/system-customize.sh @@ -2825,7 +2825,8 @@ xanmod_pending_allocation_proof_trusted() { { IFS= read -r proof_token || return 1 IFS= read -r proof_identity || return 1 - if IFS= read -r extra_line; then + # read returns failure at EOF even after consuming an unterminated tail. + if IFS= read -r extra_line || [[ -n "$extra_line" ]]; then return 1 fi } < "$proof_path" From 267823e8bceb0c41771f62f3bd7291646904e011 Mon Sep 17 00:00:00 2001 From: LucaLin <78164141+LucaLin233@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:02:54 +0000 Subject: [PATCH 3/5] test(xanmod): reject malformed proof tails without trusting allocation --- tests/test-xanmod.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test-xanmod.sh b/tests/test-xanmod.sh index a703256..5a33ff1 100755 --- a/tests/test-xanmod.sh +++ b/tests/test-xanmod.sh @@ -53,6 +53,34 @@ make_layout "$TEST_DIR/tool" # shellcheck source=../tools/xanmod-install.sh source "$TOOL" trap 'rm -rf "$TEST_DIR"' EXIT +# Proof parser fixtures: no APT, services or production paths. +( + proof_root="$TEST_DIR/proof-tail" + mkdir -m 0700 "$proof_root" + XANMOD_ALLOCATION_CANDIDATE="$proof_root/candidate" + XANMOD_ALLOCATION_KIND=file + XANMOD_ALLOCATION_OWNER_TOKEN=fixture-token + XANMOD_ALLOCATION_EXPECTED_MODE=600 + touch "$XANMOD_ALLOCATION_CANDIDATE" + chmod 600 "$XANMOD_ALLOCATION_CANDIDATE" + proof="$proof_root/proof" + xanmod_allocation_proof_path() { printf "%s\n" "$proof"; } + identity=$(stat -c "%d:%i" "$XANMOD_ALLOCATION_CANDIDATE") + printf "%s\n%s\n" fixture-token "$identity" > "$proof" + chmod 600 "$proof" + assert_ok "exact two-line proof accepted" xanmod_pending_allocation_owned + for tail in unterminated "$identity"; do + printf "%s\n%s\n%s" fixture-token "$identity" "$tail" > "$proof" + assert_fail "unterminated proof tail rejected" xanmod_pending_allocation_proof_trusted + assert_fail "malformed proof cannot establish ownership" xanmod_pending_allocation_owned + [[ -f "$XANMOD_ALLOCATION_CANDIDATE" ]] || fail "candidate unexpectedly removed" + done + printf "%s\n%s\nextra\n" fixture-token "$identity" > "$proof" + assert_fail "complete third line rejected" xanmod_pending_allocation_owned + printf "%s\n%s\n\n" fixture-token "$identity" > "$proof" + assert_fail "empty third line rejected" xanmod_pending_allocation_owned +) + OTHER_UID=65534; [[ "$OTHER_UID" == "$XANMOD_TRUSTED_UID" ]] && OTHER_UID=0 OTHER_GID=65534; [[ "$OTHER_GID" == "$XANMOD_TRUSTED_GID" ]] && OTHER_GID=0 From 944e17b29b577ab23dac221a1d2342e3372f3653 Mon Sep 17 00:00:00 2001 From: LucaLin <78164141+LucaLin233@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:17:10 +0000 Subject: [PATCH 4/5] test(xanmod): verify malformed proof cleanup and old-parser counterexample --- tests/test-xanmod.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test-xanmod.sh b/tests/test-xanmod.sh index 5a33ff1..d08dd9d 100755 --- a/tests/test-xanmod.sh +++ b/tests/test-xanmod.sh @@ -79,6 +79,39 @@ trap 'rm -rf "$TEST_DIR"' EXIT assert_fail "complete third line rejected" xanmod_pending_allocation_owned printf "%s\n%s\n\n" fixture-token "$identity" > "$proof" assert_fail "empty third line rejected" xanmod_pending_allocation_owned + + # Reproduce the old EOF mistake with inert text, not an old production script. + legacy_accepts_tail() { + local token identity extra_line + { + IFS= read -r token || return 1 + IFS= read -r identity || return 1 + if IFS= read -r extra_line; then return 1; fi + } < "$proof" + [[ "$token" == fixture-token && "$identity" =~ ^[0-9]+:[0-9]+$ ]] + } + printf "%s\n%s\ntail" fixture-token "$identity" > "$proof" + assert_ok "old parser incorrectly accepts unterminated tail" legacy_accepts_tail + assert_fail "fixed parser rejects same counterexample" xanmod_pending_allocation_proof_trusted + + # Exercise the real cleanup dispatcher; the deletion primitive is a spy. + cleanup_calls=0 + xanmod_cleanup_created_allocation() { cleanup_calls=$((cleanup_calls + 1)); return 0; } + for candidate_exists in yes no; do + XANMOD_ALLOCATION_CANDIDATE="$proof_root/candidate" + XANMOD_ALLOCATION_KIND=file + XANMOD_ALLOCATION_OWNER_TOKEN=fixture-token + XANMOD_ALLOCATION_EXPECTED_MODE=600 + XANMOD_ALLOCATION_STATE=pending + XANMOD_ALLOCATION_PROOF_OWNED=false + if [[ "$candidate_exists" == no ]]; then command rm -- "$XANMOD_ALLOCATION_CANDIDATE"; fi + assert_ok "untrusted cleanup safely clears pending state" cleanup_xanmod_pending_allocation + assert_eq 0 "$cleanup_calls" "malformed proof never authorizes deletion" + [[ -f "$proof" ]] || fail "untrusted proof was removed" + if [[ "$candidate_exists" == yes ]]; then + [[ -f "$proof_root/candidate" ]] || fail "untrusted candidate was removed" + fi + done ) OTHER_UID=65534; [[ "$OTHER_UID" == "$XANMOD_TRUSTED_UID" ]] && OTHER_UID=0 From d2bc2fa3bdccbd8385b08478a19b70f16363f4cb Mon Sep 17 00:00:00 2001 From: LucaLin <78164141+LucaLin233@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:33:55 +0000 Subject: [PATCH 5/5] test(xanmod): assert cleanup state reset and scope deletion guarantee --- tests/test-xanmod.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tests/test-xanmod.sh b/tests/test-xanmod.sh index d08dd9d..59fa3be 100755 --- a/tests/test-xanmod.sh +++ b/tests/test-xanmod.sh @@ -94,7 +94,9 @@ trap 'rm -rf "$TEST_DIR"' EXIT assert_ok "old parser incorrectly accepts unterminated tail" legacy_accepts_tail assert_fail "fixed parser rejects same counterexample" xanmod_pending_allocation_proof_trusted - # Exercise the real cleanup dispatcher; the deletion primitive is a spy. + # Only candidate state without prior ownership is covered here. + # Residue/previously-owned resources use a separate existing cleanup contract. + # Exercise the real dispatcher; the deletion primitive is a spy. cleanup_calls=0 xanmod_cleanup_created_allocation() { cleanup_calls=$((cleanup_calls + 1)); return 0; } for candidate_exists in yes no; do @@ -102,11 +104,17 @@ trap 'rm -rf "$TEST_DIR"' EXIT XANMOD_ALLOCATION_KIND=file XANMOD_ALLOCATION_OWNER_TOKEN=fixture-token XANMOD_ALLOCATION_EXPECTED_MODE=600 - XANMOD_ALLOCATION_STATE=pending + XANMOD_ALLOCATION_STATE=candidate XANMOD_ALLOCATION_PROOF_OWNED=false if [[ "$candidate_exists" == no ]]; then command rm -- "$XANMOD_ALLOCATION_CANDIDATE"; fi - assert_ok "untrusted cleanup safely clears pending state" cleanup_xanmod_pending_allocation - assert_eq 0 "$cleanup_calls" "malformed proof never authorizes deletion" + assert_ok "unowned candidate cleanup returns safely" cleanup_xanmod_pending_allocation + assert_eq "" "$XANMOD_ALLOCATION_CANDIDATE" "candidate path cleared" + assert_eq "" "$XANMOD_ALLOCATION_KIND" "allocation kind cleared" + assert_eq "" "$XANMOD_ALLOCATION_OWNER_TOKEN" "owner token cleared" + assert_eq "" "$XANMOD_ALLOCATION_EXPECTED_MODE" "expected mode cleared" + assert_eq "" "$XANMOD_ALLOCATION_STATE" "allocation state cleared" + assert_eq false "$XANMOD_ALLOCATION_PROOF_OWNED" "proof ownership remains false" + assert_eq 0 "$cleanup_calls" "malformed proof does not authorize deletion of unowned candidate" [[ -f "$proof" ]] || fail "untrusted proof was removed" if [[ "$candidate_exists" == yes ]]; then [[ -f "$proof_root/candidate" ]] || fail "untrusted candidate was removed"