Settings, Permissions & Security
Claude Code runs shell commands, edits files, and fetches web content on your behalf. That power demands precise control over what it can and cannot do. A misconfigured permission can let Claude overwrite production configs. A missing deny rule can expose API keys. An unsandboxed session can leak data to unintended domains.
This lesson gives you complete control. You will learn to configure settings at every scope, lock down tools with fine-grained permission rules, sandbox bash execution at the OS level, and protect credentials from accidental exposure.
Learning Objectives
- Configure Claude Code settings across all scopes: managed, local, project, and user
- Switch between permission modes and understand when each is appropriate
- Write Tool(specifier) rules that precisely control file access, bash commands, and web requests
- Enable and configure sandbox isolation for filesystem and network boundaries
- Protect credentials and sensitive files with deny rules and environment variables
Configuration Scopes
Claude Code uses a four-level settings hierarchy. Higher-precedence scopes override lower ones, giving organizations top-down control while allowing individual developers to personalize their workflow.
| Scope | Location | Who Controls It | Precedence |
|---|---|---|---|
| Managed | System directory (admin-deployed) | IT administrators | 1st (highest) |
| Local | .claude/settings.local.json | You, this repo only | 2nd |
| Project | .claude/settings.json | Team (committed to git) | 3rd |
| User | ~/.claude/settings.json | You, all projects | 4th (lowest) |
Command-line arguments (like --model or --add-dir) slot between managed and local, acting as temporary session overrides.
File Locations
User settings apply globally to every project you open:
~/.claude/settings.json # Your personal defaults
~/.claude/CLAUDE.md # Your personal memory file
Project settings are checked into version control so the whole team shares the same rules:
.claude/settings.json # Team settings (committed to git)
.claude/settings.local.json # Your personal overrides (gitignored)
.claude/CLAUDE.local.md # Your personal project memory (gitignored)
Managed settings are deployed by IT administrators and cannot be overridden by anyone:
# macOS
/Library/Application Support/ClaudeCode/managed-settings.json
# Linux / WSL
/etc/claude-code/managed-settings.json
# Windows
C:\Program Files\ClaudeCode\managed-settings.json
Settings File Format
Every settings file follows the same JSON structure. Here is a representative example:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)",
"Read(src/**)"
],
"deny": [
"Bash(curl *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
],
"defaultMode": "default"
},
"model": "claude-sonnet-4-5-20250929",
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1"
}
}Precedence in Action
If your user settings allow Bash(npm run *) but the project settings deny it, the project deny wins because deny rules are always evaluated first. The evaluation order is: deny -> ask -> allow. The first matching rule wins.
Precedence Example
Consider this scenario across two scopes:
// User settings (~/.claude/settings.json)
{
"permissions": {
"allow": ["Bash(npm run *)"]
}
}
// Project settings (.claude/settings.json)
{
"permissions": {
"deny": ["Bash(npm run *)"]
}
}Result: The project deny takes precedence. npm run commands are blocked in this project, even though your user settings allow them globally. Deny always wins over allow, and project scope overrides user scope.
Permission Modes
Claude Code supports several permission modes that control how aggressively tools are auto-approved. You can set the defaultMode in your settings file or cycle through modes interactively with Shift+Tab.
| Mode | Setting Value | Behavior | Best For |
|---|---|---|---|
| Normal | default | Prompts for permission on first use of each tool | Day-to-day development |
| Accept Edits | acceptEdits | Auto-accepts file edits; still prompts for bash commands | Trusted refactoring sessions |
| Plan Mode | plan | Read-only analysis; cannot modify files or run commands | Code review and architecture planning |
| Don't Ask | dontAsk | Auto-denies tools unless pre-approved via allow rules | Locked-down CI/CD pipelines |
| Bypass Permissions | bypassPermissions | Skips all permission prompts entirely | Isolated containers and VMs only |
Setting Your Default Mode
In your settings file:
{
"permissions": {
"defaultMode": "acceptEdits"
}
}Or cycle through modes interactively during a session with Shift+Tab. The current mode is displayed in the REPL prompt:
claude-sonnet-4-5 (Auto-accept edits) >
Bypass Permissions Is Dangerous
The bypassPermissions mode disables all permission checks. Only use it in isolated environments like containers or VMs where Claude Code cannot cause real damage. Administrators can prevent this mode entirely by setting disableBypassPermissionsMode to "disable" in managed settings.
Plan Mode for Safe Exploration
Plan mode is invaluable when you want Claude to analyze a codebase without risk of modification:
# Switch to plan mode
Shift+Tab (cycle to Plan mode)
# Now Claude can read, search, and analyze but cannot:
# - Edit or create files
# - Run bash commands
# - Make any modifications
Use plan mode when onboarding to a new codebase, reviewing pull requests, or having Claude design an architecture before you commit to changes.
Tool(specifier) Permission Rules
Permission rules follow the format Tool or Tool(specifier), giving you fine-grained control over exactly what Claude can access.
Rule Syntax Overview
Match all uses of a tool by using just the tool name:
| Rule | Effect |
|---|---|
Bash | Matches all bash commands |
Read | Matches all file reads |
Edit | Matches all file edits |
WebFetch | Matches all web fetch requests |
Add a specifier in parentheses for precise control:
| Rule | Effect |
|---|---|
Bash(npm run build) | Matches the exact command npm run build |
Bash(npm run *) | Matches any command starting with npm run |
Read(./.env) | Matches reading .env in the current directory |
Edit(/src/**/*.ts) | Matches editing TypeScript files under src/ |
WebFetch(domain:example.com) | Matches fetch requests to example.com |
Bash Wildcards
Bash rules support glob-style * wildcards at any position:
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git commit *)",
"Bash(* --version)",
"Bash(* --help)"
],
"deny": [
"Bash(git push *)",
"Bash(rm *)",
"Bash(curl *)",
"Bash(wget *)"
]
}
}Word boundary matters: Bash(ls *) (with a space before *) matches ls -la but not lsof. Without the space, Bash(ls*) matches both. The space enforces a word boundary.
Shell Operator Safety
Claude Code is aware of shell operators like &&, ||, and ;. A rule like Bash(safe-cmd *) will not grant permission to run safe-cmd && dangerous-cmd. The chained command is evaluated separately.
Read and Edit Patterns
Read and Edit rules follow the gitignore specification with four path types:
| Pattern | Meaning | Example |
|---|---|---|
//path | Absolute path from filesystem root | Read(//Users/alice/secrets/**) |
~/path | Path from home directory | Read(~/.ssh/*) |
/path | Relative to the settings file location | Edit(/src/**/*.ts) |
path or ./path | Relative to current working directory | Read(*.env) |
In gitignore patterns, * matches files within a single directory while ** matches recursively across directories.
{
"permissions": {
"allow": [
"Edit(/src/**)",
"Edit(/tests/**)",
"Read(~/.zshrc)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Edit(/node_modules/**)",
"Edit(//etc/**)"
]
}
}Absolute Path Syntax
A pattern like /Users/alice/file is NOT an absolute path. It is relative to your settings file. Use //Users/alice/file (double slash) for true absolute filesystem paths.
MCP and Subagent Rules
You can also control MCP server tools and subagents:
{
"permissions": {
"allow": [
"mcp__github__create_pull_request",
"Task(Explore)"
],
"deny": [
"mcp__puppeteer",
"Task(my-custom-agent)"
]
}
}mcp__puppeteermatches all tools from the puppeteer MCP serverTask(Explore)matches the built-in Explore subagent
Managing Rules Interactively
View and manage all active permission rules during a session:
/permissions
This shows every rule, its source settings file, and whether it is an allow, ask, or deny rule. You can add or remove rules directly from this interface.
Sandboxing
Permission rules control Claude's decisions. Sandboxing enforces boundaries at the OS level, so even a prompt injection attack cannot escape the defined boundaries.
How Sandboxing Works
The sandbox restricts bash commands and their child processes at two levels:
Filesystem isolation: By default, Claude can read and write within the current working directory. Access outside the project directory is blocked unless explicitly configured.
Network isolation: A proxy server running outside the sandbox controls all outbound network traffic. Only approved domains can be reached.
OS-Level Enforcement
| Platform | Technology | Notes |
|---|---|---|
| macOS | Seatbelt | Works out of the box |
| Linux/WSL2 | Bubblewrap | Requires bubblewrap and socat packages |
All child processes spawned by bash commands inherit the same sandbox restrictions. There is no way for a script running inside the sandbox to escalate its own permissions.
Enabling Sandboxing
Run the /sandbox command during a session to enable it:
/sandbox
Choose between two modes:
- Auto-allow mode: Sandboxed bash commands run automatically without permission prompts. Commands that need access outside the sandbox fall back to the regular permission flow.
- Regular permissions mode: All bash commands go through the standard approval flow, even when sandboxed. More control, more prompts.
Sandbox Configuration
Configure sandbox behavior in your settings file:
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"excludedCommands": ["docker", "git"],
"allowUnsandboxedCommands": false,
"network": {
"allowedDomains": ["github.com", "*.npmjs.org", "registry.yarnpkg.com"],
"allowLocalBinding": true,
"httpProxyPort": 8080,
"socksProxyPort": 8081
}
}
}Key options:
excludedCommands: Tools likedockerthat are incompatible with sandboxing run outside the sandbox insteadallowUnsandboxedCommands: Set tofalseto force all commands through the sandbox with no escape hatchallowedDomains: Whitelist of domains bash commands can reach; everything else is blocked
Defense in Depth
Permissions and sandboxing are complementary layers. Permission deny rules prevent Claude from attempting restricted operations. Sandbox restrictions prevent bash commands from reaching restricted resources even if a prompt injection bypasses Claude's decision-making. Use both together.
Credential Protection
Protecting sensitive data is non-negotiable. Claude Code provides multiple layers of defense against accidental credential exposure.
Deny Rules for Sensitive Files
The most direct protection: deny Claude access to files containing secrets.
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./.env.local)",
"Read(./secrets/**)",
"Read(./config/credentials.json)",
"Read(~/.ssh/**)",
"Read(~/.aws/credentials)"
]
}
}Commit this to .claude/settings.json so every team member inherits the same protections.
Environment Variables Over Hardcoded Secrets
Never hardcode API keys, tokens, or passwords in source files. Use environment variables instead:
# Set in your shell profile, not in code
export DATABASE_URL="postgres://user:pass@host/db"
export STRIPE_SECRET_KEY="sk_live_..."Claude Code itself uses ANTHROPIC_API_KEY from the environment rather than storing it in any config file.
Key Environment Variables
Claude Code recognizes many environment variables for configuration. The most important ones:
| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY | API key for Claude |
HTTP_PROXY / HTTPS_PROXY | Proxy server for network requests |
NO_PROXY | Domains to bypass the proxy |
ANTHROPIC_MODEL | Override the default model |
CLAUDE_CODE_MAX_OUTPUT_TOKENS | Max output tokens (default: 32,000) |
MAX_THINKING_TOKENS | Extended thinking budget |
CLAUDE_CODE_EFFORT_LEVEL | Effort level: low, medium, high |
CLAUDE_CODE_USE_BEDROCK | Route through AWS Bedrock |
CLAUDE_CODE_USE_VERTEX | Route through Google Vertex AI |
DISABLE_TELEMETRY | Opt out of telemetry |
You can also set environment variables in your settings file so they apply to every session:
{
"env": {
"ANTHROPIC_MODEL": "claude-sonnet-4-5-20250929",
"CLAUDE_CODE_EFFORT_LEVEL": "high",
"DISABLE_TELEMETRY": "1"
}
}Putting It All Together
Create Project Settings
Initialize a .claude/settings.json file in your repository:
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(npx jest *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git diff *)",
"Bash(git log *)",
"Bash(git status)",
"Edit(/src/**)",
"Edit(/tests/**)"
],
"deny": [
"Bash(git push *)",
"Bash(rm -rf *)",
"Bash(curl *)",
"Bash(wget *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Edit(/node_modules/**)",
"Edit(/.github/**)"
],
"defaultMode": "default"
}
}Add Personal Overrides
Create .claude/settings.local.json (gitignored) for your personal preferences:
{
"permissions": {
"defaultMode": "acceptEdits"
},
"model": "claude-sonnet-4-5-20250929",
"env": {
"CLAUDE_CODE_EFFORT_LEVEL": "high"
}
}Enable Sandboxing
Run /sandbox in your session and select auto-allow mode. Then verify the configuration:
/sandbox
For persistent sandbox configuration, add to your settings:
{
"sandbox": {
"enabled": true,
"autoAllowBashIfSandboxed": true,
"network": {
"allowedDomains": ["github.com", "*.npmjs.org"]
}
}
}Verify Your Setup
Use /permissions to review all active rules and their source files. Run a few test commands to confirm:
/permissions
# Test an allowed command
"Run npm run lint"
# Test a denied command — Claude should refuse
"Run curl https://example.com"
# Test a denied file read — Claude should refuse
"Read the .env file"
Configure a Secure Project
intermediate20 minSet up a real project with layered security. Follow these steps and verify each restriction works:
Step 1: Create .claude/settings.json in any project with:
- Allow:
npm run *,npx *,git diff *,git status, edits tosrc/**andtests/** - Deny:
rm -rf *,curl *,wget *, reads of.envand.env.*, edits tonode_modules/**
Step 2: Create .claude/settings.local.json with defaultMode set to acceptEdits.
Step 3: Start Claude Code and run /permissions to verify all rules are loaded from the correct files.
Step 4: Test the boundaries:
- Ask Claude to run
npm run lint(should auto-allow or prompt once) - Ask Claude to read your
.envfile (should be denied) - Ask Claude to run
curl https://example.com(should be denied) - Ask Claude to edit a file in
src/(should auto-accept in acceptEdits mode) - Ask Claude to edit a file in
node_modules/(should be denied)
Step 5: Enable sandboxing with /sandbox and test that network-dependent commands to non-allowed domains are blocked at the OS level, not just at the permission level.
Reflection: How do permission deny rules and sandbox restrictions differ? What happens if you allow Bash(curl *) in permissions but the sandbox blocks the domain? Which layer wins?
Key Takeaway
- Settings are scoped: Managed > Local > Project > User. Deny always beats allow. Commit project settings to git for team-wide consistency.
- Permission modes control approval flow: Use Normal for daily work, Accept Edits for trusted refactoring, Plan mode for safe exploration, and Bypass only in isolated containers.
- Tool(specifier) rules give precise control:
Bash(npm run *),Read(./.env),Edit(/src/**), andWebFetch(domain:...)let you whitelist and blacklist exactly what Claude can touch. - Sandboxing enforces boundaries at the OS level: Even prompt injection cannot escape Seatbelt (macOS) or bubblewrap (Linux) restrictions. Always enable sandboxing for defense in depth.
- Credentials need active protection: Deny rules for
.envfiles, environment variables instead of hardcoded secrets, and sandbox network restrictions to prevent data exfiltration.
Next Steps
You now have the tools to lock down Claude Code for any environment, from a solo side project to an enterprise CI/CD pipeline. In the next lesson, we will explore Git Workflows & Code Review — how to create well-crafted commits, manage branches, open pull requests with structured summaries, perform AI-powered code reviews, and resolve merge conflicts intelligently.
Resources: