Permissions & Settings

This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page is written against the Claude Code CLI 2.1.283, VS Code 1.139 with the GitHub Copilot Chat extension, and the GitHub Copilot CLI 1.0.88, all re-verified as current as of 2026-09-28; re-check the linked docs before relying on an exact key name, since both products change these surfaces often. Every rule, key and mode named below was checked against the live documentation on that date; where a detail could not be confirmed, this page describes the behavior without naming a key.

Claude Code enforces permissions itself, independently of what a prompt or CLAUDE.md asks for — instructions shape what Claude tries to do, settings and modes decide what Claude Code actually lets it run. Keep that distinction in mind throughout this page: a well-written skill or instruction file is not a substitute for a deny rule, and Hooks covers the one mechanism (a blocking PreToolUse hook) that sits above even a matching allow rule.

The settings hierarchy and precedence

Claude Code reads JSON settings from several files and merges them, with a fixed precedence when the same key is set in more than one place. Verified against Claude Code — Settings files and precedence, highest first:

Level File / source Who it affects

1. Managed

managed-settings.json in a system directory (macOS: /Library/Application Support/ClaudeCode/; Linux and WSL: /etc/claude-code/; Windows: C:\Program Files\ClaudeCode\), an MDM policy, or server-managed settings from the claude.ai console

Every developer the organization deploys it to; nothing else overrides it, apart from a short list of security-sensitive exceptions

2. Command line

claude --settings <file>

The current session only

3. Project local

.claude/settings.local.json

The current user, in this project only — Claude Code keeps it out of git when it creates the file; add it to .gitignore yourself if you create it by hand

4. Shared project

.claude/settings.json

Everyone working in the project, once the file is committed

5. User

~/.claude/settings.json

The current user, across every project on their machine

List-valued keys such as permissions.allow merge across every file that sets them instead of the higher level replacing the lower one outright; some keys never take effect from a repository file — for example permissions.defaultMode set to auto or bypassPermissions applies only from user, --settings or managed settings, never from project or local files — and a few security-sensitive keys honor a stricter value even over managed settings. See the full precedence rules and their exceptions before relying on an edge case.

.claude/settings.json
{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Bash(mvn test)",
      "Bash(git status *)",
      "Bash(git diff *)",
      "Read(./docs/**)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "deny": [
      "Read(./secrets/**)",
      "Read(./.env)"
    ]
  },
  "env": {
    "DOCS_ASSISTANT_ENV": "local"
  }
}

Settings files are strict JSON — a // comment or a trailing comma is a syntax error that Claude Code reports as a Settings Error at the next start, so keep an example like this one comment-free even when annotating it in prose.

Allow, ask and deny rules

A permission rule is a bare tool name (matches every use of that tool) or Tool(specifier) (matches a narrower pattern). Verified against Claude Code — Configure permissions:

Rule Matches

Bash(mvn *)

Any mvn command; the trailing * also matches the bare mvn invocation. Put the wildcard after the subcommand — Bash(git * main) allows any git subcommand, not just git log main.

