testsAndMisc/phone_focus_mode/config_tether.sh
Krzysztof kuhy Rudnicki 7dc3c94160
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
config.sh: split 571 -> 250, with a fingerprint that actually works
The brief's prescribed safety net for this file DOES NOT WORK, and that is
the main finding here. It specifies asdict + json.dumps(default=str) +
sha256 against baseline 1624750c. Four policy fields are frozenset, which
json cannot encode, so default=str stringifies them in SET ITERATION ORDER
-- random per process. Three consecutive runs gave three different digests;
pinning PYTHONHASHSEED=0 gave one. The recipe can never match a recorded
baseline, so it would have flagged every split as a regression regardless
of correctness.

The repo already ships a deterministic serializer. Before trusting it I
checked it was not a weaker proxy: policy_to_json emits all 16 dataclass
fields plus 6 more, so it is strictly stronger than asdict. Baseline
83e05e82, unchanged through all six extractions.

Two audits ran BEFORE any extraction:

  - the loader regex-scans config.sh's TEXT ALONE, so the fifteen names it
    reads are pinned here. Moving one does not error -- it silently yields
    an empty set, and for WHITELIST that reads as "hide everything".
  - extractions are variable-scoped, never line-range. A line-range attempt
    cut through the WHITELIST string, leaving an unterminated quote; 15
    variables vanished from the environment WHILE THE FINGERPRINT STILL
    MATCHED, because the loader's regex tolerated the mangled file. That is
    why the environment diff exists alongside the hash.

The environment check earned its keep twice: it also caught STATE_DIR being
left behind the source line, which expanded every path against an empty
prefix (/status.json rather than the real path) with the policy hash green
throughout. 79 variables, byte-identical either side, and every parsed list
compared entry-by-entry: 29/16/46/1/1/2/15, all matching.

Rationale prose moves to docs/policy-lists.md with pointers left behind,
including the blocks inside the list strings -- the loader and the shell
both ignore '#' lines there.

All five siblings are in both deploy lists. deploy.sh still sources the
chain from the checkout, run_phone.sh --help exits 0, 379 shell assertions
pass across nine suites, and focus_policy's 137 pytest cases pass.

config.sh is at EXACTLY 250 with no headroom; 184 of those lines are
loader-pinned lists that cannot move. Over-cap count 18 -> 17.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-17 17:17:02 +02:00

45 lines
2.7 KiB
Bash
Executable File

#!/system/bin/sh
# shellcheck shell=ash
# config_tether.sh — the tethering enforcer's settings: the feature switch,
# poll interval, log and state paths, the offload setting it snapshots, its
# iptables chain, and the override/force hooks.
#
# Sourced by config.sh where these definitions used to sit. None of these is
# read by focus_policy's loader; see the note in config_paths.sh.
# ============================================================
# HOTSPOT / TETHERING BLOCK (closes the "second phone" bypass)
# ============================================================
# The hosts file, dns_enforcer and the curfew net layer all only touch this
# phone's OWN traffic (system resolver + OUTPUT chain). A tethered second
# device's packets are FORWARDed + NAT'd through us on an unfiltered path, so
# it browses freely on our mobile data. tether_enforcer.sh closes that hole.
# Always-on like dns_enforcer, but only ACTS while focus mode is ON (i.e. you
# are at home: $MODE_FILE == "focus") OR the force-test file is present. Covers
# WiFi, USB and Bluetooth tethering, since all of it traverses FORWARD.
export TETHER_ENFORCER_ENABLED=1
export TETHER_CHECK_INTERVAL=5
export TETHER_LOG="$STATE_DIR/tether_enforcer.log"
export TETHER_ENFORCER_STATE="$STATE_DIR/tether_applied"
# Lever 1: disable Android's hardware/BPF tether offload so forwarded traffic
# is actually seen by netfilter (otherwise the FORWARD rule can be silently
# skipped). AOSP global key Settings.Global.TETHER_OFFLOAD_DISABLED; 1=disabled.
# Snapshotted on entry and restored on exit so we never clobber a user value.
export TETHER_OFFLOAD_KEY="tether_offload_disabled"
export TETHER_OFFLOAD_SNAP="$STATE_DIR/tether_offload.snap"
# Lever 2: blanket REJECT of the FORWARD chain (both iptables + ip6tables).
# Re-pinned only when tampered (chain_intact gate, like dns_enforcer) so we do
# not fork an `iptables -L` every second — that pegged netd + overheated the
# phone during curfew-net tuning (see CURFEW_NET_REASSERT_INTERVAL note above).
export TETHER_IPT_CHAIN="FOCUS_TETHER_BLOCK"
# Lever 3 (best-effort, WiFi only): actively stop a running softAP each tick so
# the hotspot toggle visibly flips off. Version-gated (cmd wifi stop-softap is
# Android 11+). The FORWARD block remains the version-independent catch-all.
export TETHER_STOP_SOFTAP_ENABLED=1
# Escape hatches (mirrors the curfew override/force pair):
# tether_override -> suspend the block without stopping the daemon.
# tether_force_on -> force the block ACTIVE regardless of location (daytime
# on-device validation via `focus_ctl.sh tether-test-on`).
export TETHER_OVERRIDE_FILE="$STATE_DIR/tether_override"
export TETHER_FORCE_FILE="$STATE_DIR/tether_force_on"