Problem
The README's own motivating threat list for @shield_tool includes shell.run alongside http_post:
If your agent calls tools that act on the outside world — http_post, shell.run, file read/write, database queries, sending email — then wrap those tools with @shield_tool.
But the only strong bundled policy today is URLAllowList. There is no equivalent bundled rule for shell commands — a user whose agent can call shell.run/subprocess-style tools has to hand-write their own policy from scratch, with no guidance on what a reasonable default even looks like (shell metacharacters, command allowlisting, etc.).
Arbitrary shell execution via prompt injection is at least as severe a threat as URL exfiltration, and it's explicitly named as an in-scope use case, so the gap is inconsistent with the rest of the product's positioning.
Proposal
Add a bundled shell-command policy, default-deny in the same spirit as URLAllowList:
NoDangerousShellPatterns — blocks shell metacharacters (;, |, &&, $(, backticks), redirection, and common exfil/destructive patterns (curl, wget, rm -rf, piping to sh/bash) — a quick raw-string tripwire. Must be documented explicitly as a tripwire, not a shell parser or security boundary, the same way SensitiveDataFilter is documented today (see Limitations).
ShellCommandAllowList — default-deny allowlist of permitted commands, mirroring URLAllowList's allowed-domains model. Per discussion, this should match on a structured (binary, argv) representation rather than raw-string prefix matching — raw-string matching is defeated by quoting, whitespace, environment expansion, and interpreter invocation (sh -c "...", env FOO=bar cmd). Scope during implementation: what "resolved" means (symlink/$PATH handling, whether sh -c/bash -c/env wrappers are unwrapped and their inner command checked or rejected outright).
- Add a
category field to Violation (e.g. metacharacter, interpreter, destructive_command, network_utility, not_allowlisted) alongside the existing free-text rule_name/reason, so an agent loop can branch on the block programmatically instead of parsing prose. This is a general Violation improvement, not shell-specific — worth applying consistently to URLAllowList/SensitiveDataFilter too.
Follow the same nested-container walk (dict/list/tuple/set) and self-referential-container guard already used in URLAllowList/SensitiveDataFilter for consistency.
Why this matters
Closes the gap between the README's stated threat model (URL exfiltration and shell execution) and what's actually shipped (URL only). Without it, users adopting ModelFuzz specifically for command-injection risk get no bundled help and have to write correct shell-safety logic themselves — which is easy to get wrong.
Acceptance criteria
Problem
The README's own motivating threat list for
@shield_toolincludesshell.runalongsidehttp_post:But the only strong bundled policy today is
URLAllowList. There is no equivalent bundled rule for shell commands — a user whose agent can callshell.run/subprocess-style tools has to hand-write their own policy from scratch, with no guidance on what a reasonable default even looks like (shell metacharacters, command allowlisting, etc.).Arbitrary shell execution via prompt injection is at least as severe a threat as URL exfiltration, and it's explicitly named as an in-scope use case, so the gap is inconsistent with the rest of the product's positioning.
Proposal
Add a bundled shell-command policy, default-deny in the same spirit as
URLAllowList:NoDangerousShellPatterns— blocks shell metacharacters (;,|,&&,$(, backticks), redirection, and common exfil/destructive patterns (curl,wget,rm -rf, piping tosh/bash) — a quick raw-string tripwire. Must be documented explicitly as a tripwire, not a shell parser or security boundary, the same waySensitiveDataFilteris documented today (see Limitations).ShellCommandAllowList— default-deny allowlist of permitted commands, mirroringURLAllowList's allowed-domains model. Per discussion, this should match on a structured(binary, argv)representation rather than raw-string prefix matching — raw-string matching is defeated by quoting, whitespace, environment expansion, and interpreter invocation (sh -c "...",env FOO=bar cmd). Scope during implementation: what "resolved" means (symlink/$PATHhandling, whethersh -c/bash -c/envwrappers are unwrapped and their inner command checked or rejected outright).categoryfield toViolation(e.g.metacharacter,interpreter,destructive_command,network_utility,not_allowlisted) alongside the existing free-textrule_name/reason, so an agent loop can branch on the block programmatically instead of parsing prose. This is a generalViolationimprovement, not shell-specific — worth applying consistently toURLAllowList/SensitiveDataFiltertoo.Follow the same nested-container walk (
dict/list/tuple/set) and self-referential-container guard already used inURLAllowList/SensitiveDataFilterfor consistency.Why this matters
Closes the gap between the README's stated threat model (URL exfiltration and shell execution) and what's actually shipped (URL only). Without it, users adopting ModelFuzz specifically for command-injection risk get no bundled help and have to write correct shell-safety logic themselves — which is easy to get wrong.
Acceptance criteria
NoDangerousShellPatternsships as a raw-string tripwire, documented in README Limitations as not a parser/security boundaryShellCommandAllowListmatches structured(binary, argv)commands, not raw-string prefixesViolationgains a machine-readablecategoryfieldsh -c,bash -c,env), and fail-closed behavior on unparseable/unrecognized input