Permissions & Sandboxing

Claude Code enforces a multi-layered security model that gates every tool invocation through permission checks, rule matching, sandbox enforcement, and destructive command detection. Nothing runs without passing through this gauntlet.

Three Permission Modes

Every Claude Code session operates in one of three permission modes, each with fundamentally different security postures:

Mode Behaviour Use Case Safety Guardrails
Default Interactive approval -- every write operation prompts the user Normal interactive sessions Full
Plan Read-only mode -- all write operations are blocked entirely Code review, exploration, analysis Full + Read-Only
Bypass Auto-approve all operations, but destructive command safety stays active Automated pipelines, CI/CD, trusted scripts Reduced
Warning

Bypass mode does not disable all safety. Destructive command detection (git force push to main, rm -rf /) still fires even in bypass mode. The macOS seatbelt sandbox also remains active regardless of mode.


Permission Check Flow

Every tool invocation follows a deterministic path through the permission system. The useCanUseTool() hook is the central gatekeeper:

Tool.call() useCanUseTool() hook Read-only? YES APPROVE NO Plan mode? YES BLOCK NO Bypass mode? YES APPROVE NO Approved rule? YES APPROVE NO Show PermissionRequest UI

Permission Rules

Permission rules are the mechanism for pre-authorising specific tool invocations so the user does not have to approve every action. Rules are stored as JSON in two locations:

Rule Locations
# User-global rules (apply to all projects) ~/.claude/settings.json # Project-scoped rules (apply to this repo only) .claude/settings.json

Rule Structure

