Files
Triple-C/app/src-tauri
shadow-testandClaude Opus 5 00128f9b1a Make the scrub work off /usr/bin, and stop a log level deciding whether it runs
H3 — the pre-commit scrub was a silent no-op on any base image whose coreutils
are not under /usr/bin. Hardening against a PATH-planted `stat` shim by naming
every tool absolutely bought nothing the `PATH` reset on the first line had not
already bought — the shim lives in the persisted home volume, and uid 1000
cannot write /usr/bin or /bin — and it cost the whole feature on Alpine, which
Settings -> Docker -> Custom accepts. Measured on one seeded tree in a real
container: the absolute-path script printed `###TRIPLE-C-SCRUBBED 0` and left
every planted file in place; the PATH-resolved one reclaims 77824 bytes. The
default image is unchanged at 521038.

It was silent three times over, and all three are fixed:

* The script now probes for all six things it needs (`command -v` for the five
  tools, plus the root device id reading back as a number) and, if any is
  missing, prints `###TRIPLE-C-SCRUB-UNAVAILABLE <what>` and no total at all.
* `scrub_writable_layer` reads that marker first and returns a new
  `ScrubOutcome::Unavailable`, warning with what the image is missing; a
  genuine `Reclaimed(0)` now leaves a debug line rather than nothing.
* `commit_log_suffix` renders `Reclaimed(0)` as "ran and found nothing to drop"
  rather than "0.00 MB dropped", which is what a scrub that could not run used
  to look like.

Verified in real containers: the mount-at-the-match defence still holds with a
home-volume `stat` shim first on PATH (the volume survives; deleting the PATH
reset from the same script empties it, so the harness can tell the difference).

H2 — the pre-migration scrub had been folded into `log::info!`'s argument list
to satisfy `#[must_use]`. `log::info!` expands to `if Info <= max_level() { … }`,
so the awaited scrub lived inside the level check, and `logging::init`
tolerates `dispatch.apply()` failing — which returns before `set_max_level` and
leaves the process at `Off`. In that state the scrub never ran and the layer
was committed into the longest-lived snapshot the app takes. The outcome is
bound first now, `logging::init` restores the level on failure and says so on
stderr, and a test scans all four files for an `.await` inside any `log::*!`
argument list.

Also:

* `reconcile_migration` deferred a held project instead of dropping it. Its
  only caller fires once per "Docker became available", so a project held at
  that instant was never revisited for the session — phase un-normalised, no
  resume or rollback offered, pin left `Claimed`. It now waits for the holder
  to let go (20s x 90, one waiter per project) and reconciles then.
* The scrub's byte total counts what a partly failed `rm` removed, by
  re-measuring rather than dropping the whole subtree on a non-zero exit —
  which was exactly the `--one-file-system` case.
* The scrub exec blanks `LD_PRELOAD`, `LD_AUDIT` and `LD_LIBRARY_PATH`.
  `LD_PRELOAD` is in none of the reserved env families, so a project's custom
  env var reached a root exec and injected code into every tool the scrub runs,
  `PATH` reset or not. Verified against Engine 29.7 that `docker exec -e` wins.
* The device test is described honestly: it is a mount test under `overlay2`
  and not under `vfs`, where checks 1 and 2 are what still hold.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GBq2rGum6GX7xXgsas1fDc
2026-08-23 15:47:36 -07:00
..