Summary
PR #37 (#7) shipped beetroot module <name> <zip> --auto-install for adb-adopted rooted devices with the full CI-level contract pinned by mocked, argv-exact tests (push → su -c 'magisk --install-module …' → cleanup rm, sha256 fail-closed, per-module reporting, hostile-filename injection tests). What it could not include — the development environment has no Android device or Docker daemon — is a run against a real MagiskSU.
This issue tracks that one-time smoke validation on a physical rooted phone (or any real adb target with Magisk) before the feature is treated as release-grade.
Why this is worth a real-device pass
The security fix in PR #37 rests on a specific model of two device-side shell parses: adb shell flattens argv into one line (parse 1: device shell), then MagiskSU re-joins everything after -c and executes it via an inner sh -c (parse 2). The fix (synthesized beetroot-module-<i>.zip temp names + dual-level shlex.quote) was hand-traced and review-approved against that model, but the model itself is exactly the kind of thing that varies across su implementations/versions — and mocked tests structurally cannot catch a wrong model, only a wrong implementation of it.
Checklist
Refs
Summary
PR #37 (#7) shipped
beetroot module <name> <zip> --auto-installfor adb-adopted rooted devices with the full CI-level contract pinned by mocked, argv-exact tests (push →su -c 'magisk --install-module …'→ cleanuprm, sha256 fail-closed, per-module reporting, hostile-filename injection tests). What it could not include — the development environment has no Android device or Docker daemon — is a run against a real MagiskSU.This issue tracks that one-time smoke validation on a physical rooted phone (or any real adb target with Magisk) before the feature is treated as release-grade.
Why this is worth a real-device pass
The security fix in PR #37 rests on a specific model of two device-side shell parses:
adb shellflattens argv into one line (parse 1: device shell), then MagiskSU re-joins everything after-cand executes it via an innersh -c(parse 2). The fix (synthesizedbeetroot-module-<i>.ziptemp names + dual-levelshlex.quote) was hand-traced and review-approved against that model, but the model itself is exactly the kind of thing that varies across su implementations/versions — and mocked tests structurally cannot catch a wrong model, only a wrong implementation of it.Checklist
--auto-install; module visible inmagisk --listafter reboot.My Module.zip) installs (synthesized remote name sidesteps both parses).$(true).zip— benign payload) installs normally, nothing executed, name never reaches a device shell.beetroot-module-*.zipleft in/data/local/tmpafter success AND after an install failure.Refs