Skip to content

Add bundled shell-command policy (e.g. ShellCommandAllowList / NoDangerousShellPatterns) — URL is covered, shell exec isn't #71

Description

@higagan

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

  • NoDangerousShellPatterns ships as a raw-string tripwire, documented in README Limitations as not a parser/security boundary
  • ShellCommandAllowList matches structured (binary, argv) commands, not raw-string prefixes
  • Violation gains a machine-readable category field
  • Test coverage includes: shell quoting variants, nested containers, interpreter/wrapper invocation (sh -c, bash -c, env), and fail-closed behavior on unparseable/unrecognized input

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestsecuritySecurity-relevant defect or hardening

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions