Skip to content

--permission-mode dontAsk bypasses autoAllowBashIfSandboxed for Bash commands containing shell variable expansion #51001

Description

@ejfn

Summary

When Claude Code is invoked with --permission-mode dontAsk and sandbox.autoAllowBashIfSandboxed: true, Bash commands that contain shell variable expansion (e.g. $VAR) are denied with "Permission to use Bash has been denied because Claude Code is running in don't ask mode" instead of being auto-approved through the sandbox.

Non-expansion Bash commands run correctly in the same session — so the sandbox is functional and auto-allow fires for those. The issue is specific to commands containing shell expansion.

The sandboxing documentation states:

Auto-allow mode works independently of your permission mode setting. Even if you're not in "accept edits" mode, sandboxed bash commands will run automatically when auto-allow is enabled.

This does not hold for expansion-bearing commands under dontAsk.

Environment

  • Claude Code: 2.1.114
  • OS: Linux (Ubuntu 24.04 on WSL2)
  • bubblewrap: 0.9.0
  • socat installed

Minimal Reproduction

settings.json:

{
  "permissions": { "allow": ["Read", "Glob", "Grep"] },
  "sandbox": {
    "enabled": true,
    "autoAllowBashIfSandboxed": true,
    "filesystem": {
      "denyRead": ["/"],
      "denyWrite": ["/"],
      "allowRead": [".", "/usr", "/etc"],
      "allowWrite": []
    }
  }
}

(Run from any git working tree with at least one commit.)

Failing case — command WITH expansion:

export TB=HEAD~1
claude -p "Run the bash command: git log --oneline -1 \$TB" \
  --permission-mode dontAsk --settings ./settings.json \
  --max-turns 3 --output-format stream-json --verbose < /dev/null

Tool result:

is_error=true: Permission to use Bash has been denied because Claude Code is running in don't ask mode.

Passing case — same session, same settings, same permission mode, but NO expansion:

claude -p "Run the bash command: git log --oneline -1" \
  --permission-mode dontAsk --settings ./settings.json \
  --max-turns 3 --output-format stream-json --verbose < /dev/null

Tool result:

is_error=false: <commit sha> <commit subject>

The only variable is the presence of $TB in the command string. The sandbox configuration, permission mode, and autoAllowBashIfSandboxed: true are identical.

Expected

Both commands auto-approved by the sandbox per the documented guarantee. Both return git log output.

Actual

  • Expansion command: permission-layer deny before the sandbox wrap is attempted.
  • Non-expansion command: executed inside the sandbox, output returned.

Impact

Any non-interactive workflow that relies on --permission-mode dontAsk (CI pipelines, scripted headless runs, agents invoked from external automation) cannot execute Bash commands containing shell expansion, even when the sandbox is fully capable of wrapping them safely. Models frequently generate commands with expansion when environment variables are semantically relevant (e.g. git diff \$TARGET_BRANCH...HEAD from a prompt that passes TARGET_BRANCH via env). Workarounds require rewriting commands to read env values separately and inline them, which is not always feasible for generated tool calls.

Likely 2.1.113 provenance

2.1.113 added a family of security hardenings around Bash matching/approval, including:

  • "Security: Bash deny rules now match commands wrapped in `env`/`sudo`/`watch`/`ionice`/`setsid` and similar exec wrappers"
  • "Security: `Bash(find:)` allow rules no longer auto-approve `find -exec`/`-delete`"*

These sibling changes suggest a set of heuristic checks were added that require approval for commands exhibiting certain patterns. If an expansion-heuristic was added at the same time and is evaluated before the sandbox auto-allow path, that would explain the observed behaviour.

Suggested fix

Either (a) evaluate `autoAllowBashIfSandboxed` for expansion-bearing commands before rejecting under `dontAsk`, or (b) document the expansion-command exception in the sandboxing docs so users know the mode-independence guarantee has a scope.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions