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.

18
Files
60+
Exported Functions
~10.9k
Lines of Code
7
Validation Layers

📚 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 / FunctionFilePurpose
BashToolBashTool.tsxMain tool definition via buildTool(); defines input/output schema, call(), validateInput(), checkPermissions()
bashToolHasPermission()bashPermissions.tsPrimary permission entry point combining mode checks, classifier, exact/wildcard matching, subcommand analysis
checkCommandAndSuggestRules()bashPermissions.tsEvaluates command against permission rules and suggests new rules for compound commands
bashCommandIsSafeAsync_DEPRECATED()bashSecurity.tsAsync safety validation with 23+ injection detection checks (zsh attacks, escape sequences, IFS injection)
checkCommandOperatorPermissions()bashCommandHelpers.tsValidates pipes, &&, || operators; detects cd+git across pipe segments
interpretCommandResult()commandSemantics.tsCommand-specific exit code mapping (grep: 1=no matches; diff: 1=differs; test: 1=false)
checkPermissionMode()modeValidation.tsMode-based auto-approval for safe filesystem commands (mkdir, touch, rm, cp, sed)
checkPathConstraints()pathValidation.tsValidates file paths for 35+ commands; blocks dangerous paths like rm -rf /
parseSedEditCommand()sedEditParser.tsParses sed commands, converts BRE to ERE regex, supports -i and -E flags
sedCommandIsAllowedByAllowlist()sedValidation.tsAllowlists safe sed patterns: print commands and substitutions with safe flags only
checkReadOnlyConstraints()readOnlyValidation.tsFlag whitelisting for git, docker, gh, pyright, ripgrep in read-only mode
shouldUseSandbox()shouldUseSandbox.tsMulti-level sandbox decision: system setting → override → dynamic config → user config
getSimplePrompt()prompt.tsSystem prompt with background task usage, commit instructions, sandbox rules
formatOutput()utils.tsTruncates output to max length with line count; strips empty lines
resetCwdIfOutsideProject()utils.tsValidates and resets working directory if it escaped project boundaries

Data Structures

TypeNameDescription
Input schemacommandShell command string to execute
Input schematimeoutOptional execution timeout in milliseconds
Input schemadescriptionHuman-readable description of command intent
Input schemarun_in_backgroundBoolean flag for background task execution
Input schemadangerouslyDisableSandboxOverride flag to skip sandboxing
Outputstdout / stderrCaptured command output streams
OutputinterruptedWhether command was terminated by timeout
OutputreturnCodeInterpretationSemantic meaning of exit code per command type
OutputbackgroundTaskIdTask ID if moved to background execution
EnumPermissionResultallow | deny | ask — permission check outcome
ConstantsBASH_SEARCH_COMMANDS8 commands classified as search operations (grep, rg, find, etc.)
ConstantsBASH_READ_COMMANDS15 commands classified as read-only file operations

Interactions

DirectionPartnerInterfaceDescription
→Shell / execrunShellCommand()Executes command in user’s shell via async generator, yielding streaming output
→Sandbox adaptersandbox-adapter.jsRoutes risky commands to container-based sandbox for safe execution
→Permission systemToolPermissionContextQueries allow/deny rules, user confirmation dialogs
→Git trackinggitOperationTracking.tsRecords commits, branch actions, PR operations performed via bash
→AnalyticslogEvent()Telemetry for command execution, permission outcomes
←AgentLooptool_use blockReceives command invocation from model’s tool call
←AgentToolfilterToolsForAgent()Sub-agents may include BashTool in their filtered tool set
→Task systemLocalAgentTaskBackground commands registered as async tasks with progress streaming
→FileEditToolsedEditParserSed 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:

  1. Mode auto-approval — In acceptEdits mode, safe filesystem commands (mkdir, touch, rm, rmdir, mv, cp, sed) are auto-approved
  2. Classifier evaluation — Async risk assessment classifier evaluates command semantics
  3. Exact rule matching — User-defined rules checked for exact command string match
  4. Wildcard/prefix matching — Glob-style pattern rules evaluated against normalized command
  5. Operator decomposition — Compound commands (&&, ||, pipes) split and each segment validated
  6. Subcommand analysis — Up to 50 subcommands analyzed with suggested rules for compounds
  7. User confirmation — If no rule matches, prompt user for explicit approval

Configuration

Knob / ParameterDefaultDescription
timeout60sDefault command execution timeout; configurable per invocation
PROGRESS_THRESHOLD2000msMinimum interval between streaming progress updates to UI
ASSISTANT_BLOCKING_BUDGET15000msMax time before auto-backgrounding a long-running command
MAX_SUBCOMMANDS_FOR_SECURITY_CHECK50Maximum subcommands analyzed in compound command security check
MAX_SUGGESTED_RULES_FOR_COMPOUND5Maximum permission rules suggested for compound commands
maxResultSizeChars30KBOutput exceeding this is persisted to disk; model gets preview + path
shouldUseSandbox()dynamicMulti-level: system → override flag → dynamic config → user config
acceptEdits mode commands6 commandsAuto-approved in acceptEdits mode: mkdir, touch, rm, rmdir, mv, cp, sed
ZSH_DANGEROUS_COMMANDS30+ commandsBlocked zsh builtins: zmodload, sysopen, sysread, ztcp, zsocket, zpty, etc.
BASH_SILENT_COMMANDS11 commandsCommands whose output is collapsed in the UI for cleaner display
CLAUDE_CODE_SIMPLEfalseEnv var: when true, restricts to Bash/Read/Edit tools only
_simulatedSedEditunsetInternal 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:

CommandExit 0Exit 1Exit ≥2
grep / rgMatches foundNo matches (not an error)Actual error
diffFiles identicalFiles differ (expected)Actual error
test / [Condition trueCondition falseActual error
findSuccessPartial resultsActual 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.

Extracted topic page

Security & Injection Detection Focus

23+ async safety checks: command substitution, zsh attacks, escape sequences, IFS injection, unicode whitespace, and process substitution detection.

Extracted topic page

Sandbox Execution Focus

Container-based sandboxing decision logic, exclusion patterns, wrapper stripping, and the multi-level configuration cascade from system to user settings.

Extracted topic page

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.

Extracted topic page

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;
}