What "claude code settings.json permissions" Means
claude code settings.json permissions is the configuration model that governs exactly what Claude Code — the AI coding CLI that can read, write, and execute code across your filesystem — is allowed to do without stopping to ask you first. It lives in ~/.claude/settings.json (with optional per-project overrides in .claude/settings.json) and is expressed as two lists: an allow list of actions Claude may run silently, and a deny list of actions it must never run. Together they replace ad-hoc "yes/no" prompting with a written, reviewable policy. When the two lists disagree, deny wins — always.
At CLaude coe , we treat this file as the single most important security boundary between a helpful autonomous coding agent and an agent that quietly does something you never intended. This guide walks through how allow and deny lists actually work, the highest-severity mistakes to avoid, and the concrete JSON and shell patterns that keep Claude Code productive without handing it the keys to your entire machine.
How Allow and Deny Lists Work
Claude Code decides whether to run an action by matching it against your permission rules. Each rule targets a tool and, for shell commands, a pattern — for example Bash(npm run test:*) matches any command that begins with npm run test. Rules fall into three buckets:
allow — the action runs immediately, no prompt. Use this for the safe, repetitive commands you trust Claude to run all day: running the test suite, listing files, reading source, formatting code.
deny — the action is blocked outright, and no prompt can override it. Use this for anything destructive or credential-adjacent.
ask (the default) — anything not explicitly allowed or denied triggers a confirmation prompt. This is the safe fallback and you should lean on it heavily rather than allow-listing broadly.
The rule that matters most: deny takes precedence over allow. If a command matches both an allow pattern and a deny pattern, it is denied. This lets you allow a broad, convenient category and then carve dangerous exceptions out of it. A minimal, sane starting point looks like this:
"allow": ["Bash(npm run test:*)", "Bash(git status)", "Bash(git diff:*)", "Read(./src/**)"]"deny": ["Bash(rm -rf:*)", "Bash(curl:*)", "Read(./.env)", "Read(~/.ssh/**)", "Read(~/.aws/**)"]
Notice that even though nothing in the allow list covers rm -rf or reading ~/.ssh, we deny them explicitly anyway. Defense in depth means never relying on the absence of an allow rule; state the prohibition so that a future, broader allow rule can't silently re-enable something dangerous.
The Highest-Severity Risk: --dangerously-skip-permissions
The entire permission model can be switched off with a single flag: --dangerously-skip-permissions. It does exactly what the name warns — it bypasses every confirmation prompt and ignores the spirit of your ask rules, letting Claude execute anything without interruption. It should never be used in production, and never in any directory that has access to credentials such as ~/.ssh, ~/.aws, cloud tokens, or a populated .env file.
The danger is not hypothetical. With permissions skipped, a single misread instruction, a poisoned document, or an over-eager plan can result in deleted files, exfiltrated secrets, or arbitrary commands run against your infrastructure — with no prompt standing in the way. Settings.json cannot fully protect you here, because the flag is designed to short-circuit settings.json. The only real control is to not use it outside of genuinely disposable environments.
There are narrow cases where skipping permissions is acceptable, and all of them share one property: the blast radius is contained to something you can throw away. Specifically, it is reasonable only inside:
Docker containers with no host mounts — Claude can wreck the container, but nothing leaks to your machine.
CI/CD sandboxes that are torn down after the run — ephemeral by construction, with scoped, short-lived credentials.
Throwaway Git worktrees — isolated working copies you can delete without touching your primary checkout.
Outside those, keep the flag off and let your allow/deny lists do their job.
Writing Deny Rules That Actually Protect You
Good deny lists are opinionated. They assume Claude will occasionally try something reckless and make sure the worst outcomes are simply impossible. Prioritize denying:
Credential reads:
Read(~/.ssh/**),Read(~/.aws/**),Read(./.env), and any secrets directory. An agent that can't read a secret can't leak it.Destructive filesystem commands:
Bash(rm -rf:*),Bash(git push --force:*), and anything that rewrites history or deletes broadly.Network exfiltration primitives:
Bash(curl:*)andBash(wget:*), which are the classic tools for shipping your data somewhere else. Allow-list specific, known-safe endpoints only if you truly need them.
Because deny beats allow, these rules survive even if a teammate later adds a sweeping Bash(*) allow entry in a moment of frustration. That resilience is the whole point. For a deeper, end-to-end walkthrough of these boundaries and the reasoning behind each one, our Claude Code security hardening guide covers every major attack surface in detail.
Beyond settings.json: The Surfaces Permissions Don't Cover
Allow and deny lists are the backbone of a hardened setup, but a few risks live outside them and deserve equal attention.
API key leakage through the environment
Setting ANTHROPIC_API_KEY as a shell export is convenient and quietly dangerous. Every subprocess Claude spawns — build scripts, test runners, arbitrary tooling — inherits that variable. One compromised or careless dependency in that process tree can read your key straight out of its environment. Prefer scoping the key inline for the single command that needs it, or better, authenticate with claude auth login (OAuth) so the long-lived secret never sits in a globally inherited variable at all.
MCP servers as a prompt-injection vector
Model Context Protocol (MCP) servers extend what Claude can see and do, but they also widen the attack surface. A malicious document Claude reads can embed instructions that hijack its behavior — and if a server exposes both read and write capabilities without an approval gate between them, injected text can drive real, destructive writes. Pair read and write only behind explicit approval, enable only the servers you actively need, restrict filesystem MCP paths to specific directories instead of the root, and pin servers to specific versions rather than @latest.
Hooks that run shell commands silently
Claude Code hooks execute shell commands automatically at lifecycle events, with your full user permissions, and they are remarkably easy to miss in a security review because they don't announce themselves at runtime. Audit your hooks the same way you audit your deny list: know exactly what runs, when it runs, and why. A hook is just as capable of deleting files or leaking secrets as any command you'd carefully gate — it simply does so without asking.
Applying the Principle of Least Privilege
Every recommendation here reduces to one idea: grant the minimum access needed and nothing more. Allow-list narrow command patterns, not entire tool categories. Deny credentials and destructive operations explicitly. Keep --dangerously-skip-permissions confined to disposable environments. Scope your API key instead of exporting it globally. Enable only the MCP servers you use, pin their versions, and confine their filesystem reach. Review your hooks as first-class security surfaces.
At CLaude coe , we've found that a well-written ~/.claude/settings.json turns Claude Code from a powerful-but-nervous tool into a genuinely trustworthy teammate — one that moves fast on the safe stuff and stops cold at the dangerous stuff. Start with a strict deny list, add allow rules only as you build confidence in specific commands, and let the default "ask" behavior catch everything in between.
A Practical Rollout Checklist
Create
~/.claude/settings.jsonwith an empty allow list and a strong deny list covering credentials, destructive commands, and network exfiltration.Run Claude normally for a few sessions and promote only the safe, repetitive prompts you keep approving into the allow list.
Switch authentication from an exported
ANTHROPIC_API_KEYtoclaude auth login, or scope the key inline where OAuth isn't an option.Inventory your MCP servers: disable unused ones, pin versions, restrict paths, and add approval gates wherever read and write coexist.
List every configured hook and confirm you understand exactly what shell command each one runs.
Reserve
--dangerously-skip-permissionsfor Docker without host mounts, ephemeral CI sandboxes, or throwaway worktrees — never your real working directory.
Harden the file once, review it whenever your workflow changes, and you get the speed of an autonomous coding agent without quietly signing away control of your machine.
Top comments (0)