BashTool
- Entity ID:
ent-20260410-9cb9a269b007 - Type:
tool - Scope:
shared - Status:
active - Aliases: bash tool, shell execution, command execution
Description
The shell command execution tool — the most complex and security-critical tool in Claude Code. Executes arbitrary bash commands with full system access, subject to a multi-layer security pipeline: permission rules, dangerous pattern detection (23 checks), read-only validation, path constraints, optional ML classification, and sandbox wrapping. Supports streaming output, background execution, auto-backgrounding, and platform-specific shell selection.
Implementation: src/tools/BashTool/ (12,429 lines across all files)
Execution pipeline
Input (command string)
→ validateInput (sleep pattern detection)
→ checkPermissions (compound command parsing, 23 security checks, path validation)
→ shouldUseSandbox (exclusion check)
→ findSuitableShell (bash/zsh detection)
→ buildExecCommand (snapshot + init chain + eval)
→ [if sandbox] wrapWithSandbox (bubblewrap/macOS sandbox)
→ spawn(shell, ['-c', command])
→ streaming output + progress reporting
→ output processing (truncation, image detection, exit code interpretation)
→ result
Security model
23 dangerous pattern checks (bashSecurity.ts)
| # | Check | Purpose |
|---|---|---|
| 1 | Incomplete commands | Unclosed quotes/heredocs |
| 2 | jq system() | Code execution via jq |
| 3 | jq file arguments | File I/O via jq args |
| 4 | Obfuscated flags | Hidden flags via backslash escaping |
| 5 | Shell metacharacters | Pipes, redirections outside quotes |
| 6 | Dangerous variables | IFS, PATH, LD_PRELOAD assignment |
| 7 | Newlines | Hidden commands after newlines |
| 8 | Command substitution | $(), ${}, process substitution |
| 9 | Input redirection | < for hidden input |
| 10 | Output redirection | >, >> for file writes |
| 11 | IFS injection | Field separator manipulation |
| 12 | Git commit substitution | $(git rev-parse ...) patterns |
| 13 | /proc/environ access | Reading process environment |
| 14 | Malformed token injection | Shell quote parser bugs |
| 15 | Backslash-escaped whitespace | Hidden operators: \ && |
| 16 | Brace expansion | {a,b} in sensitive contexts |
| 17 | Control characters | Non-printing chars (U+0000-U+001F) |
| 18 | Unicode whitespace | Homoglyphic whitespace (U+00A0) |
| 19 | Mid-word hash | Comments mid-command |
| 20 | Zsh dangerous commands | zmodload, emulate, sysopen, etc. |
| 21 | Backslash-escaped operators | \|, \&& escaping |
| 22 | Comment-quote desync | Quote state corruption via comments |
| 23 | Quoted newline | Newlines inside quotes |
Compound command parsing
Uses Tree-sitter for AST analysis. Splits &&, ||, ;, | into subcommands. Each subcommand validated independently against permission rules via preparePermissionMatcher. Extracts argv (strips leading VAR=val assignments).
Read-only validation (readOnlyValidation.ts)
Dynamic per-command evaluation determines isConcurrencySafe:
isConcurrencySafe(input) {
return this.isReadOnly?.(input) ?? false
}
Allowlisted read-only commands with flag whitelisting: git log/show/diff/status, find, grep, rg, fd, cat, head, tail, jq, wc, stat, file, docker ps/inspect, gh (read-only subcommands). Dangerous flags are explicitly blocked (e.g., fd -x/--exec).
Sandbox integration
shouldUseSandbox() checks if sandboxing is enabled, then wraps the command via SandboxManager.wrapWithSandbox() (bubblewrap on Linux, macOS sandbox). Excluded commands configurable via GrowthBook dynamic config and user settings. Sandbox config includes: allowWrite (cwd, temp), denyWrite (settings files, .claude/skills, bare git repo files), network domain restrictions.
Shell selection
Priority: CLAUDE_CODE_SHELL env → $SHELL (if bash/zsh) → which bash/zsh → standard paths. On Windows, uses Git Bash with POSIX path conversion. Shell init chain: source snapshot → source session env (from hooks) → disable extended globs → eval command → capture cwd.
Output handling
- Streaming: stdout/stderr directed to a file fd. Progress extracted by polling file tail. Progress-driven yielding (not interval-based).
- Truncation: >30K chars inline → persist to disk (up to 64MB), model receives 5KB preview + filepath.
- Timeout: Default 30 minutes. In assistant mode, auto-backgrounds after 15s instead of killing.
- Exit code interpretation: Command-specific semantics (grep: 1=no matches, diff: 1=differences found).
Background execution
run_in_background: true spawns as LocalShellTask. Auto-backgrounding for long-running commands (npm, yarn, node, python, docker, cargo, webpack). Background tasks have a size watchdog (5s polling, kills if output exceeds limit).
Platform differences
| Aspect | Linux/macOS | Windows |
|---|---|---|
| Shell | bash or zsh from PATH | Git Bash (POSIX) |
| Paths | Native POSIX | Converted via windowsPathToPosixPath |
| File I/O | O_APPEND + O_NOFOLLOW | 'w' mode (FILE_GENERIC_WRITE) |
| Null redirect | /dev/null |
> nul rewritten to > /dev/null |
| Sandbox | bubblewrap | Not available |
What depends on it
- StreamingToolExecutor — BashTool's
isConcurrencySafedrives the concurrency model - Permission pipeline — BashTool is the most heavily permission-checked tool
- Hooks system — PreToolUse/PostToolUse hooks fire around every Bash execution
- Task system — background Bash commands become LocalShellTasks
- Agent lifecycle — agents execute Bash commands for code operations
Design trade-offs
| Decision | Trade-off |
|---|---|
| 23 security pattern checks | Defense-in-depth, but validation overhead on every command |
| Tree-sitter AST parsing | Accurate compound command analysis, but parser dependency |
| Per-command read-only evaluation | BashTool can run concurrently for read-only commands, but requires command parsing |
| Sandbox wrapping | Strong isolation, but not available on Windows |
| Auto-backgrounding | Prevents blocking on long commands, but may surprise users |
| File-based output (not pipes) | No JS overhead for large outputs, but requires file polling for progress |
Key claims
- Most complex tool: 12,429 lines across security, validation, execution, and platform handling
- 23 dangerous pattern checks provide defense-in-depth security
- Dynamic per-command read-only evaluation enables concurrent execution for safe commands
- Sandbox integration wraps commands in bubblewrap (Linux) or macOS sandbox
- Platform-specific handling for Windows (Git Bash, path conversion, null redirect rewriting)
Relations
rel-bash-executor: StreamingToolExecutor --[evaluates]--> BashTool.isConcurrencySaferel-bash-permissions: permission-pipeline --[validates]--> BashTool commandsrel-bash-sandbox: BashTool --[wraps-via]--> SandboxManagerrel-bash-tasks: BashTool --[creates]--> LocalShellTask (background)
Sources
Source code at src/tools/BashTool/, src/utils/Shell.ts, src/utils/sandbox/sandbox-adapter.ts