mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 20:27:02 +01:00
The bind-mount logic goes to hosts_mount.sh, the Magisk-module and
cache-invalidation logic to hosts_magisk.sh. Both flat siblings, both in
both deploy lists; the two lists were then checked to correspond exactly
in both directions, since a push with no matching cp stages a file that
never lands and the enforcer would fail to source it.
Audited for stdin-draining `while read` loops BEFORE extracting, because
piping one would change behaviour in a way stubs cannot catch. There are
none here. Also confirmed no function writes a global, so there was no
cross-half hazard of the kind that kept NET_BUILT in curfew_enforcer.sh.
69 assertions, 43/43 and 62/62 lines, 17 mutations all killed. Two survived
the first run and both were real findings:
- the unmount give-up threshold was not pinned. Raising it from 5 to 500
changed nothing observable, so the enforcer could have spun 500 times
per poll against a mount that was never going to clear. Now asserted as
a call count.
- the `[ ! -f $HOSTS_TARGET ] && return 1` guard in
is_bind_mounted_correctly is PROVABLY EQUIVALENT: sha256_of returns
empty for a missing file, so the existing -n check already rejects that
case. No test can distinguish it. Equivalent code is a claim of a check
that is not being made, so it is deleted rather than covered by a test
that would pass either way. Old and new bodies were compared across all
four input states -- both-missing, target-missing, match, mismatch --
and agree on every one.
That deletion is the only non-verbatim function; the other 19 moved
untouched, making this the first fully verbatim extraction of the session.
Ordering is asserted where it is load-bearing: /system must go back to ro
after rw, and +i must be restored after being dropped. Presence assertions
alone would pass with either pair reversed.
UNVERIFIED ON DEVICE -- the highest-stakes of the four enforcer splits now
outstanding, since a failure to source leaves nothing blocking while the
phone looks normal. Over-cap count 20 -> 19.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
115 lines
4.2 KiB
Bash
Executable File
115 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# lib/tests/hosts_cases_assert.sh — assertions for assert_bind_mount, the
|
|
# recovery path of hosts_mount.sh: bind the canonical over a wrong target,
|
|
# and fall back to a direct overwrite when the bind will not take.
|
|
#
|
|
# Sourced by test_hosts_libs.sh after hosts_cases_mount.sh, which owns the
|
|
# stubs and counters. Split out to stay under the 250-line cap.
|
|
set -euo pipefail
|
|
|
|
# --- assert_bind_mount ------------------------------------------------------
|
|
|
|
# Already correct: nothing should be mounted or unmounted at all.
|
|
_reset_dev
|
|
printf '127.0.0.1 blocked.example\n' >"${HOSTS_CANONICAL}"
|
|
_copy "${HOSTS_CANONICAL}" "${HOSTS_TARGET}"
|
|
if assert_bind_mount; then
|
|
_t_pass "assert succeeds without work when already mounted"
|
|
else
|
|
_t_fail "assert should succeed when the target already matches"
|
|
fi
|
|
if _calls | grep -q "^mount --bind"; then
|
|
_t_fail "assert re-mounted a target that was already correct"
|
|
else
|
|
_t_pass "assert does not re-mount an already-correct target"
|
|
fi
|
|
|
|
# Wrong content: the bind mount is what fixes it.
|
|
_reset_dev
|
|
printf '127.0.0.1 tampered\n' >"${HOSTS_TARGET}"
|
|
if assert_bind_mount; then
|
|
_t_pass "assert bind-mounts the canonical over a tampered target"
|
|
else
|
|
_t_fail "assert should recover a tampered target via bind mount"
|
|
fi
|
|
case "$(_log)" in
|
|
*"Bind-mounted"*) _t_pass "assert logs the successful bind mount" ;;
|
|
*) _t_fail "assert should log a successful bind mount" ;;
|
|
esac
|
|
|
|
# Bind reports success but changes nothing — the case the code explicitly
|
|
# guards, since a silent no-op mount would otherwise read as enforced.
|
|
_reset_dev
|
|
printf '127.0.0.1 tampered\n' >"${HOSTS_TARGET}"
|
|
touch "${DEV}/bind_is_noop"
|
|
assert_bind_mount || true
|
|
case "$(_log)" in
|
|
*"still mismatches"*) _t_pass "assert detects a bind mount that silently did nothing" ;;
|
|
*) _t_fail "assert must notice a no-op bind mount" ;;
|
|
esac
|
|
case "$(_log)" in
|
|
*"falling back to direct overwrite"*) _t_pass "assert falls back to direct overwrite" ;;
|
|
*) _t_fail "assert should fall back when the bind does not take" ;;
|
|
esac
|
|
rm -f "${DEV}/bind_is_noop"
|
|
|
|
# Bind fails outright: the fallback path has to run and succeed.
|
|
_reset_dev
|
|
printf '127.0.0.1 tampered\n' >"${HOSTS_TARGET}"
|
|
_fail_op mount
|
|
assert_bind_mount || true
|
|
case "$(_log)" in
|
|
*"Bind mount failed"*) _t_pass "assert logs an outright bind-mount failure" ;;
|
|
*) _t_fail "assert should log when the bind mount fails" ;;
|
|
esac
|
|
_clear_fail mount
|
|
|
|
# Nothing works: assert must report failure rather than claim enforcement.
|
|
_reset_dev
|
|
printf '127.0.0.1 tampered\n' >"${HOSTS_TARGET}"
|
|
touch "${DEV}/bind_is_noop"
|
|
_fail_op cp
|
|
if ! assert_bind_mount; then
|
|
_t_pass "assert fails when neither bind nor overwrite can fix the target"
|
|
else
|
|
_t_fail "assert must not report success when the target is still wrong"
|
|
fi
|
|
_clear_fail cp
|
|
rm -f "${DEV}/bind_is_noop"
|
|
|
|
# A mount that survives five umount attempts must be reported as unclearable,
|
|
# not retried forever. The stub is made to "succeed" while leaving the entry
|
|
# in place, which is exactly a mount that will not go away.
|
|
_reset_dev
|
|
_seed_mount
|
|
touch "${DEV}/umount_is_noop"
|
|
if ! unmount_existing_hosts_mount; then
|
|
_t_pass "unmount reports failure after five attempts"
|
|
else
|
|
_t_fail "unmount must fail once it has given up"
|
|
fi
|
|
case "$(_log)" in
|
|
*"after 5 attempts"*) _t_pass "unmount logs that it gave up" ;;
|
|
*) _t_fail "unmount should log giving up" ;;
|
|
esac
|
|
# The count is asserted, not just the give-up: a larger threshold would spin
|
|
# that many times against a mount that is never going to clear, inside the
|
|
# enforcer's poll loop.
|
|
_t_eq "5" "$(_calls | grep -c "^umount ")" "unmount tries exactly five times before giving up"
|
|
rm -f "${DEV}/umount_is_noop"
|
|
|
|
# The direct-overwrite fallback succeeding is the last line of defence: the
|
|
# bind failed, but the target ends up correct anyway.
|
|
_reset_dev
|
|
printf '127.0.0.1 blocked.example\n' >"${HOSTS_CANONICAL}"
|
|
printf '127.0.0.1 tampered\n' >"${HOSTS_TARGET}"
|
|
_fail_op mount
|
|
touch "${DEV}/overwrite_works"
|
|
if assert_bind_mount; then
|
|
_t_pass "assert succeeds via the direct-overwrite fallback"
|
|
else
|
|
_t_fail "assert should succeed when the overwrite fixes the target"
|
|
fi
|
|
rm -f "${DEV}/overwrite_works"
|
|
_clear_fail mount
|