Read(./secrets/**)

As a deny/ask rule, a directory literally named secrets at any depth under the current directory (deny/ask rules match single-segment directory patterns at any depth); as an allow rule the same pattern matches only <cwd>/secrets.

mcp<server><tool>

One tool from one configured MCP server, e.g. mcpgithubget_issue; mcpgithub matches every tool on that server, and mcpgithubget_* (allow rules require the literal mcp<server>__ prefix before any wildcard) matches every get_ tool on it. See Configuring MCP Servers for configuring the servers these names come from.

WebFetch(domain:example.com)

Fetches to that exact hostname; WebFetch(domain:*.example.com) matches any subdomain but not the bare domain, and a bare WebFetch rule (no domain: part) behaves differently from WebFetch(domain:*) — only the domain: form also feeds the sandbox's allowed-domain list.

Rules are evaluated in a fixed order — deny, then ask, then allow — and the first match wins regardless of how specific a lower-priority rule is: a broad deny such as Bash(aws *) blocks a call even when a narrower allow such as Bash(aws s3 ls) also matches it. A bare tool-name deny (just Bash, with no specifier) removes the tool from Claude’s context entirely, so Claude never sees it as callable; a scoped rule such as Bash(rm *) leaves the tool available and blocks only matching calls.

Permission modes

Getting Started with Claude Code introduces the modes most relevant when starting out (Manual, Accept edits, Plan, Auto). The full set, verified against Claude Code — Choose a permission mode:

Mode (defaultMode value) What runs without asking

Manual (default)

Reads only; asks before file edits, shell commands and network access.

Accept edits (acceptEdits)

Reads, file edits and common filesystem commands for paths inside the working directory.

Plan (plan)

Reads and read-only exploration only; no edits until a plan is approved.

Auto (auto)

Almost everything, reviewed in the background by a separate classifier model instead of a prompt.

dontAsk

Auto-denies anything that would otherwise prompt, apart from what an allow rule or /permissions entry already covers — useful for an unattended run that must fail closed rather than hang on a prompt nobody can answer.

bypassPermissions

Skips permission prompts entirely, except for a short list of actions no mode auto-approves. This is a full bypass, not a convenience mode — deny rules still block, but every allow rule becomes moot and writes to protected paths such as .git and .claude are no longer gated. Only use it inside an isolated container, VM, or the sandbox runtime, never on a developer’s own machine. The CLI’s --dangerously-skip-permissions flag starts a session directly in this mode (its sibling --allow-dangerously-skip-permissions only adds the mode to the interactive cycle without activating it); the name is a deliberate warning label, not a formality.

Pick the mode a session starts in with permissions.defaultMode in a settings file, or for one run with --permission-mode:

.claude/settings.json
{
  "permissions": {
    "defaultMode": "plan"
  }
}
$ claude --permission-mode acceptEdits

Administrators can remove the two broadest modes altogether by setting permissions.disableBypassPermissionsMode or permissions.disableAutoMode to "disable" in managed settings. See Claude Code — Choose a permission mode.

Sandboxing

Permission rules and modes decide whether an action runs without asking; they are not an isolation boundary. Securing AI-Assisted Development covers sandboxing and network allowlists in depth; the essentials, verified against Claude Code — Sandboxing:

  • Once enabled (with /sandbox or sandbox.enabled in a settings file), Claude Code’s Bash sandbox enforces filesystem and network isolation at the OS level (Seatbelt on macOS, bubblewrap on Linux and WSL2) on every Bash, PowerShell or Monitor command and its child processes. The sandbox is not a permission mode; it restricts what a command can reach once it runs, whichever mode approved it.

  • No domain is pre-allowed by default. The first time a sandboxed command needs a new host, Claude Code prompts for it; hosts listed in sandbox.network.allowedDomains, and any domain already covered by a WebFetch(domain:…​) allow rule, skip that prompt.

  • The Bash sandbox constrains shell commands only — built-in tools such as Read, Edit and WebFetch are still gated by ordinary permission rules, and MCP servers and hooks run unconstrained on the host unless the whole process runs inside a container, VM or the separate sandbox runtime package.

  • An organization can lock the allowlist with sandbox.network.strictAllowlist (deny instead of prompt for any host outside it) or sandbox.network.allowManagedDomainsOnly in managed settings (only the managed entries are honored at all).

managed-settings.json
{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false,
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"]
    }
  }
}

failIfUnavailable stops Claude Code from starting when the sandbox can’t initialize, instead of falling back to unsandboxed execution, and allowUnsandboxedCommands: false removes the escape hatch that lets Claude retry a blocked command outside the sandbox. See Claude Code — Sandboxing.

How skill allowed-tools interacts with settings

A SKILL.md file’s allowed-tools frontmatter pre-approves the listed tools for the single turn that invokes the skill, so Claude can use them without an interactive prompt; invoking the skill again re-applies the grant for that turn. It does not restrict which tools are otherwise available — every tool still remains callable and ordinary settings still govern anything not listed (verified against Claude Code — Skills). The grant goes through the normal permission flow, where rules are evaluated deny, then ask, then allow, and an explicit ask rule is never auto-approved in any mode (Claude Code — Configure permissions). The interaction is therefore one-directional: deny and ask rules still override allowed-tools. A skill cannot grant its way past an organization’s deny rule, and an ask rule still interrupts the skill’s own turn even though allowed-tools named that tool:

.claude/skills/release-notes/SKILL.md (frontmatter)
---
name: release-notes
description: Draft release notes from the commits since the last tag, then push the notes branch. Use whenever the user asks for release notes.
allowed-tools: Bash(git log *) Bash(git tag *) Bash(git push *)
---
.claude/settings.json
{
  "permissions": {
    "ask": ["Bash(git push *)"]
  }
}

With both files in place, git log and git tag run without a prompt while the skill’s turn lasts, but git push still prompts, because the project’s ask rule wins over the skill’s grant. Workspace trust doesn’t gate allowed-tools either — a project skill’s grant applies even in a -p run in a folder nobody trusted — so review the allowed-tools of skills checked into a repository before running Claude Code there. This is why iru-code’s `allowed-tools (shown on the Skills page) lists narrow Bash(<prefix> *) entries rather than a bare Bash — a scoped grant composes safely with a project’s own deny rules, a bare one would just widen what needs denying elsewhere.

GitHub Copilot tool approvals

VS Code’s chat permissions picker offers three permission levels, and the separate Autopilot agent mode auto-approves tools too, verified against VS Code — Manage approvals and permissions:

Level or mode What it does and its risk

Manual permissions (default)

Uses the workspace’s tool, URL and terminal approval settings; anything not auto-approved still asks. The safest default.

Assisted permissions (experimental)

An LLM judge reviews each tool call; a call the judge does not approve still asks. Faster than Manual, but the judge is itself a model call and can misjudge a genuinely risky action — treat it as a convenience, not a security boundary.

Allow all

Runs every tool call without confirmation for the session. No per-tool or per-command scoping; a destructive edit or command runs exactly as readily as a safe one.

Autopilot (an agent mode, not a permission level)

Auto-approves every tool like Allow all, retries on errors, and answers blocking questions automatically instead of pausing for the person. The broadest option — equivalent in spirit to Claude Code’s bypassPermissions, and carries the same "isolated environment only" caveat.

Finer-grained, persistent auto-approval is set through chat.tools.* settings rather than the per-session picker, also verified against the same page:

Setting What it controls

chat.tools.global.autoApprove

Auto-approves tool calls across every workspace once enabled. VS Code shows a warning dialog the first time you enable it, because it removes confirmation prompts everywhere, not just in the current project —  the closest Copilot equivalent to a broad Claude Code bypass, and carries the same risk: only enable it on a machine or account where every workspace is already trusted.

chat.tools.eligibleForAutoApproval

Setting a tool to false here makes it always require manual approval; its confirmation dialog then offers no auto-approval option. The main lever for keeping a specific destructive tool behind a prompt.

chat.tools.terminal.autoApprove

Auto-approves (true) or requires approval for (false) specific terminal commands, by prefix or by a /regex/; common read-only commands are auto-approved by default. A false rule only requires approval, it does not block the command. chat.tools.terminal.enableAutoApprove set to false requires confirmation for every command. VS Code calls terminal auto-approval a best-effort convenience, not a security boundary.

chat.tools.urls.autoApprove

Auto-approves URL fetches by pattern — widen this deliberately, since an auto-approved fetch is exactly the surface prompt injection through repo content, issues and MCP tools targets.

Organizations set org- and enterprise-level Copilot policies — which features, models and agents are available to a team, and whether an individual can override settings like these for themselves — from organization or enterprise settings, documented at GitHub Copilot policies for enterprises and organizations. The nearest Claude Code equivalent is managed settings, covered below.

Safe defaults for CI

Headless & CI Automation covers running each tool unattended end to end; this section states the permission-side rule that page’s examples follow: grant a CI job only the narrowest allowlist the prompt actually needs, and never reach for a broad bypass just because nobody is present to answer a prompt.

In Claude Code
$ claude -p "Look at my staged changes and create an appropriate commit" \
    --allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)" \
    --permission-mode dontAsk
In GitHub Copilot: VS Code settings.json
{
  "chat.tools.terminal.autoApprove": {
    "git diff": true,
    "git status": true,
    "git log": true,
    "git push": false
  }
}

The Copilot cloud agent has no approval prompts to tune: it runs in an ephemeral sandbox with every tool pre-approved, so its safety levers are that sandbox’s firewall and preToolUse hooks, not an allowlist (see GitHub Copilot — Hooks reference).

Both examples name specific command prefixes rather than a bare Bash/terminal grant, and neither reaches for --dangerously-skip-permissions, bypassPermissions, chat.tools.global.autoApprove or Autopilot — a broad grant belongs only inside a disposable, network- and filesystem-restricted runner, never in a workflow that reviews content from an untrusted fork, per Safe permission settings for CI. See Claude Code — Run Claude Code programmatically for --allowedTools and dontAsk in -p runs.

For an organization running many such jobs, pin the allowlist itself in managed-settings.json rather than trusting every repository’s own .claude/settings.json, so a compromised or careless project file cannot widen what CI is allowed to run. allowManagedPermissionRulesOnly in managed settings additionally makes managed settings the only source of permission rules — Claude Code then also drops allow rules passed as --allowedTools, so the managed list itself must cover what the job runs:

managed-settings.json
{
  "allowManagedPermissionRulesOnly": true,
  "permissions": {
    "allow": ["Bash(git diff *)", "Bash(git log *)", "Bash(git status *)", "Bash(git commit *)"],
    "deny": ["Read(./.env)", "Bash(git push *)"],
    "disableBypassPermissionsMode": "disable"
  }
}

See Claude Code — Deploy managed settings for the delivery mechanisms (a file, MDM, or the claude.ai console) and precedence details.