← Back to index
Chapter 5

Security — Permissions, Hooks, and Three Defense Layers

27 permission files, the hook system's five capabilities, and why hook allow doesn't override settings deny.

The Permission System

The permission system spans 27 files and centers on three concepts: PermissionMode, PermissionRule, and the decision resolution logic.

PermissionMode encodes the overall permission stance for the session: whether the agent runs in auto-accept mode, requires approval for destructive operations, or runs in fully manual mode. This is the session-level switch.

PermissionRule encodes specific allow/deny rules for tool calls. Rules can match on tool name, on argument patterns, or on combinations. A rule like “allow Bash if the command matches this pattern” is a PermissionRule. Rules are evaluated in priority order; the first match wins.

bashClassifier deserves separate mention. The Bash tool is the highest-risk surface in the system — a single shell command can delete files, exfiltrate data, or modify system state in ways no other tool can. The bash classifier is a separate evaluation pass that analyzes the shell command before any hooks run, looking for patterns from dangerousPatterns: known-dangerous command patterns (recursive deletes, pipe-to-shell, credential exfiltration patterns, etc.).

The Hook System

Hooks intercept tool calls at two points: before execution (PreToolUse) and after (PostToolUse, PostToolUseFailure).

A PreToolUse hook can return five types of output:

  1. permissionBehavior — override the default permission decision (allow or deny)
  2. updatedInput — replace the tool’s input arguments with modified ones
  3. blockingError — halt execution with an error message surfaced to the user
  4. preventContinuation — stop the agent loop entirely, not just this tool call
  5. additionalContexts — inject extra context into the model’s next turn

These capabilities make hooks powerful: they can silently modify arguments, block specific operations, or inject context that changes how the model reasons about the next step. Hooks are how external systems (CI pipelines, team policy engines, IDE plugins) can influence Claude Code’s behavior without forking the tool.

resolveHookPermissionDecision(): The Critical Glue

This function is the most important piece of the security architecture to understand. It sits between hook output and execution, and it enforces one rule: a hook allowing an action cannot override a settings-level deny.

The decision resolution order:

  1. Check PermissionMode — if the session is in a restrictive mode, that takes precedence
  2. Check PermissionRule matches — explicit rules override defaults
  3. Check hook permissionBehavior output — hook decisions apply within the space allowed by rules
  4. Apply the Speculative Classifier result as additional signal

A hook can make a denied-by-default action allowed. But it cannot make a denied-by-settings action allowed. The settings are the floor; hooks operate above it.

This design prevents a class of attacks where a malicious tool or prompt injection attempts to grant itself permissions by crafting output that looks like a hook allow signal. No matter what the hook says, it cannot escalate past what the settings permit.

Three Defense Layers

The full security model stacks three layers:

Layer 1: Speculative Classifier

Runs async in parallel with the permission checks. Classifies the tool call’s risk profile based on the command, arguments, and current context. This is a fast heuristic pass — it’s not making a final decision, it’s producing a risk signal that feeds into later decisions. Its async nature means it adds no latency on the critical path.

Layer 2: Hook Policy Layer

PreToolUse hooks run synchronously before execution. They have access to the full tool call context and can apply complex policy logic: team-specific rules, environment-based restrictions, audit logging requirements, argument sanitization. This is the extensible layer — the place where organizations customize Claude Code’s behavior.

Layer 3: Permission Decision

resolveHookPermissionDecision() takes the combined output of the permission system, hook policy layer, and speculative classifier and makes the final binary decision: execute or block. This is not extensible — it’s the enforcement point.

The layers are ordered so that the cheapest and fastest check (speculative classifier) runs first and in parallel, the most flexible check (hooks) runs next, and the final enforcement (permission decision) is deterministic and not bypassable.

What “27 Files” Implies

The permission system’s 27-file footprint reflects real complexity. Different tools have different permission shapes — Bash needs command-level analysis, Write needs path-level analysis, WebFetch needs URL-level analysis. The dangerousPatterns file alone encodes years of accumulated knowledge about what shell commands are dangerous and why.

The system is not trying to be clever about permissions — it’s trying to be thorough. The goal is that a developer who reads the permission system’s source can understand exactly what will and won’t be allowed in any scenario, without needing to run experiments.


Reference: This chapter draws on Xiao Tan’s (@tvytlx) Claude Code Architecture Deep Dive V2.0 report.