Security Analysis of Claude Code v2.1.88 — Source Reconstructed from Source Maps
A deep-dive security review of Claude Code's internals, reconstructed from its published source maps. Covers permission sandboxing, shell execution, credential handling, remote killswitches, and 10 findings.
On this page
Security Analysis Report: Claude Code v2.1.88
Date: 2026-03-31
Source: Reconstructed from cli.js.map (source map) in @anthropic-ai/claude-code@2.1.88
Total source files: 4,756 (1,902 under src/, rest are node_modules/ and vendor/)
Method: Static code review of extracted TypeScript/TSX source
Table of Contents
- Executive Summary
- Architecture Overview
- Secrets & Credential Handling
- Authentication & Authorization
- Permission Model & Sandbox
- Remote Feature Gates (GrowthBook/Statsig)
- Bash / Shell Execution Security
- Path Traversal & Filesystem Protection
- Network Security (WebFetch)
- Code Injection Analysis
- Prompt Injection & Data Leakage
- Supply Chain Security
- Internal / “Ant-Only” Features
- Findings Summary Table
- Recommendations
1. Executive Summary
Claude Code v2.1.88 demonstrates a mature, defense-in-depth security architecture. The codebase uses multi-stage validation for shell commands, comprehensive path sanitization, PKCE-based OAuth, OS keychain credential storage, and a layered permission system with remote killswitch capability.
No critical vulnerabilities were found. The most notable concerns are:
- A
bypassPermissionsmode that disables all permission checks (mitigated by a remote killswitch) - Dangerous shell pattern blocking that is partially restricted to internal (“ant”) users only
- Tree-sitter bash parser fallback to less robust regex analysis in external builds
- A 5-minute cache TTL for domain safety checks, allowing brief windows after domain compromise
Overall risk assessment: LOW
2. Architecture Overview
Security Decision Flow
User Input / AI Tool Call
│
▼
┌─────────────────────┐
│ Permission System │ ← Rules: deny → ask → allow → default(ask)
│ (permissions.ts) │ ← Remote gates (GrowthBook/Statsig)
└────────┬────────────┘
│
┌────┴────┐
│ │
▼ ▼
┌────────┐ ┌────────────┐
│ Bash │ │ File Tools │
│ Tool │ │ Read/Write │
└───┬────┘ └─────┬──────┘
│ │
▼ ▼
┌────────────┐ ┌──────────────────┐
│ Multi-stage│ │ Path validation │
│ parse → │ │ Traversal check │
│ detect → │ │ Dangerous files │
│ semantic → │ │ Symlink protect │
│ permission │ │ Case-insensitive │
└────────────┘ └──────────────────┘
Key Components
| Component | Path | Purpose |
|---|---|---|
| Permission engine | utils/permissions/ | Multi-mode permission system with rule matching |
| Bash security | utils/bash/bashSecurity.ts, ast.ts, commands.ts | Multi-stage command validation |
| Shell execution | utils/Shell.ts | Process spawning with sandbox wrapping |
| Credential storage | utils/secureStorage/ | macOS Keychain + plaintext fallback |
| WebFetch | tools/WebFetchTool/ | URL validation, SSRF protection, domain blocking |
| Feature gates | services/analytics/growthbook.ts | Remote killswitches via GrowthBook/Statsig |
| Subprocess scrubbing | utils/subprocessEnv.ts | Strip secrets from child process environments |
| Undercover mode | utils/undercover.ts | Prevent internal codename leakage |
3. Secrets & Credential Handling
3.1 Secure Storage
Primary: macOS Keychain (utils/secureStorage/macOsKeychainStorage.ts)
- Credentials stored in macOS Keychain using hex encoding
- Hex encoding prevents credential visibility in process monitors (addresses CrowdStrike detection concerns noted in comments)
Fallback: Plaintext File (utils/secureStorage/plainTextStorage.ts)
- Stores credentials in
~/.claude/.credentials.json - File permissions set to
0o600(owner read/write only) at line 61 - Explicit warning returned to user:
"Warning: Storing credentials in plaintext."(line 64) - Used only when OS keychain is unavailable
3.2 API Key Handling
ANTHROPIC_API_KEYretrieved via centralizedgetAnthropicApiKey()function- Keys are never logged directly — auth status logged as booleans:
has Authorization header: ${!!customHeaders['Authorization']}(services/api/client.ts:120) - API key display truncated in UI:
sk-ant-...{last chars}(components/ApproveApiKey.tsx:63-71) - Keys normalized/hashed for config storage via
normalizeApiKeyForConfig()
3.3 Subprocess Environment Scrubbing
File: utils/subprocessEnv.ts
When CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is enabled (automatically set in GitHub Actions with untrusted content), the following are stripped from child process environments:
| Category | Variables Stripped |
|---|---|
| Anthropic auth | ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_AUTH_TOKEN, ANTHROPIC_FOUNDRY_API_KEY, ANTHROPIC_CUSTOM_HEADERS |
| OTEL headers | OTEL_EXPORTER_OTLP_HEADERS, ..._LOGS_HEADERS, ..._METRICS_HEADERS, ..._TRACES_HEADERS |
| Cloud credentials | AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN, AWS_BEARER_TOKEN_BEDROCK, GOOGLE_APPLICATION_CREDENTIALS, AZURE_CLIENT_SECRET, AZURE_CLIENT_CERTIFICATE_PATH |
| GitHub Actions OIDC | ACTIONS_ID_TOKEN_REQUEST_TOKEN, ACTIONS_ID_TOKEN_REQUEST_URL |
| GitHub Actions runtime | ACTIONS_RUNTIME_TOKEN, ACTIONS_RUNTIME_URL |
| Action-specific | ALL_INPUTS, OVERRIDE_GITHUB_TOKEN, DEFAULT_WORKFLOW_TOKEN, SSH_SIGNING_KEY |
Also strips INPUT_<NAME> duplicates that GitHub Actions auto-creates for with: inputs.
Note: GITHUB_TOKEN/GH_TOKEN are intentionally not scrubbed — wrapper scripts need them, and they are job-scoped and expire when the workflow ends.
3.4 Client-Side Secret Scanning
File: services/teamMemorySync/secretScanner.ts
Before uploading to team memory, a secret scanner with 40+ gitleaks-based rules checks for:
- Anthropic API keys (
sk-ant-*pattern) - AWS, GCP, Azure credentials
- GitHub PATs, Slack tokens, etc.
Detected secrets are replaced with [REDACTED] before storage.
4. Authentication & Authorization
4.1 OAuth Implementation
File: services/oauth/client.ts
- Uses PKCE (Proof Key for Code Exchange) flow with
code_challengeandcode_verifier - OAuth parameters redacted from logs via
redactSensitiveUrlParams()inservices/mcp/auth.ts:100-125— stripsstate,nonce,code_challenge,code_verifier,code - Token refresh with proper scoping
- CSRF protection via
stateandnonceparameters
4.2 Supported Auth Methods
File: services/api/client.ts
| Provider | Mechanism |
|---|---|
| Anthropic Direct | x-api-key header |
| OAuth | Authorization: Bearer token |
| AWS Bedrock | AWS_BEARER_TOKEN_BEDROCK |
| Azure Foundry | Azure client credentials |
| GCP Vertex AI | Google application credentials |
4.3 MCP Server Authentication
File: services/mcp/auth.ts
- Implements OAuth discovery per RFC 9728 & RFC 8414
- Token caching and refresh
- Error normalization for non-standard OAuth servers (e.g., Slack)
- Authorization server metadata discovery
4.4 MCP Header Helpers
File: services/mcp/headersHelper.ts
- Dynamic headers can be fetched from helper scripts
- Trust dialog validation required before executing
headersHelperfrom project/local settings (lines 20-56) - Output validated: must be a JSON object with string-only values (lines 79-97)
5. Permission Model & Sandbox
5.1 Permission Modes
File: utils/permissions/PermissionMode.ts
| Mode | Behavior | Color |
|---|---|---|
default | Ask user for each tool use | Normal |
plan | Plan mode with approval required | Blue |
acceptEdits | Auto-approve file edits within working directory | Green |
bypassPermissions | Skip ALL permission checks | Red |
dontAsk | Auto-deny without prompting | Red |
auto | ML classifier-based auto-approval (ant-only, requires TRANSCRIPT_CLASSIFIER feature flag) | Yellow |
5.2 Permission Rule System
Files: utils/permissions/PermissionRule.ts, permissionRuleParser.ts
Rules are parsed into Tool + RuleContent + Behavior triplets:
- Rules evaluated in order: deny → ask → allow
- Prefix matching for patterns like
Bash(python:*),PowerShell(rm:*) - Wildcard patterns for complex cases
- Rule sources: config file, CLI flags, session, or slash commands
5.3 Dangerous Pattern Stripping in Auto Mode
File: utils/permissions/permissionSetup.ts:94-147
When entering auto mode, rules matching dangerous interpreters are automatically stripped. The matcher handles variants: exact match, :*, *, *, -*.
This prevents rules like Bash(python:*) from auto-allowing arbitrary code execution in auto mode.
5.4 Dangerous Shell Patterns
File: utils/permissions/dangerousPatterns.ts
Cross-platform (all users):
python, python3, python2, node, deno, tsx, ruby, perl, php, lua,
npx, bunx, npm run, yarn run, pnpm run, bun run,
bash, sh, ssh
Additional patterns (ant-only, lines 58-79):
zsh, fish, eval, exec, env, xargs, sudo,
fa run, coo, gh, gh api, curl, wget, git, kubectl, aws, gcloud, gsutil
Finding: External users do not get protection against auto-allowing
curl,wget,git,gh,kubectl,aws,gcloud, etc. These are only blocked for internal Anthropic users. See Finding #2.
5.5 bypassPermissions Mode
File: utils/permissions/PermissionMode.ts:66-71
This mode completely disables all permission checks. It is:
- Shown in red (
color: 'error') as a visual danger indicator - Controlled by a remote killswitch (see Section 6)
- Activated via
--dangerously-skip-permissionsCLI flag
5.6 Filesystem Permission Controls
File: utils/permissions/filesystem.ts
Protected files (auto-edit blocked):
.gitconfig, .gitmodules, .bashrc, .bash_profile,
.zshrc, .zprofile, .profile, .ripgreprc, .mcp.json, .claude.json
Protected directories:
.git, .vscode, .idea, .claude
- Case-insensitive path comparison prevents bypass via mixed-case paths on macOS/Windows (e.g.,
.cLauDe/Settings.locaL.json) - Skill scope validation with traversal checks and glob metacharacter rejection
6. Remote Feature Gates (GrowthBook/Statsig)
6.1 Architecture
File: services/analytics/growthbook.ts
Claude Code uses GrowthBook (a feature flagging/A/B testing platform) and Statsig for remote configuration. These control:
tengu_disable_bypass_permissions_mode— killswitch for bypassPermissions modeTRANSCRIPT_CLASSIFIER— gates the auto mode ML classifier- Various other feature flags for security and functionality
6.2 bypassPermissions Killswitch
File: utils/permissions/bypassPermissionsKillswitch.ts
The flow:
- User opts into
bypassPermissionsmode locally - Before the first query, Claude Code calls
shouldDisableBypassPermissions() - This checks the
tengu_disable_bypass_permissions_modegate via GrowthBook - If gate returns
true→ the client forcibly downgrades the user out of bypass mode - If gate returns
falseor is unreachable → bypass mode stays as user set it
Key properties:
- One-way only: Anthropic can revoke bypassPermissions remotely, but cannot grant it — the user must opt in locally
- Fail-open: If GrowthBook is unreachable, defaults to
false(don’t disable) — seegrowthbook.ts:887-888:// No cache - return false (don't block on init for uncached gates) - Runs once: Only checks before the first query per session (
bypassPermissionsCheckRanflag) - Resets on login:
resetBypassPermissionsCheck()re-runs the check after/loginto pick up new org context
Override chain (checkSecurityRestrictionGate, lines 854-862):
- Environment variable overrides (for eval harnesses)
- Config file overrides
- Statsig cache (safety fallback from previous session)
- GrowthBook cache
- Default:
false
6.3 Auto Mode Gate
File: utils/permissions/bypassPermissionsKillswitch.ts:74-117
Similar pattern for auto mode:
- Gated on
TRANSCRIPT_CLASSIFIERfeature flag (build-time) - Runtime gate via
verifyAutoModeGateAccess()with GrowthBook - Re-checks when model or fast mode changes
- Can display notification warnings to user
7. Bash / Shell Execution Security
7.1 Multi-Stage Validation Pipeline
Command Input
│
▼
┌─────────────────────────────┐
│ Stage 1: AST Parsing │ tree-sitter-bash (preferred)
│ (ast.ts, parser.ts) │ or shell-quote + regex (fallback)
├─────────────────────────────┤
│ Stage 2: Dangerous Patterns │ 23+ security checks
│ (bashSecurity.ts) │ Zsh-specific bypass detection
├─────────────────────────────┤
│ Stage 3: Semantic Analysis │ Redirect validation
│ (commands.ts) │ Environment variable tracking
├─────────────────────────────┤
│ Stage 4: Permission Match │ argv[0] + subcommands vs rules
│ (bashPermissions.ts) │ Wrapper handling (timeout, nohup)
└─────────────────────────────┘
7.2 AST Parsing (Stage 1)
File: utils/bash/ast.ts, utils/bash/parser.ts
- Primary: tree-sitter-bash parser for full AST analysis
- Fallback: shell-quote library + regex (external builds where tree-sitter is unavailable)
- Fail-closed:
PARSE_ABORTEDsentinel (line 93) distinguishes timeout/panic from successful null parse - Allowlist of safe AST node types; anything not explicitly handled becomes
too-complex(requiring user approval) - Conservative variable assignment tracking and resolution
Finding: The tree-sitter fallback path is less robust. External builds using regex-based parsing are more susceptible to parser differential attacks. See Finding #3.
7.3 Dangerous Pattern Detection (Stage 2)
File: utils/bash/bashSecurity.ts
23+ security checks (BASH_SECURITY_CHECK_IDS, lines 76-101) including:
- Command substitution (
$(...),`...`) - Process substitution (
<(...),>(...)) - IFS injection
- Control characters
- Unicode whitespace (U+00A0, U+2000-U+200B, etc.)
- Brace expansion with quotes
- Heredoc extraction
Zsh-specific bypass detection (lines 22-73):
- Zsh equals expansion:
=cmd→/usr/bin/cmd - Zsh process substitution:
=(cmd) - Zsh module loading:
zmodload,zpty,ztcp - PowerShell comment syntax:
<#
7.4 Shell Quoting Security
File: utils/bash/shellQuote.ts
- Safe wrapper around
shell-quotelibrary with error handling - Strict input type validation (lines 47-95)
- Detects malformed tokens and unterminated quotes (lines 117-143)
- Cryptographic placeholder salt: 8 random bytes in hex (lines 20-36) prevent injection attacks like
sort __SINGLE_QUOTE__ hello --help - Checks for unbalanced braces/parentheses
7.5 Command Continuation Handling
File: utils/bash/commands.ts:106-139
- Correctly handles
\<newline>line continuations with odd/even backslash counting - Prevents exploits like
tr\<NL>aceroutebeing parsed as two commands - Heredocs extracted before parsing and restored after, to work around shell-quote limitations
7.6 Output Redirection Security
File: utils/bash/commands.ts:42-81
- Only static redirections to
/dev/nulland stderr-to-stdout pipes are stripped - Dynamic targets (variables, command substitutions, globs, tilde expansion) are rejected and require user approval
7.7 Shell Providers
Bash (utils/shell/bashProvider.ts):
- Disables extglob patterns (line 45)
- Commands wrapped with
evalfor alias expansion - Safe
pwd -P >| quoted_pathredirection
PowerShell (utils/shell/powershellProvider.ts):
- Uses
-EncodedCommandwith base64 UTF-16LE encoding instead of-Command - Per-cmdlet parameter validation
- Cmdlet-specific path parameter tracking (
tools/PowerShellTool/pathValidation.ts)
7.8 Process Spawning
File: utils/Shell.ts
- Uses
spawn()with separate args array (never shell string concatenation) - Commands passed to shell via
-cargument with proper quoting - Never uses
shell: truewith unsanitized user input O_NOFOLLOWflag prevents symlink attacks (line 303)- Sandbox wrapping for additional isolation
8. Path Traversal & Filesystem Protection
8.1 Path Validation
File: utils/path.ts
| Check | Implementation |
|---|---|
| Type validation | typeof path !== 'string' throws TypeError (lines 37-45) |
| Null byte detection | path.includes('\0') throws Error (lines 48-50) |
| Tilde expansion | Safe ~/ handling via homedir() (lines 59-65) |
| Traversal detection | Regex /(?:^|[\\/])\.\.(?:[\\/]|$)/ matches ../ patterns |
| UNC path blocking | Skip filesystem ops for \\ and // paths — prevents NTLM credential leak attacks |
| Windows path conversion | POSIX-style /c/Users/... → C:\Users\... on Windows |
| Unicode normalization | All paths normalized to NFC form |
8.2 File Read Tool Protection
File: tools/FileReadTool/FileReadTool.ts
Blocks dangerous device paths (lines 98-128):
/dev/zero,/dev/urandom,/dev/tty(prevents hangs)/proc/*/fd/0-2(stdin/stdout/stderr aliases)- Validates against readability permissions
8.3 File Write Tool Protection
File: tools/FileWriteTool/FileWriteTool.ts
- Path expansion in
backfillObservableInput()(lines 125-130) - Wildcard pattern matching for permissions (line 133)
- Write permission checks via
checkWritePermissionForTool()(line 137)
9. Network Security (WebFetch)
9.1 URL Validation
File: tools/WebFetchTool/utils.ts
| Control | Detail |
|---|---|
| Max URL length | 2,000 characters (line 106) |
| Max HTTP content | 10 MB (line 112) |
| Fetch timeout | 60 seconds (line 116) |
| Max redirects | 10 hops (line 125) |
| Credential blocking | URLs with embedded username:password rejected (lines 156-158) |
| Single-label hostname blocking | Requires at least 2 domain parts (line 164) — prevents internal network access |
| HTTP → HTTPS upgrade | Automatic at lines 376-377 |
| Markdown truncation | 100,000 characters max (line 128) |
9.2 Redirect Security
File: tools/WebFetchTool/utils.ts:212-243
- Only allows same-origin redirects (with optional
www.variations) - Validates protocol, port, and credentials match on redirects
- Cross-domain redirects require user approval
- 10-hop cap prevents redirect loop DoS
9.3 Domain Blocklist Preflight
File: tools/WebFetchTool/utils.ts:176-203
- Queries
api.anthropic.com/api/web/domain_infobefore fetching - 10-second timeout for the check
- Results cached with 5-minute TTL in LRU cache
- URL content cached with 15-minute TTL
Finding: The 5-minute cache TTL means a newly-compromised domain could remain accessible for up to 5 minutes after being blocklisted. See Finding #4.
9.4 Preapproved Domains
File: tools/WebFetchTool/preapproved.ts
130+ curated domains for documentation and package registries are preapproved for GET-only WebFetch access. These bypass the domain blocklist check.
Security separation (explicitly documented in comments, lines 5-12):
- Preapproved list is only for WebFetch (GET requests)
- Not inherited by the sandbox system for general network restrictions
- Some preapproved domains (e.g.,
huggingface.co,kaggle.com,nuget.org) allow file uploads and would be dangerous for unrestricted access
Path-prefixed entries validated with segment boundaries (line 162):
if (pathname === p || pathname.startsWith(p + '/')) return true
This prevents /anthropics from matching /anthropics-evil/malware.
9.5 Missing Protocol Check
The URL validation function (validateURL) does not explicitly check for file:// protocol. While new URL('file:///etc/passwd') would have a hostname of "" (empty string) and parts.length < 2 would catch "", this is an implicit rather than explicit check.
10. Code Injection Analysis
10.1 Dynamic Code Execution
| Pattern | Found | Status |
|---|---|---|
eval() | 0 | Safe |
new Function() | 0 | Safe |
vm.runInContext() | 0 | Safe |
vm.runInNewContext() | 0 | Safe |
vm.runInThisContext() | 0 | Safe |
No dynamic code execution patterns found in the codebase.
10.2 Module Loading
- 30 files with
require/dynamicimport— all legitimate module loading - MCP feature loading uses conditional require gated by feature flags:
const fetchMcpSkillsForClient = feature('MCP_SKILLS') ? (require('../../skills/mcpSkills.js') as ...) : null - No dynamic requires with user-controlled paths
10.3 JSON Parsing
- 53+ JSON operations found — all use standard
JSON.parse()/JSON.stringify() - Schema validation via Zod library (
z.object,z.string, etc.) - No
eval()of JSON data
11. Prompt Injection & Data Leakage
11.1 WebFetch Output Processing
File: tools/WebFetchTool/utils.ts, WebFetchTool.ts
- HTML converted to Markdown via Turndown library
- Markdown output passed to Claude for summarization via
applyPromptToMarkdown() - Output cached in LRU cache
Finding: No explicit HTML sanitization before Markdown conversion. A crafted web page could inject Markdown that formats in unexpected ways. See Finding #5.
11.2 Bash Tool Output
- Tool output (command results) are not used to make security decisions
- Security decisions based solely on command text, not output
- This is correct — output injection cannot bypass the sandbox
11.3 Analytics Data Sanitization
File: services/analytics/metadata.ts
- Sensitive marker type
AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHSforces explicit verification that logged data doesn’t contain code/file paths - MCP tool names redacted as
'mcp_tool'by default (lines 70-77) - Custom MCPs sanitized while official registry MCPs can be logged (lines 102-116)
11.4 OAuth Parameter Redaction
File: services/mcp/auth.ts:100-125
redactSensitiveUrlParams() strips OAuth parameters from logs:
state,nonce,code_challenge,code_verifier,code
11.5 Undercover Mode
File: utils/undercover.ts
Prevents Anthropic-internal information leakage when contributing to public repositories:
- Activation: Auto-enabled unless repo remote matches internal allowlist; force-on via
CLAUDE_CODE_UNDERCOVER=1; no force-off (safety default) - Scope: Only active for
USER_TYPE === 'ant'(internal builds); dead-code-eliminated in external builds - Blocks in commits/PRs: Internal model codenames (Capybara, Tengu, etc.), unreleased model versions, internal repo names, Slack channels, short links, “Claude Code” attribution, Co-Authored-By lines
11.6 Error Message Safety
File: services/api/errors.ts
- Error messages are generic and helpful (e.g.,
"Not logged in · Please run /login") - No API keys or token values included in error messages
12. Supply Chain Security
12.1 Dependencies
The published package.json has minimal explicit dependencies. The application is bundled into a single cli.js (13 MB) with an accompanying source map (59.8 MB).
12.2 No Suspicious Patterns
- No postinstall/preinstall scripts in package.json
- No dynamic requires from external sources in core security modules
- MCP plugin loading uses explicit path resolution, not arbitrary URL fetching
12.3 Hook System
File: utils/hooks.ts
- User-defined shell hooks executed via
spawn()(line 7) - Hooks sourced from user configuration files
- Input validation through
HookCallbacktype schema (lines 66-74) - Hook execution gated on permission system
12.4 Plugin Loading
File: utils/plugins/pluginLoader.ts
- Plugin path sanitization (removes non-alphanumeric characters, lines 145-150)
- Marketplace policy enforcement via
isSourceAllowedByPolicy(),isSourceInBlocklist() - Manifest validation with schemas
- Git repository cloning with controlled commands
13. Internal / “Ant-Only” Features
Several security features are gated on process.env.USER_TYPE === 'ant', which is a build-time define (constant-folded by the bundler):
| Feature | Behavior for External Users |
|---|---|
| Extended dangerous patterns (curl, git, gh, kubectl, aws, gcloud, gsutil) | Not blocked — only basic interpreter patterns protected |
| Auto mode (ML classifier) | Unavailable — gated on TRANSCRIPT_CLASSIFIER |
| Undercover mode | Dead-code-eliminated — all functions return trivially |
| Additional zsh/fish/eval/exec/env/xargs/sudo patterns | Available to all users (in DANGEROUS_BASH_PATTERNS before the ant-only block) |
The ant-only gating is documented in dangerousPatterns.ts:53-57:
“These stay ant-only — external users don’t have coo, and the rest are an empirical-risk call grounded in ant sandbox data, not a universal ‘this tool is unsafe’ judgment.”
14. Findings Summary Table
| # | Severity | Finding | Location | Mitigation |
|---|---|---|---|---|
| 1 | High | bypassPermissions mode disables ALL permission checks | PermissionMode.ts:66-71 | Remote killswitch via GrowthBook gate tengu_disable_bypass_permissions_mode; requires explicit --dangerously-skip-permissions flag; Anthropic can revoke remotely but cannot grant; killswitch is fail-open (if GrowthBook unreachable, bypass stays) |
| 2 | High | Dangerous shell patterns (curl, wget, git, gh, kubectl, aws, gcloud) only blocked for internal ant users | dangerousPatterns.ts:58-79 | Basic interpreter blocking (python, node, ruby, etc.) applies to all users; ant-only patterns based on internal usage data |
| 3 | Medium | Tree-sitter bash parser fallback to less robust regex in external builds | parser.ts:65-83 | Fail-closed design — unknown syntax becomes too-complex requiring user approval; extensive regex checks as secondary layer |
| 4 | Medium | Domain blocklist check cached for 5 minutes | WebFetchTool/utils.ts:66-78 | Domain approval still requires user consent for non-preapproved domains |
| 5 | Medium | No HTML sanitization before Markdown conversion in WebFetch | WebFetchTool/utils.ts:95 | Output is passed to Claude for summarization, not rendered directly; Markdown length capped at 100K chars |
| 6 | Medium | Excluded commands are user convenience, not a security boundary | shouldUseSandbox.ts:18-20 | Explicitly documented as not a security boundary; permission system is the real enforcement layer |
| 7 | Low | Plaintext credential fallback when OS keychain unavailable | plainTextStorage.ts:61-64 | 0o600 file permissions; explicit warning to user; only used when keychain fails |
| 8 | Low | No explicit file:// protocol check in WebFetch URL validation | WebFetchTool/utils.ts:139-168 | Implicitly blocked: file:// URLs have empty hostname, caught by parts.length < 2 check |
| 9 | Low | Preapproved domains (130+) bypass domain blocklist entirely | preapproved.ts:5-12 | Curated list of documentation/registry domains; GET-only; path segment boundary enforcement; sandbox does NOT inherit this list |
| 10 | Info | Permission rule ordering could allow early allow to shadow later deny | permissions.ts:126-173 | Rules processed deny → ask → allow; source ordering generally prevents this |
15. Recommendations
Priority 1 (High)
-
Extend dangerous pattern blocking to external users — The ant-only gate on
curl,wget,git,gh,kubectl,aws,gcloudleaves external users without auto-mode protection for these high-risk commands. Even if the current justification is based on internal usage data, these commands represent universal data exfiltration and code execution risks. -
Consider fail-closed behavior for bypassPermissions killswitch — The current
checkSecurityRestrictionGatereturnsfalsewhen GrowthBook is unreachable (growthbook.ts:887-888), meaning the killswitch is fail-open. If the intent is a safety mechanism, consider defaulting totrue(disable bypass) when the remote check fails.
Priority 2 (Medium)
-
Ensure tree-sitter is bundled in all builds — Or significantly harden the regex fallback path. Parser differential attacks are a known risk when the security parser and the execution parser disagree.
-
Reduce domain check cache TTL — Consider reducing from 5 minutes to 1 minute, or implement a push-based revocation mechanism for emergency domain blocks.
-
Add HTML sanitization in WebFetch pipeline — Sanitize HTML before Turndown Markdown conversion to prevent formatting injection from crafted web pages.
Priority 3 (Low)
-
Add explicit
file://protocol rejection — While implicitly blocked, an explicit check would be clearer and more robust against future refactoring. -
Consider requiring re-authentication for bypassPermissions — Adding an MFA or re-auth step before enabling
--dangerously-skip-permissionswould add defense-in-depth. -
Document excluded commands limitation — Ensure user-facing documentation clearly states that excluded commands are not a security boundary.
Appendix: Security Architecture Strengths
The following security patterns are well-implemented and noteworthy:
- Defense-in-depth for bash execution: 4-stage validation pipeline with fail-closed defaults
- Cryptographic placeholder salting: Prevents injection via command parsing intermediaries
- PKCE OAuth with parameter redaction: Strong auth flow with no credential logging
- Subprocess environment scrubbing: Comprehensive secret stripping in CI/CD contexts
- Case-insensitive path comparison: Prevents filesystem case-folding bypasses on macOS/Windows
- UNC path blocking: Prevents NTLM credential leak attacks on Windows
- Segment-boundary path matching: Preapproved domain paths validated at
/boundaries - Client-side secret scanning: 40+ rules prevent credential upload to team memory
- Undercover mode: Automated prevention of internal information leakage to public repos
- Remote killswitch capability: Anthropic can remotely disable dangerous modes without shipping client updates