ShellAllowlist
Process-global allowlist for run_shell. The host sets a set of command prefixes (e.g. "pm list packages", "dumpsys battery", "settings get global") at startup; the tool refuses any command whose argv doesn't start with one of them.
Token matching. Each entry is whitespace-tokenized; it matches a request only if its tokens are exactly equal to the request's first N argv entries (N = entry token count). "pm list" matches argv [pm, list, packages] but NOT [pmx, ...], [pm, listx] or [pm, "list packages"]. Entries therefore can't express an argument containing whitespace. Trailing arguments beyond the entry are unrestricted — so allowlist the narrowest prefix that works.
Interpreter entries are rejected. An entry whose first token (or its basename, e.g. /system/bin/sh) is in FORBIDDEN_LEADING_COMMANDS — shells, toybox/busybox, su, app_process, env, xargs, nohup, timeout, etc. — would let any command run as the shell/root UID, defeating the allowlist. set throws IllegalArgumentException for such entries (and for blank entries). Commands that can themselves spawn subprocesses given the right arguments (find -exec, awk 'BEGIN{system(...)}', am-started instrumentation) are not all enumerable — review each entry with that in mind.
Conservative by design: empty allowlist = run_shell is disabled. The LLM cannot extend the allowlist; only host code can.
Not safe for parallel test execution. Tests mutate the global via set(emptySet()) in @BeforeEach/@AfterEach. If JUnit5 parallel test execution is enabled in this module, concurrent tests would race on prefixes. The current build.gradle.kts doesn't enable parallel mode; if you ever do, fence these tests with @Execution(SAME_THREAD) or refactor the allowlist out of process-global state.