Guide

Claude Code Permissions and Security

Configure permission modes, auto-approval rules, and security boundaries to keep autonomous file edits and commands safe.

~8 min read

One of the first questions any security-conscious engineer or business owner asks when looking at Claude Code is: "If this tool can edit files and run shell commands, how do I know it won't delete my database or push broken code to main?"

That concern is legitimate. An AI agent with terminal access is a powerful lever. Without permission boundaries, a misunderstood instruction could trigger an unintended git push or run a destructive cleanup script. Fortunately, Claude Code was designed with explicit permission tiers and allow/deny rules.

The default permission contract: reads are quiet, writes prompt

When you install Claude Code and open your first session, it starts in normal mode. In this mode, reading files inside your repository is silent. Claude opens files, reads documentation, and analyzes syntax without interrupting you.

Modifications, deletions, and shell command executions stop and prompt for explicit user confirmation. You inspect the proposed diff or shell command before approving.

You type: change the timeout setting in config/api.ts to 5000
Output: Claude wants to edit config/api.ts
Output: - Replace timeout: 1000 with timeout: 5000
Output: Allow? [y] Yes / [a] Yes, and do not ask again for this file / [n] No
Note: Selecting "a" grants permission for future edits to this specific file only.
Interactive approval prompt on first file edit.

The three operational permission modes

Claude Code supports three operational permission tiers. You can cycle between them mid-session by pressing Shift+Tab at the terminal prompt:

  • Normal mode: Every file edit, deletion, and shell command asks for interactive confirmation. Safest for exploring unfamiliar codebases or reviewing junior developer pull requests.
  • Auto mode: An autonomous classifier model reviews proposed actions before execution. Low-risk operations (formatting, running tests, local edits) proceed automatically, while high-risk commands (git push, rm -rf, network calls) still pause for your confirmation. This is the standard mode for subscribers after their initial session.
  • Bypass mode: All tool calls execute without interactive prompts. Intended strictly for sandboxed containers and headless CI automation runners where no human is present to approve.

How auto mode evaluates actions

You type: run test suite and update the failing test in tests/auth.test.ts
Output: Auto-mode approved: Bash(npm test)
Output: Auto-mode approved: FileEdit(tests/auth.test.ts)
You type: push changes to main
Output: Auto-mode paused: High-risk action detected.
Output: Claude wants to run: git push origin main
Output: Allow? [y/n]
Note: Safe local commands proceed silently; remote mutations stop and ask.
Auto mode reviewing routine edits versus destructive commands.

Auto mode does not give Claude a blank check. Instead, a lightweight secondary model evaluates each tool call before execution. If the action matches standard development workflows (such as editing a test file or running npm test), it auto-approves. If an action touches external endpoints, modifies git history, or removes directories, auto mode drops back to an interactive prompt.

Persistent permission configuration in settings.json

Answering approval prompts for routine commands like `npm test` or `git status` creates unnecessary friction. Instead of approving commands turn-by-turn, you can establish persistent rules in `.claude/settings.json`.

{
  "permissions": {
    "allow": [
      "Bash(npm test*)",
      "Bash(npm run lint)",
      "Bash(git status)",
      "FileEdit(src/**/*)"
    ],
    "deny": [
      "FileEdit(.env*)",
      "FileRead(.env*)",
      "Bash(git push*)",
      "Bash(rm -rf*)"
    ]
  }
}

Notice the hierarchy: deny rules always take precedence over allow rules. Even if an allow rule matches `FileEdit(**/*)`, a deny rule targeting `.env*` blocks any edit or read of your environment file instantly without prompting.

Scoping tool patterns and skills

Permission patterns use the syntax `ToolName(pattern)`:

  • Exact match: `Bash(npm test)` permits only that exact string. Any additional flags prompt for confirmation.
  • Prefix match: `Bash(npm test*)` permits `npm test`, `npm test -- --watch`, and any other command starting with that prefix.
  • Skill scoping: `Skill(commit)` or `Skill(deploy *)` allows you to control which skills run automatically vs which require an explicit user prompt.

Inspecting active permissions with /permissions

Whenever you start work on an important branch, run `/permissions` to verify which rules are currently in force.

You type: /permissions
Output: Current Mode: Auto mode
Output: Active Allow Rules (from .claude/settings.json):
Output: - Bash(npm test*)
Output: - FileEdit(src/**/*)
Output: Active Deny Rules:
Output: - FileEdit(.env*)
Output: - FileRead(.env*)
Note: Use /permissions whenever you pull changes from teammates to confirm safety boundaries.
Inspect currently active permission grants.
Stay updated

Get new guides in your inbox

One task, one guide, done fast. Practical Claude Code skills, zero noise.