Skip to content

POSIX safe mode has the same dynamic-construct bypass as the PowerShell one fixed in #37 #44

Description

@cf-pages

Problem

SHELL_EXPANSION_RE gates backticks, $(, and ${, but not a bare $var. Since the destructive and network scans match on literal command names, a name assembled at runtime slips past them:

c=cu'rl'; $c example.com          # NETWORK_RE never sees "curl"
f='-rf'; rm $f /some/path         # DESTRUCTIVE_RE never sees "rm -rf"

This is the POSIX twin of the PowerShell bypass fixed in #37 ($c='Invoke-WebRequest'; & $c example.com). It predates that PR — it's in main today — and the same reasoning applies: keyword scanning is only sound when the command text is literal, and a bare variable breaks that precondition.

Why it wasn't fixed alongside #37

The PowerShell gate was scoped to hosts where PowerShell is the interpreter precisely to avoid changing POSIX behavior in a contributor PR. Extending it to POSIX means echo $HOME starts requiring the shell_expansion permission in safe mode, which is a much more visible change — plenty of ordinary commands use variables.

Options

  1. Gate bare $ on POSIX too, consistent with Windows: execute string commands with PowerShell 7 #37. Most sound, most disruptive; needs a look at how often real safe-mode usage would start prompting.
  2. Gate only where it matters — a $ in command-name position (start of a segment, or after the call operator) rather than in argument position. Narrower blast radius, but "command position" needs a real parser to determine reliably, which is its own project (Codex uses tree-sitter for exactly this).
  3. Accept it and rely on the sandbox. On Linux, Landlock already contains the filesystem effects, and network is the main residual exposure — which Windows: no OS-level sandbox, so command scanning is the only boundary #43 and the network-egress issue address more fundamentally.

Option 3 is the honest current state and argues for prioritizing sandbox/egress work over more scanner hardening. Worth deciding explicitly rather than leaving the gap undocumented.

Related: #37, #43.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions