DEV Community

Sangmin Lee
Sangmin Lee

Posted on • Originally published at claudeguide.io

Claude Code Permissions: Trust Levels, Allow Lists, and Safe Defaults

Originally published at claudeguide.io/claude-code-permissions

Claude Code Permissions: Trust Levels, Allow Lists, and Safe Defaults

Claude Code's permission model has 3 levels: Always-allowed (read-only tools, 0 prompts), Prompt-on-first-use (default — Edit/Write/Bash ask once per session), and Skip-permissions (--dangerously-skip-permissions for trusted environments). Configuring this correctly cuts ~80% of routine confirmation prompts while preserving safety for destructive operations. Understanding how the permission model works lets you configure it to match your risk tolerance — from "ask me before every file write" to "run fully autonomously."

How permissions work

Every tool call Claude Code makes falls into one of three permission buckets:

1. Always allowed — low-risk read operations (Read, Grep, Glob) that never prompt.

2. Prompt on first use (default) — higher-risk operations (Edit, Write, Bash) where Claude asks for your approval the first time in each session.

3. Always denied — tools or patterns you've explicitly blocked in settings. Claude cannot call these regardless of context.

The permission model is configured in your settings.json. There are two levels:

  • User-level (~/.claude/settings.json): applies to all projects
  • Project-level (.claude/settings.json): applies only when running from that directory, overrides user-level for the tools it specifies

For a complete reference of every field in settings.json, see the Claude Code settings.json reference.


Permission modes

You set the permission mode via the --permission-mode flag or in settings.

Mode Behavior
default Prompt for higher-risk tool uses on first call in session
bypassPermissions Skip all confirmation prompts — never asks
--dangerously-skip-permissions (CLI flag) Identical to bypassPermissions for one session

When to use bypass: automated scripts, CI/CD pipelines where you've reviewed the task in advance. Never use it when exploring an unfamiliar codebase or running an unknown task.


Configuring allowed and disallowed tools

Allow specific tools only

Restrict Claude to a whitelist of tools. Any tool not in the list is implicitly denied:

{
  "allowedTools": ["Read", "Grep", "Glob"]
}
Enter fullscreen mode Exit fullscreen mode

This creates a read-only Claude Code — it can explore and search but cannot modify files or run commands. Useful for code review and analysis tasks.

Disallow specific tools

Block specific tools while keeping all others available:

{
  "disabledTools": ["Bash", "WebFetch", "WebSearch"]
}
Enter fullscreen mode Exit fullscreen mode

This keeps file editing but prevents shell execution and web requests. Good for offline-only workflows.

Common safe configurations

Read-only mode (safe for exploring unfamiliar codebases):

{
  "allowedTools": ["Read", "Grep", "Glob", "Agent"]
}
Enter fullscreen mode Exit fullscreen mode

No shell execution (file edits only):

{
  "disabledTools": ["Bash"]
}
Enter fullscreen mode Exit fullscreen mode

No web access (air-gapped or policy-restricted environments):

{
  "disabledTools": ["WebFetch", "WebSearch"]
}
Enter fullscreen mode Exit fullscreen mode

Per-project permissions

For a project with different risk tolerance than your default, use .claude/settings.json in the project root. This file is typically committed to version control so the entire team shares the same settings. You can layer Claude Code hooks on top of permissions for even finer control — hooks can block specific commands or run formatters after writes.

Example: a production infrastructure project where shell execution requires explicit approval:

