10 / 20 Security — 5-Layer Defense src/tools/BashTool/bashSecurity.ts (2,592 lines)

6 layers. 24 bash validators. Some never bypass.

Each layer is independent — compromising one doesn't defeat the others. The bash security system uses tree-sitter AST analysis with regex fallback. An ML classifier (internal-only) adds context-aware decisions. Server-side killswitches can remotely disable bypass/auto mode.

2,592
Lines in bashSecurity.ts alone
24+
Validators — each catches a specific attack class
50
Max subcommands parsed per compound command
5
Never-bypassable protections

Defense-in-Depth Stack

5Sandboxing@anthropic-ai/sandbox-runtime macOS native sandbox. Network domain allowlist. Filesystem read/write restrictions. Ripgrep path deny patterns. Policy allowManagedDomainsOnly cannot be overridden.
4Pre/Post Tool Use Hooks — User-configured shell commands in settings.json. Run before/after any tool call. Can intercept, transform output, trigger alerts. 15s default timeout. Cannot block execution (PostToolUse only) — but PreToolUse hooks CAN block.
3Tool-Level Checks — 24+ bash validators (tree-sitter AST + regex fallback). PowerShell AST analysis. Path validation (symlinks, traversal, Windows UNC). Destructive command detection (rm -rf, DROP TABLE, kubectl delete). Max 50 subcommands per compound command.
2Permission Rules — 8-source priority: Policy → User → Project → Local → CLI → cliArg → command → session. Pattern matching: Bash(git *), Write(**/.env). Allow/deny/ask per tool+pattern.
1Permission Modes — default (prompt), acceptEdits (auto-allow files), bypassPermissions (skip all), plan (read-only), dontAsk (auto-deny), auto (ML classifier, ANT-only), bubble (fork → parent).
0Authentication — OAuth 2.0 + PKCE to claude.ai. Trusted device tokens in macOS Keychain (90-day expiry). JWT proactive refresh (5min before expiry). Session ingress tokens (env var → file descriptor → well-known path).

Bash Validators — What They Catch

Each validator targets a specific attack vector. Tree-sitter AST parsing when available; regex fallback when not. If unsure, defaults to 'ask' (safe default).

CategoryValidatorsExample Attacks Blocked
InjectionCOMMAND_SUBSTITUTION, SHELL_METACHARACTERS, NEWLINES$(rm -rf /), unquoted $ / backticks, \n injection
Variable abuseDANGEROUS_VARIABLES, IFS_INJECTIONIFS=/ cmd, LD_PRELOAD, BASH_ENV manipulation
ObfuscationOBFUSCATED_FLAGS, BACKSLASH_ESCAPED_OPERATORS/WHITESPACE- -rf (space in flags), \; / \| (escape desync)
UnicodeUNICODE_WHITESPACE, CONTROL_CHARACTERSU+2003 em-space (zsh parsing bypass), invisible 0x00-0x1F bytes
QuotingCOMMENT_QUOTE_DESYNC, QUOTED_NEWLINE, MID_WORD_HASHUnclosed quotes hiding code, newlines in "...", # comment desync
ExpansionBRACE_EXPANSION, HEREDOC_EXPANSION{a,b} near metacharacters, heredoc with $() expansion
Zsh-specificZSH_DANGEROUS_COMMANDS (16 patterns)zmodload, sysopen, sysread, ztcp, mapfile, emulate
Data exfilJQ_SYSTEM_FUNCTION, JQ_FILE_ARGUMENTS, PROC_ENVIRON_ACCESSjq @base64d+system(), jq file args, /proc/self/environ
GitGIT_COMMIT_SUBSTITUTIONCode injection via git commit message $(cmd)
StructuralINCOMPLETE_COMMANDS, MALFORMED_TOKEN_INJECTIONFragments, broken shell-quoting bypasses

ML Classifier (ANT-Only)

A context-aware classifier that can approve rm /tmp/test but deny rm /root/secret — understands intent, not just patterns.

How it works

Takes: command text + CWD + behavior descriptions (deny/ask/allow). Returns: { matches, confidence: high|medium|low, reason, matchedDescription }. Two-stage: Fast initial pass + Thinking reasoning if needed. Tracks API tokens for analytics.

External build: stubbed

isClassifierPermissionsEnabled() returns false. All functions return { matches: false, confidence: 'high', reason: 'disabled' }. External users rely on pattern-based validators only. Feature gate: TRANSCRIPT_CLASSIFIER.

Never Bypassable — Hardcoded Protections

  • .git/ directory protection — blocks writes to hooks/, HEAD, objects/, refs/. Handles path escapes (..\HEAD), NTFS 8.3 short names (GIT~1), case-insensitive on Windows.
  • Symlink attack prevention — checks permissions on BOTH original path AND resolved path. symlink /tmp/safe → /etc/shadow caught because resolved path is checked.
  • Dangerous files — .gitconfig, .gitmodules, .bashrc, .bash_profile, .zshrc, .zprofile, .profile, .ripgreprc, .mcp.json, .claude.json. checkEditableInternalPath runs BEFORE working dir check.
  • Dangerous directories — .git/, .vscode/, .idea/, .claude/
  • Policy sandbox restrictionsallowManagedDomainsOnly cannot be overridden by user rules or bypassPermissions mode.

Server-Side Killswitches

GateEffect
tengu_sessions_elevated_auth_enforcementDisables bypassPermissions mode entirely. Checked on startup. Reverts to 'default' if flipped.
tengu_auto_mode_configDisables auto mode (ML classifier). Checked on model/fast-mode change. Can also disable fast mode via disableFastMode circuit breaker.

OAuth 2.0 + PKCE Authentication

1
Generate PKCE pair
code_verifier (crypto random) + code_challenge = SHA256(verifier)
2
Redirect to /authorize
code_challenge + method=S256 sent to claude.ai
3
User authenticates in browser
auth_code returned via redirect
4
Exchange code + verifier → tokens
access_token + refresh_token. Stored in macOS Keychain (or encrypted file).