Skip to content

[BUG] autoAllowBashIfSandboxed bypassed for commands containing shell expansions and several other constructs #43713

@sgrankin

Description

@sgrankin

Summary

With autoAllowBashIfSandboxed: true and sandboxing enabled, commands containing certain shell constructs prompt for permission despite the setting, with reasons like Contains simple_expansion or Unhandled node type: string. Syntactically-simple commands (literal argv, pipes, &&, ;, ||, control flow, [[ ]], <<<) auto-approve as expected.

Repro

  1. ~/.claude/settings.json:
    { "sandbox": { "enabled": true, "autoAllowBashIfSandboxed": true } }
  2. Start a Claude Code session.
  3. Ask Claude to run echo $USER.
  4. Observe: permission prompt, reason: Contains simple_expansion.
  5. Ask Claude to run echo "user is $USER".
  6. Observe: auto-approved.
  7. Ask Claude to run echo "$HOME".
  8. Observe: permission prompt, reason: Unhandled node type: string.

Observed behavior matrix (v2.1.92, macOS)

Auto-approved ✓:

  • ls /tmp, cat /etc/hosts, date; uptime | tail -1
  • if true; then echo y; fi
  • [[ -f /etc/hosts ]] && echo yes
  • cat <<< "hello"
  • echo "user is $USER", echo "$HOME/x", echo "h=$HOME" (string with literal content + expansion)

Prompts ✗:

Command Reason shown
echo $USER, ls $HOME Contains simple_expansion
echo "$HOME", echo "$USER" Unhandled node type: string
echo $(date) Contains command_substitution
echo $'hello world' Contains ansi_c_string
echo {a,b,c} Contains brace_expression

Expected

When autoAllowBashIfSandboxed: true and sandboxing is enabled, commands that would run inside the sandbox should auto-approve regardless of whether a static analyzer can prove them safe. The sandbox is the security boundary — that's the documented purpose of the setting. Failure to statically analyze a command shouldn't weaken the sandbox trust model.

Impact — this is severe for automated workflows

autoAllowBashIfSandboxed is the standard configuration for running Claude with meaningful autonomy while keeping a real security boundary. It's what lets an agent run an edit → test → fix loop unattended. Shell variables and command substitutions appear in practically every realistic test/build command (go test -coverprofile=\$TMPDIR/x.cov, python -m pytest --basetemp=\$(mktemp -d), cargo test --target-dir \"\$CARGO_TARGET_DIR\", etc.), so in practice this bug means:

  • Every edit+test loop now hits a prompt per iteration. Unattended runs stall.
  • The only remaining way to run unattended is --dangerously-skip-permissions, which disables the entire permission system — a massive security regression compared to "sandbox contains everything."
  • The documented safe middle ground (sandbox + auto-allow) no longer exists in practice.

Users have to choose between constant prompts or dropping all security. That's a significant workflow regression.

Partial source-level workarounds:

  • Quote the variable AND ensure the string has literal content (\"\$TMPDIR/file\" works; \"\$TMPDIR\" alone prompts).
  • No workaround for \$(...), brace expansion, ANSI-C strings, heredoc bodies, etc.

Suggested fix

When sandboxing is enabled and autoAllowBashIfSandboxed: true, check sandbox auto-allow before returning "ask" for commands the static analyzer can't handle. The sandbox contains the blast radius regardless of what the parser couldn't prove.

Environment

  • Claude Code CLI v2.1.92 (macOS, Apple Silicon)
  • sandbox.enabled: true, autoAllowBashIfSandboxed: true

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions