Claude Code Permissions: Control What Your AI Assistant Can Do
When you run Claude Code in a project, it can execute bash commands, read and edit files, fetch data from the web, and communicate with subagents. Without explicit permission boundaries, the AI could accidentally push to production, delete critical code, expose secrets in .env files, or make irreversible changes. Permission rules are your safety valve—they let you grant just the access the AI needs while blocking dangerous operations by default.
Claude Code uses a hierarchical permission system where you define rules for each tool (Bash, Read/Edit, WebFetch, MCP, Agents) and scope them with exact paths, glob patterns, or command filters. Rules are evaluated in a strict order: deny rules first (block everything), then ask rules (prompt for permission), then allow rules (execute immediately). Understanding this system means you can grant helpful automation while maintaining security boundaries appropriate to your team and project.
Why Permissions Matter
Permission boundaries exist because Claude Code can take destructive actions faster than a human can stop it. A single bash command (rm -rf src/) or a careless git push --force can wipe out hours of work. Unlike a developer who pauses to think, Claude Code will execute a command if it believes the action serves your goal—which is exactly why you want explicit permission gates.
Real scenarios where permissions save projects: You want Claude to run npm test and commit passing changes, but you don't want it running npm publish to push a broken package to the registry. You want it to read your application code in src/, but deny it access to .env, .vercel/, or node_modules/. You want it to fetch documentation from trusted domains but block access to your internal wiki or private GitHub repos. Each of these requires a permission rule—otherwise, the AI will try the operation if it thinks it's necessary.
Quick reference
- Permissions are evaluated before the tool runs—Claude Code asks for approval (or blocks the operation) based on the rules you've defined.
- Deny rules take precedence over allow rules, so a single deny blocks an entire category even if an allow rule exists elsewhere.
- Rules apply to the AI's decision-making, not OS-level security—run Claude Code in a container or restricted shell if you need OS isolation too.
- Permission modes (manual, auto, plan, dontAsk) set the default behavior; specific rules override the mode for targeted control.
Remember this
Permissions prevent accidental or misguided destructive actions by making Claude Code ask for approval (or deny outright) before running dangerous commands or accessing sensitive paths.
Permission Modes and Default Behavior
Claude Code operates in one of several permission modes, each defining the default behavior when no specific rule matches. Think of modes as your baseline trust level—then override that baseline with specific allow and deny rules for fine-grained control.
Manual mode is the safest: Claude Code asks for approval before every tool invocation. This is slow but gives you full visibility and control; use it when onboarding Claude Code to a new project or when working with sensitive operations. Auto mode trusts Claude Code to act without prompting for non-destructive reads; it still prompts for writes and risky bash commands. Plan mode (the default for most workflows) prompts for risky operations like git push, rm, and writes to production paths, but executes safe reads like ls and cat without asking. dontAsk mode runs almost everything without prompting, except for operations explicitly denied by your rules. bypassPermissions (used in automation scripts) removes all prompts and applies only deny rules—useful in CI/CD pipelines where you cannot interact. acceptEdits is a convenience mode that auto-approves file edits without prompting.
Quick reference
- Plan (default) is a good balance: safe reads run silently, risky writes/deletes get approval.
- Switch to Manual when onboarding or dealing with financial, customer, or irreversible data.
- Use bypassPermissions in CI/CD, but always pair it with strict deny rules to prevent accidents.
- Modes can be overridden by specific allow/ask rules—a rule always takes precedence over the mode default.
- Tool-specific modes can differ; you might set Bash to plan but Read to auto for faster iteration.
Remember this
Permission modes set your default trust level; specific allow/deny rules override the mode for surgical control over what Claude Code can do.
Building Permission Rules: Deny, Ask, Allow
Permission rules come in three flavors, evaluated in a strict order: deny, ask, then allow. A deny rule will always block the operation, regardless of whether an allow rule exists. An ask rule prompts the user for approval. An allow rule executes the operation immediately.
Deny rules typically protect broad categories—all .env files, all git push operations, all npm publish commands. Ask rules narrow down to specific operations where you want a safety check—edit src/config.ts, run git push to origin/main only. Allow rules grant explicit permission—read any file in src/, run npm test, fetch from docs.example.com. The key insight: start with deny, then be selective about allow. This way, if the AI encounters an operation you haven't explicitly thought about, it will ask before proceeding, rather than silently running it.
Quick reference
- Deny rules are checked first—a deny always blocks, even if an allow rule would otherwise permit it.
- Use deny to protect whole categories:
*.env,.vercel/*,node_modules/*, all secrets. - Ask rules are useful for operations that are usually safe but warrant a confirmation:
git push, file deletes, domain lookups. - Allow rules should be as specific as possible:
src/**(not**),npm test(notnpm). - Exact path matches take precedence over glob patterns—
src/index.ts(exact) beatssrc/**/*.ts(glob).
Remember this
Deny rules block entire categories first, ask rules prompt for approval on risky operations, and allow rules grant explicit permission—evaluate in that order.
Wildcard Patterns and Precise Scoping
Permission rules use glob patterns (similar to .gitignore) to scope which files, commands, or domains are affected. A pattern like src/** means 'all files under src recursively,' while *.env means 'any file ending in .env in the project root.' Precision matters: overly broad patterns (like ** or /*) defeat the purpose of permissions, while overly narrow patterns might accidentally allow something dangerous.
Exact path matches always take precedence over glob patterns. If you define a rule for /src/config.ts (exact) and another for /src/** (glob), the exact rule wins. This is useful when you want to block one critical file while allowing reads to its parent directory—for example, deny /secrets.json (exact) while allowing /src/**. Symlinks behave differently depending on the rule type: a deny rule follows symlinks (if /config symlinks to /.private/config, the deny applies to both paths), but an allow rule respects the target (allows read to the symlink only if the target matches the allow rule). This distinction prevents attackers from bypassing deny rules via symlink tricks.
Quick reference
- Glob syntax:
*matches anything in one directory level,**matches recursively,?matches a single char. - Exact paths (e.g.,
/src/index.ts) always beat glob patterns (e.g.,/src/**). - Symlinks in deny rules follow to the target—
deny /.private/**blocks symlinks that point into/.private/. - Symlinks in allow rules do NOT follow—you must explicitly allow the symlink target path.
- Order matters for overlaps: most specific rule wins. A more specific glob like
/src/app/**beats a broad/src/**.
Remember this
Use exact paths for critical files, glob patterns for categories, and understand that symlinks behave differently in deny vs allow rules.
Tool-Specific Permission Rules
Each tool (Bash, Read/Edit, WebFetch, MCP, Agents) has its own rule syntax and risk profile. Bash rules filter by command name and arguments. Read/Edit rules filter by file path and glob patterns. WebFetch rules filter by domain and protocol. Understanding each tool type prevents accidentally opening security holes.
Bash rules can allow or deny entire commands or filter by partial match. A rule like allow npm test permits the literal command npm test. A rule like deny rm blocks any rm command, including rm -rf. Read-only bash commands (like ls, cat, grep, ps, curl) never prompt in plan or auto mode because they don't change state. Dangerous bash commands (like git push, rm, docker run, ssh) always prompt or require an allow rule. Read/Edit rules use file paths; allow src/** grants read to all files under src. Edit rules require explicit permission—you cannot implicitly allow edits even in auto mode; Claude Code always prompts unless you set an explicit allow rule. WebFetch/WebSearch rules whitelist or blacklist domains. You might allow docs.anthropic.com but deny internal-wiki.company.com. MCP (Model Context Protocol) and Agent rules inherit permissions from the parent—if the parent (Claude Code) is in plan mode, subagents also run in plan mode, but parent deny rules apply to subagents too.
Quick reference
- Bash rules: matching is case-sensitive;
git pushdoes not matchGit Push. - Read-only bash commands never require permission in plan/auto modes; only writes/dangerous ops prompt.
- Edit rules require explicit allow—there is no auto-allow for file writes to prevent accidental changes.
- WebFetch rules use domain + path; you can whitelist specific API endpoints but block the entire domain's web interface.
- MCP rules inherit from parent; if parent denies bash, subagents cannot use bash either.
Remember this
Each tool type has different risk profiles and rule syntax: bash filters by command name, read/edit by file path, web by domain—tailor rules to each tool.
Real-World Setup: A Team Project
Let's set up permissions for a realistic scenario: a team Node.js project where you want Claude Code to run tests and commit changes, but not publish to npm. You want it to read source files but not access deployment secrets or vendor code.
Your settings.json starts with a deny-first foundation: block all sensitive paths (.env, .vercel, deployment keys), block all npm publish and git force-push. Then ask for approval on code deletes and direct-to-main pushes—this prompts for confirmation so you catch mistakes. Finally, allow the safe operations: read all source code, run npm test, commit to feature branches, fetch documentation. When Claude Code tries to run npm test, the rule allow bash npm test is matched and the command runs silently. If Claude Code later tries npm publish, the deny rule deny bash npm publish blocks it immediately. If Claude Code tries to commit to main, the ask rule prompts you first—you see the diff and approve or reject. This layered approach keeps the project secure while letting the AI automate routine tasks.
Quick reference
- Deny broad categories first (all
.envfiles, all npm publish, all docker push). - Ask for operations that are usually safe but warrant a look (git push to feature branches, rm on src files).
- Allow only the specific commands and paths you want Claude Code to use regularly.
- Read-only operations (npm test, git status, ls) can be allowed without much risk.
- Edit operations need explicit allow—even if read is allowed, edit is not.
Remember this
Start with a deny-first foundation, add ask rules for usually-safe operations, and explicitly allow only the specific commands you trust the AI to run.
Permission Precedence and Rule Evaluation
Claude Code evaluates permission rules in a strict order: first deny, then ask, then allow. If a deny rule matches, the operation is blocked—period. If no deny matches but an ask rule does, Claude Code prompts you. If no deny or ask matches but an allow rule does, it runs silently. If none of the three match, the permission mode determines the default: plan mode prompts, auto mode allows, manual mode always prompts.
Compound bash commands (those using &&, ||, ;, or |) are parsed and each step is evaluated separately. A command like npm test && git commit is split into npm test and git commit, and permission rules are applied to each. If either step is denied, the whole pipeline is blocked. Wrappers like timeout, nice, xargs, docker exec, and env are stripped before rule matching—so a rule deny rm will block both rm file.txt and timeout 10 docker exec container rm file.txt. This prevents bypassing rules by wrapping commands.
Quick reference
- Deny rules are always checked first—no other rule can override a deny.
- Symlinks in deny rules follow to the target; deny rules block both the symlink and its target.
- Symlinks in allow rules respect the target; only the actual target path needs to match the allow rule.
- Compound commands are split and each step is checked—if one step is denied, the entire pipeline is denied.
- Wrappers (
timeout,docker exec,env,nice,xargs) are stripped before matching, preventing circumvention.
Remember this
Deny is evaluated first and cannot be overridden; compound commands are checked step-by-step; wrappers are stripped to prevent rule bypasses.
Zooming In: Decision Tree for Choosing Rule Type
When you're designing permission rules, a simple decision tree helps you categorize each operation. For any operation, first ask: Is it destructive or dangerous? If yes, deny it by default. Examples: git push --force (rewrites history), rm -rf (deletes data), npm publish (releases to the world), docker push (deploys images). These should all be denied unless there's an explicit exception.
If the operation isn't inherently dangerous, ask: Would it be useful to get user approval? If the operation is usually safe but warrants a check (like pushing to a remote repository where you'd want to see what's being pushed), add an ask rule so Claude Code prompts you. If the operation is routine and safe (like running tests, checking git status, reading documentation), allow it silently. This decision tree ensures you neither over-constrain (making the AI too slow) nor under-constrain (opening security holes).
Quick reference
- Dangerous operations:
git push --force,rm -rf,npm publish,docker push→ DENY - Usually-safe but important:
git push origin/feature,edit package.json→ ASK - Routine and safe:
npm test,git status,ls,cat→ ALLOW - Start with DENY for broad categories (all
.envfiles), then selectively ASK or ALLOW. - Test your rules by asking Claude Code to perform an operation you expect to be blocked.
Remember this
Choose rule type by asking: Is it dangerous? (deny) → Is approval useful? (ask) → Otherwise allow.
Advanced: PreToolUse Hooks for Custom Logic
For complex permission scenarios, Claude Code supports PreToolUse hooks—custom code that runs before a tool invocation and can modify permissions, inject environment variables, or log sensitive operations. A hook receives the tool request, inspects it, and returns a modified request or a denial. This lets you layer additional logic on top of static rules.
Common use cases for hooks: inject temporary credentials into the environment before running a deploy script, log all aws cli commands to an audit trail, validate that a database migration script contains only safe operations before allowing it, or require an additional approval step for risky tools by integrating with your team's approval system. Hooks are written in JavaScript/TypeScript and run in a sandboxed context—they cannot access the file system or network directly, but they can read the tool request and return metadata about it.
Quick reference
- Hooks run in a sandboxed JavaScript environment—no direct file/network access.
- Hooks can inspect the tool type (bash, read, edit, fetch) and request details.
- Hooks can modify the request (e.g., inject env vars, add flags) or deny it (throw an error).
- Hooks are async and can integrate with external services (Slack, approval systems, audit logs).
- Combine hooks with static rules for defense-in-depth: rules handle 95% of cases, hooks handle the 5% of complex scenarios.
Remember this
PreToolUse hooks let you add custom logic (approval workflows, credential injection, logging) beyond static permission rules.
Understanding the Boundaries of Permission Control
Permission rules control what Claude Code's AI can decide to do, but they don't replace operating-system-level security. A user running Claude Code in a terminal inherits all the permissions of their shell—if your OS user can delete /home/user/project, Claude Code can delete /home/user/project regardless of permission rules. Permission rules are a behavioral guardrail, not a security boundary.
For strong isolation in production environments, combine Claude Code permissions with OS-level controls: run Claude Code in a Docker container with a restricted file system (read-only / except for specific mounted volumes), use a dedicated unprivileged OS user, or deploy Claude Code in a Kubernetes pod with resource quotas and network policies. In CI/CD pipelines, use separate credentials for each stage (e.g., deploy role with no delete permissions) so even if the AI requests rm -rf /, the AWS/GCP/cloud provider identity limits the damage. Permission rules shine for local development and team collaboration where you want to prevent accidental mistakes and enforce safe practices—but for financial data, customer PII, or production infrastructure, layer OS-level controls too. See Claude Code Sandboxing for deeper isolation strategies.
Quick reference
- Permission rules control Claude Code's AI decision-making, not OS-level access control.
- A rule cannot prevent operations the OS user can already perform.
- For production: run Claude Code in containers with restricted file systems, use temporary credentials, deploy with resource limits.
- In CI/CD: use role-specific identities (deploy role, test role) with minimal permissions.
- Permission rules are most effective for preventing honest mistakes and enforcing best practices in dev/test environments.
Remember this
Permission rules prevent accidental mistakes and enforce safe behavior, but for production security, combine them with OS-level isolation and principle-of-least-privilege identities.
Key takeaway
Set up Claude Code permissions by starting with a deny-first foundation—block sensitive paths (.env, secrets, deployment keys), block dangerous operations (git force-push, npm publish, rm -rf). Then layer on ask rules for operations that are usually safe but warrant a look. Finally, explicitly allow only the specific commands and paths you trust the AI to use regularly. Test your rules by asking Claude Code to perform an operation that should be denied (e.g., read .env) and confirm it's blocked. For complex scenarios, use PreToolUse hooks to add custom approval workflows or credential injection.
Start here: Create a settings.json in your project with a deny list for .env, .vercel, and dangerous bash commands. Allow reads to src/** and edits to src/**/*.ts. Run Claude Code in your project and watch it respect the boundaries you've defined. As you become more comfortable, refine the rules—add ask rules for git operations, allow specific npm commands, whitelist trusted domains. The goal is to grant Claude Code enough freedom to automate routine tasks while maintaining the guardrails that keep your project safe from accidental or misguided changes.
Polo Khan
Lead Author & Systems ArchitectSoftware engineer and distributed systems architect specializing in backend scalability, cloud-native infrastructure, databases, and AI engineering workflows. Author and maintainer of Core Concept Learning.
Related Articles
Explore this topic