Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions