Library
Claude Code Documentation (June 2026) · 12 of 15
Claude Code Documentation (June 2026)
ai CRITICAL

Permissions & Layered Safety

permissions sandboxing auto-mode managed-settings security

Key Principle

Claude Code's safety is defense in depth — independent layers, each closing a gap the layer above leaves open:

  1. Managed settings (org policy) — the outermost ring; overrides local developer config and is the only layer a developer cannot edit away (p. Chunk 001).
  2. Permission rules — allow / ask / deny which tool or command runs (p. Chunk 001, 014).
  3. Sandboxing — OS-level filesystem + network isolation gating what a tool's process can actually touch (p. Chunk 001, 014).
  4. Auto mode / the classifier — a second gate that runs after permissions, judging whether an allowed action risks data exfiltration (p. Chunk 013, 012).
  5. Hooks — deterministic scripts that guarantee an action happens (covered in implementation-playbook).

Why This Matters

Each layer exists because the one above has a hole. Without managed settings, every control is per-developer and can be relaxed locally (p. Chunk 001). Permissions vs. sandboxing — why you need both: denying WebFetch blocks Claude's fetch tool, but if Bash is allowed, curl/wget still reach any URL; sandboxing closes that gap with an OS-enforced domain allowlist (p. Chunk 001). And only permissions.deny is unconditional — it blocks before the classifier is consulted and cannot be overridden; a soft_deny prose rule you assumed was a hard wall can be cleared by user intent or an allow exception (p. Chunk 013).

Good Examples

  • Org policy with merge-safe arrays. Managed values override user/project settings, but array settings like permissions.allow / permissions.deny merge entries from all sources — developers can extend managed lists but cannot remove managed entries. Verify with /status → Status tab → Setting sources showing Enterprise managed settings and its source (remote)/(plist)/(HKLM)/(file) (p. Chunk 001).
  • Close the Bash-curl gap with the sandbox. Enable /sandbox (or sandbox.enabled) and scope the network with sandbox.network.allowedDomains (and deniedDomains) so Claude works freely inside defined boundaries while curl-via-Bash can only reach allowlisted hosts (p. Chunk 001, 014).
  • Teach the classifier your trusted infra. Out of the box the classifier trusts only the working dir and the current repo's remotes, so pushing to your org's source control is blocked until added. The only field most orgs need is autoMode.environment, written as natural-language prose. Include the literal "$defaults" to extend rather than replace:
{ "autoMode": { "environment": [
  "$defaults",
  "Source control: github.example.com/acme-corp and all repos under it",
  "Key internal services: Jenkins at ci.example.com, Artifactory at artifacts.example.com"
] } }

(p. Chunk 013, 012)

Counterpoints

  • The "$defaults" splice gotcha — omitting it replaces, not extends. Setting any auto-mode section without "$defaults" silently discards that section's built-in list: soft_deny without it loses force-push / curl | bash / production-deploy rules; hard_deny without it loses data-exfiltration and auto-mode-bypass rules. Sections are independent. To take ownership safely: run claude auto-mode defaults, copy the rules in, review each, then edit (p. Chunk 013).
  • hard_deny is not a hard wall for tool patterns. The classifier's 4-tier precedence is hard_deny > soft_deny > allow > explicit user intent — but it runs inside the classifier, after permissions. For a tool-pattern block that must never run regardless of intent, use permissions.deny, not hard_deny (p. Chunk 013).
  • "Clean up the repo" does not authorize force-push. Explicit user intent overrides remaining soft blocks only when the message directly and specifically describes the exact action — "force-push this branch" counts; general requests don't (p. Chunk 013).
  • The classifier ignores autoMode in shared project settings (.claude/settings.json) — so a malicious checked-in repo can't self-grant trust. Put autoMode in user, local, or managed settings (p. Chunk 012).

Key Commands & Config

/permissions          Manage allow/ask/deny; "Recently denied" tab — press r to retry on exit
/sandbox              Enable OS-level filesystem/network isolation
permissions.allow / .ask / .deny     (.deny is the only unconditional block)
permissions.disableBypassPermissionsMode , allowManagedPermissionRulesOnly
sandbox.enabled , sandbox.network.allowedDomains / .deniedDomains
autoMode.environment        Prose list of trusted infra ("$defaults" to extend)
autoMode.allow / .soft_deny / .hard_deny    Prose arrays that REPLACE built-in rule lists
claude auto-mode defaults    Print built-in environment/allow/soft_deny/hard_deny as JSON
claude auto-mode config      Print effective rules ("$defaults" expanded) — run after saving
claude auto-mode critique    AI review of custom rules (ambiguous/redundant/false-positive)
PermissionDenied hook        React to denials programmatically

Rules of Thumb

  • For an unconditional, non-negotiable block, use permissions.deny in managed settings — nothing else is a hard wall.
  • Permissions gate which tool; sandboxing gates what the process touches. Use both.
  • Always splice "$defaults" when editing any autoMode section, then run claude auto-mode config to confirm effective rules.
  • Repeated denials for the same destination usually mean missing context — add it to autoMode.environment.

Related References

Diagram

Diagram