tools/BashTool/ — Shell Command Execution Tool
Executes shell commands in the user’s terminal with streaming output and security validation
Core of the agentic coding workflow — runs builds, tests, git commands, file operations, and arbitrary shell invocations. Handles streaming output, timeouts, working directory management, sandboxing, and a multi-layered security stack that validates every command before execution.
📚 What is this & why does it exist?
BashTool is the primary way Claude Code interacts with the user’s operating system. When the AI needs to run a build, execute tests, install packages, or perform any shell operation, it invokes BashTool. The tool streams output back in real-time, handles timeouts for long-running processes, and enforces strict security rules to prevent destructive or malicious commands.
💡 Real-world analogy
Think of BashTool as a remote control for your terminal — the AI can press buttons, but security guards (permission layers) check each command first. Just like a hotel concierge who can call any department on your behalf, but only after verifying your room key and making sure you’re not asking them to open someone else’s safe. The concierge (BashTool) can run errands in the background, report back with results, and even switch departments (working directories), but always within the boundaries of what’s allowed.
Contents
Architecture
flowchart TD
A["AgentLoop / Model"] -->|"tool_use: Bash"| B["BashTool.call()"]
B --> C{"validateInput()"}
C -->|"invalid"| D["Return error + errorCode"]
C -->|"valid"| E{"checkPermissions()"}
E -->|"deny"| F["Permission denied"]
E -->|"ask"| G["User confirmation dialog"]
E -->|"allow"| H["runShellCommand()"]
G -->|"approved"| H
H --> I["Streaming output via async generator"]
I --> J{"Timeout / background?"}
J -->|"background"| K["Register background task"]
J -->|"complete"| L["interpretCommandResult()"]
L --> M["Format + return stdout/stderr"]
Security Validation Stack
flowchart LR
CMD["Command string"] --> MV["modeValidation"]
MV --> PV["pathValidation"]
PV --> SV["sedValidation"]
SV --> RO["readOnlyValidation"]
RO --> CS["commandSemantics"]
CS --> OP["operatorPermissions"]
OP --> SB{"shouldUseSandbox?"}
SB -->|"yes"| SX["Sandbox execution"]
SB -->|"no"| DX["Direct execution"]
Key Classes & Functions
| Class / Function | File | Purpose |
|---|---|---|
BashTool | BashTool.tsx | Main tool definition via buildTool(); defines input/output schema, call(), validateInput(), checkPermissions() |
bashToolHasPermission() | bashPermissions.ts | Primary permission entry point combining mode checks, classifier, exact/wildcard matching, subcommand analysis |
checkCommandAndSuggestRules() | bashPermissions.ts | Evaluates command against permission rules and suggests new rules for compound commands |
bashCommandIsSafeAsync_DEPRECATED() | bashSecurity.ts | Async safety validation with 23+ injection detection checks (zsh attacks, escape sequences, IFS injection) |
checkCommandOperatorPermissions() | bashCommandHelpers.ts | Validates pipes, &&, || operators; detects cd+git across pipe segments |
interpretCommandResult() | commandSemantics.ts | Command-specific exit code mapping (grep: 1=no matches; diff: 1=differs; test: 1=false) |
checkPermissionMode() | modeValidation.ts | Mode-based auto-approval for safe filesystem commands (mkdir, touch, rm, cp, sed) |
checkPathConstraints() | pathValidation.ts | Validates file paths for 35+ commands; blocks dangerous paths like rm -rf / |
parseSedEditCommand() | sedEditParser.ts | Parses sed commands, converts BRE to ERE regex, supports -i and -E flags |
sedCommandIsAllowedByAllowlist() | sedValidation.ts | Allowlists safe sed patterns: print commands and substitutions with safe flags only |
checkReadOnlyConstraints() | readOnlyValidation.ts | Flag whitelisting for git, docker, gh, pyright, ripgrep in read-only mode |
shouldUseSandbox() | shouldUseSandbox.ts | Multi-level sandbox decision: system setting → override → dynamic config → user config |
getSimplePrompt() | prompt.ts | System prompt with background task usage, commit instructions, sandbox rules |
formatOutput() | utils.ts | Truncates output to max length with line count; strips empty lines |
resetCwdIfOutsideProject() | utils.ts | Validates and resets working directory if it escaped project boundaries |
Data Structures
| Type | Name | Description |
|---|---|---|
| Input schema | command | Shell command string to execute |
| Input schema | timeout | Optional execution timeout in milliseconds |
| Input schema | description | Human-readable description of command intent |
| Input schema | run_in_background | Boolean flag for background task execution |
| Input schema | dangerouslyDisableSandbox | Override flag to skip sandboxing |
| Output | stdout / stderr | Captured command output streams |
| Output | interrupted | Whether command was terminated by timeout |
| Output | returnCodeInterpretation | Semantic meaning of exit code per command type |
| Output | backgroundTaskId | Task ID if moved to background execution |
| Enum | PermissionResult | allow | deny | ask — permission check outcome |
| Constants | BASH_SEARCH_COMMANDS | 8 commands classified as search operations (grep, rg, find, etc.) |
| Constants | BASH_READ_COMMANDS | 15 commands classified as read-only file operations |
Interactions
| Direction | Partner | Interface | Description |
|---|---|---|---|
| → | Shell / exec | runShellCommand() | Executes command in user’s shell via async generator, yielding streaming output |
| → | Sandbox adapter | sandbox-adapter.js | Routes risky commands to container-based sandbox for safe execution |
| → | Permission system | ToolPermissionContext | Queries allow/deny rules, user confirmation dialogs |
| → | Git tracking | gitOperationTracking.ts | Records commits, branch actions, PR operations performed via bash |
| → | Analytics | logEvent() | Telemetry for command execution, permission outcomes |
| ← | AgentLoop | tool_use block | Receives command invocation from model’s tool call |
| ← | AgentTool | filterToolsForAgent() | Sub-agents may include BashTool in their filtered tool set |
| → | Task system | LocalAgentTask | Background commands registered as async tasks with progress streaming |
| → | FileEditTool | sedEditParser | Sed edits can be simulated via FileEditTool for diff-tracked modifications |
Permission Resolution Flow
The permission system uses a fail-closed strategy where commands must be explicitly allowed. The resolution order is:
- Mode auto-approval — In
acceptEditsmode, safe filesystem commands (mkdir, touch, rm, rmdir, mv, cp, sed) are auto-approved - Classifier evaluation — Async risk assessment classifier evaluates command semantics
- Exact rule matching — User-defined rules checked for exact command string match
- Wildcard/prefix matching — Glob-style pattern rules evaluated against normalized command
- Operator decomposition — Compound commands (
&&,||, pipes) split and each segment validated - Subcommand analysis — Up to 50 subcommands analyzed with suggested rules for compounds
- User confirmation — If no rule matches, prompt user for explicit approval
Configuration
| Knob / Parameter | Default | Description |
|---|---|---|
timeout | 60s | Default command execution timeout; configurable per invocation |
PROGRESS_THRESHOLD | 2000ms | Minimum interval between streaming progress updates to UI |
ASSISTANT_BLOCKING_BUDGET | 15000ms | Max time before auto-backgrounding a long-running command |
MAX_SUBCOMMANDS_FOR_SECURITY_CHECK | 50 | Maximum subcommands analyzed in compound command security check |
MAX_SUGGESTED_RULES_FOR_COMPOUND | 5 | Maximum permission rules suggested for compound commands |
maxResultSizeChars | 30KB | Output exceeding this is persisted to disk; model gets preview + path |
shouldUseSandbox() | dynamic | Multi-level: system → override flag → dynamic config → user config |
acceptEdits mode commands | 6 commands | Auto-approved in acceptEdits mode: mkdir, touch, rm, rmdir, mv, cp, sed |
ZSH_DANGEROUS_COMMANDS | 30+ commands | Blocked zsh builtins: zmodload, sysopen, sysread, ztcp, zsocket, zpty, etc. |
BASH_SILENT_COMMANDS | 11 commands | Commands whose output is collapsed in the UI for cleaner display |
CLAUDE_CODE_SIMPLE | false | Env var: when true, restricts to Bash/Read/Edit tools only |
_simulatedSedEdit | unset | Internal input field: routes sed edits through FileEditTool for diff tracking |
Command-Specific Exit Code Semantics
BashTool interprets exit codes differently depending on the command, preventing false “error” reports for commands where non-zero codes are normal:
| Command | Exit 0 | Exit 1 | Exit ≥2 |
|---|---|---|---|
grep / rg | Matches found | No matches (not an error) | Actual error |
diff | Files identical | Files differ (expected) | Actual error |
test / [ | Condition true | Condition false | Actual error |
find | Success | Partial results | Actual error |
Directory Map
Source tree for BashTool
BashTool/
├── BashTool.tsx ← Main tool definition: call(), validateInput(), checkPermissions()
├── BashToolResultMessage.tsx ← React component for rendering command output in UI
├── UI.tsx ← Tool use message rendering, user-facing name logic
├── bashCommandHelpers.ts ← Operator permission checking for pipes, &&, ||
├── bashPermissions.ts ← Primary permission layer (2621 LOC): rule matching, classifier
├── bashSecurity.ts ← Injection detection (2592 LOC): 23+ async safety checks
├── commandSemantics.ts ← Exit code interpretation per command type (grep, diff, test)
├── commentLabel.ts ← UI label generation for command display
├── destructiveCommandWarning.ts ← Warning messages for destructive operations
├── modeValidation.ts ← Permission mode auto-approval for safe filesystem commands
├── pathValidation.ts ← File path validation for 35+ commands (1303 LOC)
├── prompt.ts ← System prompt: background tasks, git, sandbox instructions
├── readOnlyValidation.ts ← Flag whitelisting for read-only mode (1990 LOC)
├── sedEditParser.ts ← Sed command parsing, BRE→ERE regex conversion
├── sedValidation.ts ← Sed pattern allowlisting (print + substitution only)
├── shouldUseSandbox.ts ← Multi-level sandboxing decision logic
├── toolName.ts ← Tool name constant and aliases
└── utils.ts ← Output formatting, image handling, CWD reset
Deep-Dive Highlights
Core mechanisms from BashTool have been extracted into dedicated focus pages for in-depth exploration. Each page covers one algorithm, protocol, or subsystem with diagrams, code walks, and configuration details.
Deep-Dive Pages
Permission System Focus
Multi-layer permission checking: mode validation, classifier evaluation, exact/wildcard rule matching, subcommand analysis, and rule suggestion generation.
Security & Injection Detection Focus
23+ async safety checks: command substitution, zsh attacks, escape sequences, IFS injection, unicode whitespace, and process substitution detection.
Sandbox Execution Focus
Container-based sandboxing decision logic, exclusion patterns, wrapper stripping, and the multi-level configuration cascade from system to user settings.
Streaming & Background Tasks Focus
Async generator-based output streaming, progress throttling, auto-background after blocking budget, large output persistence to disk, and image data URI handling.
Code Examples
Example: Permission Flow Entry Point
// bashPermissions.ts — Main permission entry point
export async function bashToolHasPermission(
input: { command: string; timeout?: number },
context: ToolPermissionContext,
): Promise<PermissionResult> {
// 1. Mode-based auto-approval (acceptEdits allows mkdir, touch, rm, etc.)
const modeResult = checkPermissionMode(input.command, context);
if (modeResult) return modeResult;
// 2. Async classifier evaluation for risk assessment
const classifierResult = await evaluateClassifier(input, context);
if (classifierResult.behavior === 'deny') return classifierResult;
// 3. Exact command matching against user-defined rules
const exactMatch = matchExactRule(input.command, context.rules);
if (exactMatch) return exactMatch;
// 4. Operator permission checking (pipes, &&, ||)
const operatorResult = checkCommandOperatorPermissions(input.command, context);
if (operatorResult.behavior !== 'allow') return operatorResult;
// 5. Subcommand analysis for compound commands
return checkCommandAndSuggestRules(input.command, context);
}
Example: Command Exit Code Interpretation
// commandSemantics.ts — Semantic exit code mapping
export function interpretCommandResult(
command: string,
exitCode: number,
): string | undefined {
const baseCmd = command.split(/\s+/)[0];
switch (baseCmd) {
case 'grep': case 'rg':
// 0 = matches found, 1 = no matches (not an error), ≥2 = real error
if (exitCode === 1) return 'No matches found (not an error)';
if (exitCode >= 2) return 'Error occurred during search';
break;
case 'diff':
// 0 = identical, 1 = differences found, ≥2 = error
if (exitCode === 1) return 'Files differ (expected behavior)';
break;
case 'test': case '[':
// 0 = condition true, 1 = condition false, ≥2 = error
if (exitCode === 1) return 'Condition evaluated to false';
break;
}
return undefined;
}