Architecture e Command Pipeline
O Bash Tool processa comandos por "sete camadas distintas" antes do subprocess spawn e mais tres durante o retorno do output.
tools/BashTool/-- tool definition, permissions, UI classificationtools/BashTool/bash*.ts-- security validators and permission rulesutils/bash/-- AST parsing, command splitting, ShellSnapshotutils/shell/-- BashProvider, output limits, shell detectionutils/sandbox/-- filesystem and network policy enforcement
Input/Output Schema
O tool mantem dois schemas: publico (exposto ao Claude) e completo (interno). O campo _simulatedSedEdit permanece oculto do modelo para prevenir bypass de dialogos de permissao.
O shell provider usa 2>&1 -- stderr e merged no stdout no nivel de file-descriptor. O campo stderr no output schema e portanto sempre vazio para o path normal de execucao.
Shell Snapshot System
Cada sessao captura o ambiente shell interativo uma vez em ~/.claude/shell-snapshots/snapshot-{shell}-{timestamp}-{random}.sh.
- User aliases and functions (filtradas para completion functions)
- Shell options (setopt/shopt)
- PATH export
- Embedded ripgrep shim (se system rg ausente)
- find/grep shims (em builds nativos)
Se snapshot creation falhar, a promise resolve para undefined. Comandos entao spawnam com flag -l (login shell), perdendo aliases/functions mas permanecendo funcionais. TOCTOU awareness: antes de cada comando, access(snapshotFilePath) verifica existencia do arquivo.
Command Building Pipeline (6 Stages)
buildExecCommand() monta a string completa do shell em seis estagios:
- Windows null redirect rewrite --
2>nulvira2>/dev/null - Stdin redirect decision -- append
< /dev/nullcondicionalmente - Shell quoting -- wrap em single quotes para eval
- Pipe rearrangement -- move
< /dev/nullpara primeiro segmento se pipes existem - Assembly -- source snapshot, session env, disable extglob, eval command
- Prefix formatting -- aplica CLAUDE_CODE_SHELL_PREFIX se definido
Bash faz parse da linha inteira antes de executar. Aliases de snapshots sourced so ficam disponiveis apos source rodar. Usar eval 'command' cria um segundo parse pass onde aliases estao vivos.
Apos sourcing do snapshot, extended glob patterns sao explicitamente desabilitados. Extended globs como !(pattern) podem ser triggered por filenames, criando ataques de post-validation expansion.
The 23 Security Validators
Cada validator em bashCommandIsSafe() retorna um de tres resultados: allow (early-allow, skip restantes), passthrough (sem opiniao, continua chain), ask (trigger permission dialog).
| # | Name | Purpose |
|---|---|---|
| 1 | INCOMPLETE_COMMANDS | Detects fragments (tabs, flags, operators) |
| 2 | JQ_SYSTEM_FUNCTION | Blocks jq intrinsics (env, path, builtins, debug) |
| 3 | JQ_FILE_ARGUMENTS | Blocks --args, --jsonargs, --rawfile patterns |
| 4 | OBFUSCATED_FLAGS | Detects encoded/spaced flags differing from shell |
| 5 | SHELL_METACHARACTERS | Unquoted ;, &&, ||, | compound detection |
| 6 | DANGEROUS_VARIABLES | $BASH_ENV, $ENV, $CDPATH, $IFS references |
| 7 | NEWLINES | Unquoted newlines (command separators) |
| 8-10 | DANGEROUS_PATTERNS | Substitution, input redirection, output redirection |
| 11 | IFS_INJECTION | IFS= assignment or unquoted $IFS |
| 12 | GIT_COMMIT_SUBSTITUTION | Special early-allow for git commit -m |
| 13 | PROC_ENVIRON_ACCESS | /proc/*/environ references |
| 14-16 | TOKEN/BACKSLASH/BRACE | Malformed tokens, escaped whitespace, brace expansion |
| 17-18 | CONTROL/UNICODE | Control characters, non-ASCII whitespace |
| 19-21 | HASH/ZSH/BACKSLASH | Mid-word hash, zsh commands, escaped operators |
| 22-23 | COMMENT/QUOTED | Comment-quote desync, quoted newline |
Five Pre-Computed Command Views
- originalCommand -- verbatim do Claude
- baseCommand -- primeiro token apos env vars
- unquotedContent -- double-quotes stripped
- fullyUnquotedContent -- ambos tipos de quotes stripped
- unquotedKeepQuoteChars -- content stripped mas delimiters mantidos (para mid-word-hash detection)
Compound commands sao split e validados individualmente com cap de 50. Alem disso, o sistema retorna ask como safe default. Previne "CPU-starvation DoS from exponentially growing subcommand arrays."
Permission Plumbing e Sandboxing
checkPermissions() chama bashToolHasPermission() que empilha checks independentes:
- Permission mode -- read-only mode check
- Security validators -- 23-validator chain
- Read-only constraints -- filesystem write restrictions
- Path constraints -- allowlist/denylist checking
- Sed constraints -- in-place edit detection
- Command operator permissions -- per-subcommand rule matching
Rule Matching Modes
| Mode | Example | Matches |
|---|---|---|
| Exact | Bash(git status) | Only exact command |
| Prefix | Bash(npm run:*) | npm run + anything |
| Wildcard | Bash(git * --force) | Matching glob |
sandbox.excludedCommands e uma convenience feature -- permite tools como docker ou bazel rodarem sem sandboxing porque precisam de acesso direto ao processo. Mas NAO e um controle de seguranca. O dialogo de permissao e a fronteira real de seguranca.
Background Execution e Output Handling
Tres paths produzem background execution:
- run_in_background: true -- model-initiated explicito
- User Ctrl+B -- manual background (sets backgroundedByUser: true)
- Auto-background -- apos 15 segundos em assistant mode (assistantAutoBackgrounded: true)
Size Limits
- BASH_MAX_OUTPUT_DEFAULT -- 30,000 chars (configuravel)
- BASH_MAX_OUTPUT_UPPER_LIMIT -- 150,000 chars (hard cap)
- Outputs exceeding inline limit persistem em arquivo no tool-results directory
- Full output capped em 64MB via truncation
Semantic Exit Code Interpretation
interpretCommandResult() mapeia non-zero codes para notas human-readable. Exemplo: grep retornando 1 significa "no matches" (nao erro); diff retornando 1 significa "files differ" (nao erro).
Claude Code Hints Protocol
CLIs com CLAUDECODE=1 emitem tags <claude-code-hint /> para stderr (merged no stdout). Tool registra hints, strip antes do modelo ver output. Subagent outputs tambem stripped para que hints nao escapem a fronteira do agent.
UI Classification e Sed Special Case
Comandos classificados para determinar collapse behavior default:
BASH_SEARCH_COMMANDS = {find, grep, rg, ag, ack, ...}
BASH_READ_COMMANDS = {cat, head, tail, jq, awk, ...}
BASH_LIST_COMMANDS = {ls, tree, du}
BASH_SILENT_COMMANDS = {mv, cp, rm, mkdir} // show "Done" instead of "(No output)"
Comandos matching sed -i 's/.../.../' file renderizam como file-edit operations usando FileEdit display name, provendo file-diff-style permission dialog. Para pipelines: TODAS as partes devem ser search/read para o pipeline todo ser colapsavel.
Environment Variable Stripping
Antes de rule matching, stripSafeWrappers() executa two-phase stripping:
- Phase 1: Strip leading env vars onde variable name esta em SAFE_ENV_VARS (build/locale/display vars). Notavelmente exclui PATH, LD_PRELOAD, PYTHONPATH.
- Phase 2: Strip wrapper commands (timeout, time, nice, nohup) com strict flag parsing.
Ambas fases rodam em fixed-point loop, handling interleaved patterns.
🎯 Resumo e Takeaways
Shell snapshot roda uma vez por sessao capturando aliases, functions, options, PATH sem login shell caro por comando.
6 estagios de command building com razoes especificas de seguranca/corretude para cada um.
23 validators formam defense-in-depth contra injection em niveis de character encoding, shell syntax e Zsh-specific.
Sandbox exclusions sao convenience, nao seguranca. O dialogo de permissao e a fronteira real.
Background execution tem 3 paths distinguiveis (explicit, user-initiated, auto 15s).
stderr sempre merged no stdout via 2>&1; campo stderr sempre vazio para comandos normais.