Evidence
tools/bash.rs:40 runs bash -c <command> — full shell: word-splitting, quote removal, variable/glob/brace expansion, eval.
permissions/builtin.rs (split_command_segments / match_breaker_in_segment) matches literal token text like rm -rf after collapsing whitespace.
grep -rn "pre_exec|SandboxPolicy" tools/ → nothing. The sandbox (sandbox/mod.rs, Landlock/Seatbelt) is wired only into hook subprocesses (hooks/dispatch.rs), though the module docs name the bash tool as the next consumer.
Impact
Any shell construct that yields a destructive command without the literal substring evades the floor and reaches bash -c:
rm -r''f /, \rm -rf /, X=rm; $X -rf /, rm -rf --no-preserve-root /, xargs rm -rf /, eval "rm -rf /". The same tokenizer gates user deny rules (rule.rs matches_liberal), so those are evadable too. Under FullyUnattended / AFK the floor is the only backstop.
A string-matcher cannot be made sound against a full shell interpreter.
Fix (two parts)
- Wire the existing
SandboxPolicy into BashTool::run via pre_exec (cwd + system libs read; cwd + /tmp write; $HOME dotfiles excluded) — the only sound boundary. At minimum gate it behind FullyUnattended/AFK where there is no human backstop.
- Harden the breaker against cheap evasions: strip quotes/backslashes before matching, match
rm flag-order-independently (-rf/-fr/-r -f), and add a --no-preserve-root pattern.
Related
#26 (file/path-access dimension of the same "no real boundary" problem).
Acceptance
- Known-gap tests for each evasion (assert current behavior, flip when fixed — like the existing
xargs note).
- Bash tool runs under a sandbox policy in unattended modes.
Evidence
tools/bash.rs:40runsbash -c <command>— full shell: word-splitting, quote removal, variable/glob/brace expansion,eval.permissions/builtin.rs(split_command_segments/match_breaker_in_segment) matches literal token text likerm -rfafter collapsing whitespace.grep -rn "pre_exec|SandboxPolicy" tools/→ nothing. The sandbox (sandbox/mod.rs, Landlock/Seatbelt) is wired only into hook subprocesses (hooks/dispatch.rs), though the module docs name the bash tool as the next consumer.Impact
Any shell construct that yields a destructive command without the literal substring evades the floor and reaches
bash -c:rm -r''f /,\rm -rf /,X=rm; $X -rf /,rm -rf --no-preserve-root /,xargs rm -rf /,eval "rm -rf /". The same tokenizer gates userdenyrules (rule.rsmatches_liberal), so those are evadable too. Under FullyUnattended / AFK the floor is the only backstop.A string-matcher cannot be made sound against a full shell interpreter.
Fix (two parts)
SandboxPolicyintoBashTool::runviapre_exec(cwd + system libs read; cwd +/tmpwrite;$HOMEdotfiles excluded) — the only sound boundary. At minimum gate it behind FullyUnattended/AFK where there is no human backstop.rmflag-order-independently (-rf/-fr/-r -f), and add a--no-preserve-rootpattern.Related
#26 (file/path-access dimension of the same "no real boundary" problem).
Acceptance
xargsnote).