{
  "permissions": {
    "allow": [],
    "deny": [
      "Bash(git push*)",
      "Bash(kubectl*)",
      "Bash(terraform apply*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This uses pattern-based deny rules: Bash(pattern) blocks bash commands matching the pattern. Claude cannot run git push, kubectl commands, or terraform apply without triggering an error message.


Pattern-based rules

Pattern rules match against the tool input, not just the tool name.

Syntax: ToolName(pattern) where pattern is a glob-style match against the command input.

{
  "permissions": {
    "deny": [
      "Bash(rm -rf*)",
      "Bash(* --force*)",
      "Edit(*/.env*)",
      "Write(*secrets*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This blocks:

  • Any rm -rf command
  • Any command with --force
  • Editing any .env file
  • Writing any file with "secrets" in the path

Note: the patterns are case-sensitive and match against the full command string for Bash, or the full file path for file-operation tools.


The full settings.json reference

{
  "permissions": {
    "allow": [
      "Bash(npm test)",
      "Bash(npm run build)"
    ],
    "deny": [
      "Bash(git push*)",
      "Bash(rm -rf*)"
    ]
  },
  "allowedTools": [],
  "disabledTools": [],
  "hooks": {
    "PreToolUse": [],
    "PostToolUse": []
  }
}
Enter fullscreen mode Exit fullscreen mode

permissions.allow: specific patterns that are always permitted without a prompt, even if they would normally require approval.

permissions.deny: patterns that are always blocked. Takes precedence over allow.

allowedTools: if non-empty, only these tools are available. Everything else is blocked.

disabledTools: tools in this list are blocked regardless of other settings.


Trust levels explained

When you approve a tool call during a session, Claude Code remembers the approval at different levels:

Response What it means
Yes, allow once Approved for this single call
Yes, allow for this session Approved for all identical calls this session
Yes, always allow Added to settings.json permanently
No, deny once Blocked for this call; will ask again
No, always deny Added to permissions.deny in settings permanently

"Always allow" and "always deny" responses write to your project-level .claude/settings.json if it exists, or create one. Review these accumulating entries periodically — a "yes always" given hastily can create a permanent permission you didn't intend.


Auto mode and autonomous execution

When you invoke /auto or start a session with a task that requires unattended execution, you can pre-authorize the tools Claude will need rather than accepting all prompts:

In settings.json before starting the session:

{
  "permissions": {
    "allow": [
      "Bash(npm*)",
      "Bash(git add*)",
      "Bash(git commit*)"
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

This pre-authorizes npm commands and safe git operations without enabling a full bypass.


What each tool can do (risk reference)

Understanding what each tool is capable of helps you set the right defaults.

Tool What it does Default behavior
Read Read file contents Always allowed
Glob Find files by pattern Always allowed
Grep Search file contents Always allowed
Edit Replace text in existing files Prompt on first use
Write Create or overwrite files Prompt on first use
Bash Execute shell commands Prompt on first use
WebFetch Fetch a URL Prompt on first use
WebSearch Search the web Prompt on first use
Agent Spawn a subagent Prompt on first use

Bash is the highest-risk tool — it has the same access to your system as you do. A Bash command can delete files, push code, modify databases, and make network requests. Pattern-based deny rules on Bash are the most important safety measure.


Common permission mistakes

Setting bypassPermissions permanently in ~/.claude/settings.json: this turns off all safety prompts for every project. Only set this for specific automated workflows, not as a global default.

Forgetting to commit .claude/settings.json: your project team shares your safety rules only if you commit the file. Add it to version control with the rest of your project config.

Too-broad allow rules: Bash(*) in allow means every bash command is approved without prompting — equivalent to full bypass. Be specific: Bash(npm test) not Bash(*).

Not reviewing accumulated always allow entries: over time, quickly-granted "always allow" approvals accumulate in settings.json. Review and trim them quarterly. A well-crafted CLAUDE.md reduces how often you need to grant "always allow" in the first place by giving Claude clear instructions upfront.


FAQ

Does Claude Code need root/sudo access?
No. Claude Code runs as the current user. If it attempts a command requiring sudo, it will be blocked by the OS, not by the permission system. Never run Claude Code as root.

Can Claude Code access files outside my working directory?
Yes — Claude Code's Read and Bash tools can access any path the current user can access. Use .gitignore-style patterns in hooks to block reads of sensitive paths if needed.

Can I share settings between team members?
Yes — commit .claude/settings.json to your repository. All team members who use Claude Code in that project will use the same settings. User-level ~/.claude/settings.json is personal.

What happens when Claude tries a denied action?
The tool call is blocked and Claude receives an error message explaining what was denied. Claude will typically acknowledge the restriction and ask you how to proceed differently.

Is there an audit log of what Claude did?
Not built-in, but the Hooks system (see Claude Code Hooks guide) lets you log every tool call to a file with a PreToolUse hook on "*".

Sources

  1. Claude Code permissions documentation — April 2026
  2. Claude Code security guide — April 2026

Frequently Asked Questions

How do I make Claude Code read-only so it can't modify files?

Set allowedTools to ["Read", "Grep", "Glob"] in your .claude/settings.json. This restricts Claude to search and read operations only — it cannot edit, write, or run shell commands. Useful for code review tasks or exploring an unfamiliar codebase safely.

What is the difference between allowedTools and permissions.allow?

allowedTools is a whitelist of tool names — only listed tools are available at all. permissions.allow contains specific patterns (like Bash(npm test)) that are pre-approved without a prompt, while all other uses of those tools still require confirmation. Use allowedTools for hard restrictions; use permissions.allow for pre-authorizing specific safe commands.

How do I block Claude Code from running git push or kubectl?

Add pattern-based deny rules under permissions.deny in .claude/settings.json: "Bash(git push*)" and "Bash(kubectl*)". These patterns block any Bash command matching the glob, and Claude receives an error message explaining the restriction. Commit this file to version control so the rules apply to your whole team.

Can I permanently allow a tool action without re-approving every session?

Yes. When Claude Code prompts for approval, choose "Yes, always allow" — this writes the pattern to your settings.json under permissions.allow. On subsequent sessions the action runs without prompting. Review accumulated entries periodically; hasty "always allow" grants can create broader permissions than intended.


Take It Further

Claude Code Power Prompts 300 — 300 battle-tested prompts for Claude Code, organized by use case. Copy, paste, ship.

40 slash command templates. Token-optimized variants. JSONL file for direct import. Tested in production sessions.

→ Get Claude Code Power Prompts 300 — $29

30-day money-back guarantee. Instant download.

Top comments (0)