Commit Graph

7 Commits

Author SHA1 Message Date
0a07684cca hosts_enforcer: split 421 -> 207 + 166 + 91, both libs at 100%
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
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
2026-08-17 16:30:56 +02:00
9dd9aeb50e Fix the 11 shellcheck findings that kept CI red since b83ce5d
"Pre-commit checks" has failed on every push since b83ce5d removed
shellcheck's --severity filter. The findings never appeared locally: CI
installs an older shellcheck than this machine, and 0.11.0 no longer emits
SC2002/SC2015/SC2119 at all. Reproduced by fetching shellcheck 0.9.0, which
matches CI's output exactly, and verified against both versions.

SC2015 x6 -- rewrote `A && B || C` as an explicit if. Each keeps its trailing
`|| true`, so a failing side effect still cannot abort the caller under set -e.
The first draft dropped it in run_phone.sh; a review pass caught that and it
was restored, since that call is the last statement of a function.

SC2119 x3 -- require_imagemagick and adb_select_device take an OPTIONAL first
argument, so the bare calls were already correct. Following shellcheck's
literal advice and passing "$@" would forward the script's own arguments as a
tool preference, which would be a real defect. Passing an explicit "" states
the same intent without changing behaviour, confirmed by running both forms.

SC2002 x2 -- `cat FILE | tr ...` becomes `tr ... < FILE`, keeping 2>/dev/null
so a missing file still yields an empty string.

No suppressions, no --severity filter, no exclusions added. Verified: 0
findings under both shellcheck versions, `pre-commit run shellcheck
--all-files` passes, test_adb_common 13/13, test_boot_repair 31/31, 1193
Python tests at 100% coverage.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ReVsw7qovYkkGbqcU9tn7
2026-08-04 20:55:02 +02:00
dd3191d961 phone_focus_mode: fix YouTube DNS blocking via netd cache restart
- Added restart_netd_for_hosts_cache() to hosts_enforcer.sh with PID-stamp
  deduplication to prevent double-restarts across enforcer invocations
- Removed explicit netd restart from deploy.sh (caused double-restart
  that broke ConnectivityService binder link and dropped default route)
- deploy.sh: wait 10s after starting focus_daemon.sh for enforcer to
  complete its single netd restart before companion app install
- Misc updates to dns_enforcer.sh and config.sh
2026-05-22 15:58:36 +02:00
00c383008a phone_focus_mode: prevent Magisk app from disabling Systemless Hosts module
The Magisk app's Modules tab "Disable" / "Remove" buttons work by
creating marker files (disable, remove) in /data/adb/modules/hosts/.
Tapping Disable in the app on next boot would skip the module's
magic-mount of /system/etc/hosts, silently disabling all hosts-file
blocking.

Defense in depth:

1. deploy.sh chattr +i's the module dir + its hosts file so the
   Magisk app cannot create disable/remove markers (kernel returns
   EPERM). The +i attribute survives reboot.

2. hosts_enforcer.sh adds protect_magisk_module(): every poll cycle
   (and on startup) scans for disable/remove/update markers, deletes
   them, logs TAMPER, and re-asserts +i on the dir. Safety net in
   case the lock is bypassed.

3. sync_magisk_module() now drops +i briefly before its cp and
   re-locks via protect_magisk_module() so workout-state hosts
   swaps still work.

4. deploy.sh detects the previously-silent failure mode of the
   module being enabled on disk but not yet magic-mounted (no
   /system/etc/hosts) and aborts with a clear reboot-required
   message instead of producing a deploy that does nothing.

5. focus_ctl.sh hosts-status now prints the lock state and warns
   about any present markers.

Verified end-to-end on BL9000EEA0000102:
- Pre-reboot: chattr +i set, touch /data/adb/modules/hosts/disable
  returns Operation not permitted.
- Post-reboot: /system/etc/hosts magic-mounted (178303 lines, sha
  matches canonical), lock survives reboot, ping youtube.com -> 127.0.0.1.
- Tamper test: chattr -i + touch disable -> enforcer logs
  'TAMPER: removed Magisk module marker' within 15s and re-locks.

Documented intentional override path inline (focus_ctl.sh hosts-stop;
chattr -i; touch disable).
2026-05-06 21:40:51 +02:00
fa24f22ca0 Apply focus-mode, screen-locker, and steam backlog updates 2026-05-03 22:41:53 +02:00
589e059eee feat(phone-focus): add recovery workflow, automation scripts, and docs 2026-05-01 19:07:27 +02:00
135ef0c62d phone_focus_mode: add persistent home-mode status notification
- New companion Android app (com.kuhy.focusstatus) under
  phone_focus_mode/focus_status_app/ with a pure-Java, Gradle-less
  command-line build pipeline (build.sh). Shows an ongoing
  notification titled 'Focus: HOME / AWAY / DAEMON DOWN' with
  distance, GPS, disabled-app count, last check, daemon checkmarks,
  and a 'Re-check now' action button.
- focus_daemon.sh: write_status_snapshot() + sleep_with_recheck()
  for JSON status + early-wake on trigger file. init() chmods
  STATE_DIR 777 so the app can drop the trigger file.
- config.sh: new STATUS_FILE / RECHECK_TRIGGER; WHITELIST expanded
  with com.kuhy.focusstatus and 11 more user-requested apps
  (podcini X, mpv, bible/openbible, pkp/portalpasazera, orange,
  runnerup, splitbills/splitwise, xiaomi smarthome).
- focus_ctl.sh: new 'recheck' + 'notif-status' subcommands.
- deploy.sh: new step [7/7] builds APK, installs, grants
  POST_NOTIFICATIONS, pre-approves Magisk SU policy, launches
  foreground service.
- .gitignore: exclude focus_status_app/build symlink + debug.keystore.

End-to-end verified on device: notification live with real values;
Re-check button triggers a daemon location check within ~1s.
2026-04-20 15:33:46 +02:00