settings.json
{ "permissions": { "allow": [ { "tool": "Bash", // Tool name "pattern": "npm test*", // Glob pattern for command "scope": "project" // "project" or "global" }, { "tool": "Edit", "pattern": "src/**/*.ts", // Glob pattern for file path "scope": "project" } ], "deny": [ { "tool": "Bash", "pattern": "rm -rf *", "scope": "global" } ] } }
How Rule Matching Works

When a tool is invoked, the system iterates through deny rules first (deny takes precedence), then allow rules. Pattern matching uses glob-style wildcards. If no rule matches, the system falls through to the interactive PermissionRequest UI.


Bash Tool: 7 Security Layers

The Bash tool is the most dangerous tool in the system -- it can execute arbitrary shell commands. As a result, it has the most elaborate security stack of any tool, with seven distinct validation layers that run sequentially:

Layer 1: Mode Validation

Permission Mode Gate

The first check is the broadest: what permission mode is the session in? In Plan mode, all Bash commands that are not read-only are rejected immediately. In Bypass mode, most commands skip the interactive prompt but still pass through the remaining layers.

Layer 2: Path Validation

Every command is analysed to ensure it targets the project directory. Commands that reference paths outside the project root are flagged and may require explicit approval. This prevents accidental (or intentional) modification of system files.

Path Validation Logic
// Simplified representation of path validation function validatePath(command, projectRoot) { const paths = extractPaths(command); for (const p of paths) { if (!isWithin(p, projectRoot)) { return { allowed: false, reason: "Path outside project" }; } } return { allowed: true }; }

Layer 3: Read-Only Validation

Commands classified as read-only are auto-approved without user interaction. The system maintains a curated allowlist of commands known to be safe:

Category Auto-Approved Commands
Git (read) git log, git show, git diff, git status, git branch, git remote, git config --get
File inspection cat, head, tail, less, more, file, strings, od, xxd, hexdump, wc
Search grep, rg, find, fd, locate, which, type
Docker (read) docker ps, docker logs, docker inspect, docker images
Data processing jq, xargs (read-only), sort, uniq, awk (read-only)
Analysis pyright, tsc --noEmit, ls, du, df

Layer 4: Command Semantics

Commands that are not read-only are classified into semantic categories that determine how they are presented to the user:

Category Description Examples UI Treatment
Safe Read-only, no side effects cat, grep, git log Auto-approve
File-Modifying Changes files in the project npm install, touch, mkdir Prompt with diff preview
System-Modifying Changes system state beyond files brew install, apt-get, pip install Prompt with warning
Network Makes network requests curl, wget, ssh Prompt with warning

Layer 5: Destructive Command Warning

Destructive Command Detection

Specific command patterns are flagged as destructive and receive an elevated warning in the UI, even in Bypass mode. These patterns are hardcoded and cannot be overridden by permission rules.

Hardcoded Destructive Patterns
// Commands that always trigger a destructive warning "git reset --hard" "git push --force" // or -f "git push --force-with-lease" "git clean -f" "rm -rf /" "rm -rf ~" "rm -rf *" "chmod -R 777" ":(){ :|:& };:" // Fork bomb detection

Layer 6: Git Safety

A dedicated layer for git-specific safety checks that goes beyond simple destructive command detection:

  • Force push to main/master is blocked -- the system detects git push --force targeting the main or master branch and refuses to execute, even in Bypass mode
  • --no-verify flag triggers a warning -- skipping git hooks is flagged as risky behaviour
  • --no-gpg-sign detection -- bypassing commit signing is noted
  • Branch deletion safety -- git branch -D on the current branch receives a warning
Source Insight

The git safety layer is why Claude's system prompt explicitly says "NEVER run force push to main/master, warn the user if they request it". The prompt instruction and the code-level enforcement are dual layers of the same safety net.

Layer 7: macOS Seatbelt Sandbox

OS-Level Process Isolation

On macOS, every Bash command runs inside a sandbox-exec seatbelt profile. This is a kernel-level enforcement mechanism that restricts file system access, network connectivity, and process spawning -- regardless of what the user or permission rules say.

High Impact

The seatbelt sandbox enforces these restrictions at the OS level:

Resource Policy Details
File System (read) Allowed Project directory, system libraries, temp directories
File System (write) Restricted Only project directory and /tmp
Network (outbound) Restricted Allowed for npm/git operations; general outbound varies by config
Process spawning Restricted Child processes inherit sandbox restrictions
System calls Filtered Dangerous syscalls (ptrace, etc.) are blocked

Sed Validation

The sed command receives special treatment because it can be either read-only (printing, filtering) or file-modifying (in-place editing). The system parses the sed expression to determine its intent:

Sed Expression Analysis
// Read-only sed patterns (auto-approved) sed -n '5,10p' file.txt // Print lines 5-10 sed 's/foo/bar/' // Pipe mode (no file arg = stdout) // File-modifying sed patterns (require approval) sed -i 's/foo/bar/' file.txt // In-place edit (the -i flag) sed -i.bak 's/foo/bar/' file // In-place with backup

The key signal is the -i flag. If present, the command is classified as file-modifying and goes through the standard approval flow. Without -i, sed only writes to stdout and is treated as read-only.


File Operation Permissions

FileEditTool (Edit)

The Edit tool has two critical preconditions that are enforced before the permission check even runs:

  • Must read first: The file must have been read with the Read tool in the current conversation. The tool will error if you attempt an edit without a prior read. This prevents blind edits.
  • old_string must be unique: The string to replace must appear exactly once in the file. If it matches zero times or more than once, the edit is rejected. This prevents ambiguous edits.

FileWriteTool (Write)

The Write tool has a similar read-first requirement for existing files:

  • Existing files: Must be read first. The tool will fail if you did not read the file before overwriting.
  • New files: No read required. Creating a file from scratch is allowed directly.
Why Read-First Exists

The read-first requirement serves two purposes: it ensures Claude has seen the current state of the file before modifying it (preventing stale edits after compaction), and it creates an auditable trail showing what was in the file before the change.


Permission UI Components

When a tool invocation requires interactive approval, the system renders one of several UI components depending on context:

Component Trigger User Actions
PermissionRequest Standard write operation Allow once, Allow always, Deny
DestructiveWarning Destructive command detected Allow once (no "always" option), Deny
BashPreview Bash command with file changes Allow, Deny, with command preview
EditDiff File edit operation Allow, Deny, with inline diff view

When the user selects "Allow always", a permission rule is created and persisted to the appropriate settings.json file. Future invocations matching that rule skip the prompt entirely.


Denial Tracking

The system tracks permission denials to provide context to Claude and prevent repeated requests for the same blocked action:

  • Denial count: Each denied tool+pattern combination increments a counter
  • Backoff behaviour: After repeated denials of the same action, Claude is instructed to stop retrying and ask the user for guidance
  • Session scoped: Denial counters reset when the session ends
  • Surfaced in context: Recent denials are included in the system prompt so Claude knows what has been rejected

Enterprise MDM Controls

For enterprise deployments, Claude Code supports Mobile Device Management (MDM) style controls that override user-level settings:

Enterprise Configuration
// Enterprise MDM can enforce: { "enforcePermissionMode": "default", // Prevent bypass mode "blockNetworkTools": true, // Block curl, wget, ssh "requireReadFirst": true, // Cannot be disabled "allowedDirectories": [ // Restrict project roots "/Users/*/work/*", "/opt/projects/*" ], "blockedCommands": [ // Additional deny patterns "docker run*", "kubectl*" ] }
MDM Priority

Enterprise MDM rules take the highest priority in the permission chain. They are evaluated before user rules, before mode checks, and cannot be overridden by any local configuration. This is the mechanism organisations use to enforce security policies across developer machines.