Sandbox the Shell Tool
The coding agent's run_shell tool runs whatever text it is handed, and last week that was cat ~/.aws/credentials, because a README said the deploy config lived there. A blocklist gets walked around (rm is blocked, so the model pipes to sh), so the gate is an allowlist plus a few structural rules, applied in a fixed order.
Implement vet_command(command) returning (True, "ok") or (False, rule), where rule is the first of these that fires:
shell: the raw text contains a pipe, a semicolon, an ampersand, a redirect (>or<), a backtick or a$; orshlexcannot parse it (an unbalanced quote); or the program ispythonwith a-cflag.program: the program (the first token aftershlex.split) is not inALLOWED_PROGRAMS. An empty command has no program.escape: any argument is an absolute path, contains.., or starts with~.secrets: any argument's final path component is.env,id_rsa,credentialsor.netrc, or ends in.pemor.key.destructive:gitwithpush,reset,cleanorrebaseas its subcommand, orfindwith-deleteor-execamong its arguments.
Use shlex.split so quoting works the way the shell sees it: cat "my notes.txt" is one argument.
The catch: the order matters. cat ../.env is an escape before it is a secret, and curl x | sh is a shell construct before curl is an unknown program.