Permissions
5 layersEvery tool call passes through a 5-layer gauntlet — from AST parsing to ML classifiers to interactive dialogs. Claude Code's security is thorough by design.
- Every Bash command passes through 5 layers: mode check → allow-list → AST analysis → yoloClassifier (ML) → interactive dialog. You can't skip layers.
- yoloClassifier is a real ML model in production code. It decides if a shell command is safe enough to auto-approve in auto-mode, based on command patterns.
- The permission system has 4 modes (default, plan, acceptEdits, bypassPermissions) plus org-level overrides via MDM. Each has a different risk profile.
The ML classifier is literally named yoloClassifier. Anthropic engineers named a production safety system after an internet meme. It auto-approves safe patterns like 'git status' or 'ls'.
Permission Decision Flow
When you run rm -rf, here is what happens at each layer before execution. Most calls are resolved before reaching Layer 5.
validateInput()Pre-checks: file exists, path safe, not stale. Returns error early if failed.
checkPermissions()Tool-specific rules. BashTool: AST shell parsing, dangerous pattern detection, redirect analysis → allow/deny/ask/passthrough.
ML Classifier (opt-in)AI safety scoring. Auto-approves safe patterns like 'git status' or 'ls'. Only available in 'auto' mode via tryClassifier().
Permission HooksCustom logic from settings.json. Shell commands evaluate tool safety → allow, deny, or escalate to user.
User UI DialogInteractive confirmation if no auto-decision reached. Shows tool name + input, asks allow/deny, can save as persistent rule.
Permission Modes
defaultAsk user for each potentially unsafe operation
planAll tools require explicit plan approval first
acceptEditsAuto-approve file edits without asking
bypassPermissionsAuto-approve everything (dangerous!)
dontAskAuto-deny everything silently
autoML 'yoloClassifier' auto-approval
Permission Rules
Rules are defined in settings.json with three possible behaviors: allow, deny, ask.
// Permission rule examples:
// Tool name targeting
{ "tool": "Bash", "behavior": "ask" }
// Tool + pattern targeting
{ "tool": "Bash", "pattern": "git *", "behavior": "allow" }
// → Only allow git commands automatically
// File path targeting
{ "tool": "Edit", "pattern": "~/.claude/*", "behavior": "allow" }
// → Auto-approve edits only in .claude folder
// Wildcard patterns
{ "tool": "Bash", "pattern": "*test*", "behavior": "allow" }
// → Allow any command containing "test"
// Bash permission rule matching order:
1. Exact match: "git"
2. Prefix match: "git *" (any git subcommand)
3. Wildcard: "*test*" (contains pattern)Permission Result Types
type PermissionResult =
| { behavior: 'allow'; message?: string } // Auto-approve
| { behavior: 'deny'; message: string } // Block with reason
| { behavior: 'ask'; message: string } // Show user dialog
| { behavior: 'passthrough' } // No special handling
// PermissionContext provides:
tryClassifier() // Await ML-based safety results
runHooks() // Execute permission hooks
persistPermissions() // Save updates to disk
buildAllow() / buildDeny() // Decision factories
handleUserAllow() // Log + state update
pushToQueue() // Queue management for UIFilesystem Permission Checks
File operations go through additional path-level security checks:
// filesystem.ts — checkWritePermissionForTool()
Blocked paths:
.gitconfig, .bashrc, .zshrc, .profile // Shell configs
.git/*, .vscode/*, .idea/* // IDE/VCS internals
/etc/*, /usr/* // System directories
Blocked patterns:
Files matching deny rules from toolPermissionContext
Files outside allowed working directories
// checkReadPermissionForTool()
Respects deny rules
Enforces file read ignore patterns (node_modules, .git)
// FileReadTool blocked devices (would hang process):
/dev/zero, /dev/random, /dev/urandom // Infinite output
/dev/stdin, /dev/tty // Blocking input
/proc/self/fd/* // stdio aliases