Claude Code Permission Boundaries: A Complete Guide to allowed-tools, Deny Rules, and Slash Commands
SkillShield Research Team
Security Research
Securing Claude Code: Beyond the Defaults
Claude Code is one of the most capable AI coding agents available. It can read files, execute commands, query databases, and interact with APIs. But with that power comes risk — especially when using third-party skills or working in sensitive environments.
This guide explains Claude Code's permission system in depth: how allowed-tools works, how to use deny rules to restrict access, and how slash commands create security boundaries. By the end, you'll know how to lock down Claude Code for production use.
Understanding Claude Code's Permission Model
Three Layers of Control
Claude Code uses three mechanisms to control what the AI can do:
allowed-tools— What tools the AI can use- Deny rules — What the AI is explicitly forbidden from doing
- Slash commands — Scoped permissions for specific workflows
These layers work together to create defense in depth.
Layer 1: allowed-tools
What Is allowed-tools?
The allowed-tools configuration defines which tools Claude Code can invoke. It's the primary permission boundary.
Default Tools
Out of the box, Claude Code has access to:
| Tool | Purpose | Risk Level |
|---|---|---|
read_file |
Read file contents | 🟢 Low |
write_file |
Write/modify files | 🟡 Medium |
bash |
Execute shell commands | 🔴 Critical |
search |
Search codebase | 🟢 Low |
list_dir |
List directory contents | 🟢 Low |
git |
Git operations | 🟡 Medium |
curl |
HTTP requests | 🔴 Critical |
db |
Database queries | 🟡 Medium |
Configuring allowed-tools
In AGENTS.md:
# Project Security
## Allowed Tools
- read_file
- write_file
- search
- list_dir
- git
## Forbidden Tools
- bash
- curl
- db
In .claude/config.json:
{
"security": {
"allowed_tools": [
"read_file",
"write_file",
"search",
"list_dir",
"git"
],
"forbidden_tools": [
"bash",
"curl",
"db"
]
}
}
Tool-Specific Configuration
You can also configure tools with restrictions:
{
"security": {
"tools": {
"bash": {
"allowed": true,
"restricted_commands": ["rm", "sudo", "curl", "wget"],
"require_confirmation": true
},
"write_file": {
"allowed": true,
"forbidden_paths": [".env", "secrets/", ".ssh/"],
"require_confirmation_for": ["*.key", "*.pem"]
}
}
}
}
The Risk Spectrum
Low Risk (generally safe):
read_file— Reading code and documentationsearch— Searching the codebaselist_dir— Directory exploration
Medium Risk (use with caution):
write_file— Can modify code (but usually reversible with git)git— Can commit, push, branch (but git history preserves changes)db— Database queries (risk depends on permissions)
Critical Risk (restrict or monitor):
bash— Arbitrary command executioncurl/wget— Network requests, data exfiltrationeval— Dynamic code execution
Layer 2: Deny Rules
What Are Deny Rules?
Deny rules are explicit prohibitions that override other permissions. They're your "red lines" — things Claude Code is never allowed to do, regardless of context.
Types of Deny Rules
1. File Access Deny Rules
{
"security": {
"deny": {
"file_access": [
".env",
".env.*",
"secrets/**",
".ssh/**",
"*.key",
"*.pem",
"/etc/passwd",
"/etc/shadow"
]
}
}
}
2. Command Deny Rules
{
"security": {
"deny": {
"commands": [
"rm -rf /",
"rm -rf ~",
"sudo *",
"curl *",
"wget *",
"eval *",
"exec *",
"* > /etc/*",
"* | bash",
"* | sh"
]
}
}
}
3. Network Deny Rules
{
"security": {
"deny": {
"network": {
"outbound": [
"*.malicious.com",
"192.168.1.0/24",
"10.0.0.0/8"
],
"except": [
"api.github.com",
"pypi.org",
"npmjs.org"
]
}
}
}
}
4. Pattern-Based Deny Rules
{
"security": {
"deny": {
"patterns": [
"password *= *",
"secret *= *",
"API_KEY *= *",
"BEGIN RSA PRIVATE KEY"
]
}
}
}
Deny Rule Precedence
Deny rules always win:
User: "Read the .env file"
Claude: "I can't read .env files — they're in my deny list."
User: "Run sudo apt-get update"
Claude: "I can't execute sudo commands — they're forbidden."
Even if a tool is in allowed-tools, deny rules prevent specific uses.
Creating Effective Deny Rules
Start with these defaults:
{
"security": {
"deny": {
"file_access": [
".env*",
"secrets/**",
".ssh/**",
"*.key",
"*.pem",
"id_rsa*",
".aws/**",
".kube/**"
],
"commands": [
"sudo *",
"rm -rf *",
"curl * | bash",
"wget * | bash",
"eval *",
"exec *"
],
"patterns": [
"*password*=*",
"*secret*=*",
"*token*=*",
"BEGIN * PRIVATE KEY"
]
}
}
}
Then add project-specific rules:
{
"security": {
"deny": {
"file_access": [
"production-database.yml",
"prod-credentials.json",
"/var/log/secure"
]
}
}
}
Layer 3: Slash Commands
What Are Slash Commands?
Slash commands create scoped environments with specific permissions. They're like "sandboxed modes" for particular tasks.
Built-in Slash Commands
Claude Code has several built-in commands:
| Command | Purpose | Permissions |
|---|---|---|
/help |
Show help | Read-only |
/clear |
Clear conversation | No file access |
/undo |
Undo last change | Git operations |
/review |
Review changes | Read-only |
/commit |
Commit changes | Git operations |
Creating Custom Slash Commands
You can define custom commands with restricted permissions:
{
"commands": {
"/secure-query": {
"description": "Query the database (read-only)",
"permissions": {
"tools": ["db"],
"db": {
"allowed_queries": ["SELECT"],
"forbidden_queries": ["INSERT", "UPDATE", "DELETE", "DROP"]
}
}
},
"/lint": {
"description": "Run linter on current file",
"permissions": {
"tools": ["bash"],
"bash": {
"allowed_commands": ["npm run lint", "eslint", "prettier"],
"forbidden_commands": ["*"]
}
}
},
"/docs-only": {
"description": "Read and analyze documentation",
"permissions": {
"tools": ["read_file", "search"],
"file_access": {
"allowed_patterns": ["*.md", "*.txt", "docs/**"],
"forbidden_patterns": ["*.js", "*.ts", "*.py", "src/**"]
}
}
}
}
}
Slash Command Use Cases
Use Case 1: Read-Only Mode
{
"commands": {
"/read-only": {
"description": "Read and analyze code without modifying",
"permissions": {
"tools": ["read_file", "search", "list_dir"],
"file_access": {
"read": "*",
"write": "none"
}
}
}
}
}
Usage: /read-only Analyze the authentication flow in this codebase
Use Case 2: Safe Database Queries
{
"commands": {
"/db-query": {
"description": "Execute read-only database queries",
"permissions": {
"tools": ["db"],
"db": {
"allowed_operations": ["SELECT", "EXPLAIN"],
"forbidden_operations": ["INSERT", "UPDATE", "DELETE", "ALTER", "DROP"],
"max_rows": 100
}
}
}
}
}
Usage: /db-query Show me the user table schema
Use Case 3: Deployment Workflow
{
"commands": {
"/deploy-staging": {
"description": "Deploy to staging environment",
"permissions": {
"tools": ["bash", "git"],
"bash": {
"allowed_commands": [
"npm run build",
"npm run test",
"git push origin staging"
],
"require_confirmation": true
},
"git": {
"allowed_operations": ["push", "status"],
"forbidden_operations": ["push origin main", "push origin production"]
}
}
}
}
}
Usage: /deploy-staging Deploy the current branch to staging
Putting It All Together
A Complete Security Configuration
{
"name": "Secure Project Configuration",
"description": "Production-ready Claude Code security settings",
"security": {
"allowed_tools": [
"read_file",
"write_file",
"search",
"list_dir",
"git"
],
"forbidden_tools": [
"bash",
"curl",
"db",
"eval"
],
"tools": {
"write_file": {
"forbidden_paths": [
".env*",
"secrets/**",
".ssh/**",
"*.key",
"*.pem"
],
"require_confirmation_for": ["*.json", "*.yml", "*.yaml"]
},
"git": {
"forbidden_operations": ["push origin main", "push origin production"],
"require_confirmation": true
}
},
"deny": {
"file_access": [
".env*",
"secrets/**",
".ssh/**",
"*.key",
"*.pem",
".aws/**",
".kube/**",
"/etc/passwd",
"/etc/shadow"
],
"commands": [
"sudo *",
"rm -rf *",
"curl *",
"wget *",
"eval *",
"exec *",
"* | bash",
"* | sh"
],
"patterns": [
"*password*=*",
"*secret*=*",
"*token*=*",
"*api_key*=*",
"BEGIN * PRIVATE KEY"
]
},
"require_confirmation": {
"file_write": true,
"git_push": true,
"tool_use": ["bash", "curl", "db"]
}
},
"commands": {
"/read-only": {
"description": "Read and analyze without modifying",
"permissions": {
"tools": ["read_file", "search", "list_dir"]
}
},
"/safe-db": {
"description": "Read-only database queries",
"permissions": {
"tools": ["db"],
"db": {
"allowed_operations": ["SELECT"],
"max_rows": 100
}
}
},
"/lint": {
"description": "Run linters and formatters",
"permissions": {
"tools": ["bash"],
"bash": {
"allowed_commands": ["npm run lint", "npm run format", "eslint", "prettier"]
}
}
}
}
}
SkillShield Integration
Scanning Claude Code Configurations
SkillShield can audit your Claude Code security configuration:
# Scan AGENTS.md for security issues
skillshield scan --type claude-config ./AGENTS.md
# Scan .claude/config.json
skillshield scan --type claude-config ./.claude/config.json
# Full project security audit
skillshield scan ./ --include claude-config
What SkillShield Checks
| Check | Description |
|---|---|
| Overprivileged tools | Flags dangerous tools in allowed-tools |
| Missing deny rules | Warns if critical deny rules absent |
| Weak restrictions | Identifies permissive bash/curl rules |
| Secret exposure | Detects potential credential access |
| Confirmation gaps | Flags actions without confirmation |
Example Output
╔════════════════════════════════════════════════════════╗
║ Claude Code Security Audit ║
╠════════════════════════════════════════════════════════╣
║ Config: ./.claude/config.json ║
║ Risk Score: 34/100 🟡 MEDIUM ║
╠════════════════════════════════════════════════════════╣
║ FINDINGS: ║
║ ║
║ ⚠️ bash tool allowed without restrictions ║
║ Risk: Arbitrary command execution ║
║ Fix: Add restricted_commands or deny list ║
║ ║
║ ⚠️ Missing deny rules for credential files ║
║ Risk: Secret exposure ║
║ Fix: Add .env*, .ssh/* to deny.file_access ║
║ ║
║ ✅ Git push requires confirmation ║
║ ✅ Write access restricted for sensitive files ║
╚════════════════════════════════════════════════════════╝
Best Practices
1. Start Restrictive, Loosen Gradually
Don't: Start with everything allowed and try to lock down.
Do: Start with minimal permissions and add as needed.
// Start here
{
"security": {
"allowed_tools": ["read_file", "search", "list_dir"]
}
}
// Add tools only when needed
{
"security": {
"allowed_tools": ["read_file", "search", "list_dir", "git"]
}
}
2. Use Deny Rules for Defense in Depth
Even if you trust a tool, add deny rules:
{
"security": {
"allowed_tools": ["bash"],
"deny": {
"commands": ["sudo *", "rm -rf *", "curl * | bash"]
}
}
}
3. Require Confirmation for Dangerous Actions
{
"security": {
"require_confirmation": {
"file_write": true,
"git_push": true,
"bash": true,
"curl": true
}
}
}
4. Create Task-Specific Slash Commands
Instead of giving broad permissions, create scoped commands:
{
"commands": {
"/migrate-db": {
"description": "Run database migrations",
"permissions": {
"tools": ["db", "bash"],
"bash": {
"allowed_commands": ["npm run migrate", "npx prisma migrate"]
},
"db": {
"allowed_operations": ["ALTER", "CREATE"]
}
}
}
}
}
5. Regular Security Audits
# Weekly audit
skillshield scan --report ./claude-security-report.json
# Before adding new tools
cat AGENTS.md | skillshield scan --stdin
# CI/CD integration
skillshield scan --fail-on medium ./.claude/
Common Patterns
Pattern 1: The Secure Defaults Template
{
"security": {
"allowed_tools": ["read_file", "write_file", "search", "list_dir", "git"],
"deny": {
"file_access": [".env*", "secrets/**", ".ssh/**"],
"commands": ["sudo *", "rm -rf *", "curl * | bash"]
},
"require_confirmation": {
"file_write": true,
"git_push": true
}
}
}
Pattern 2: The Database Developer
{
"security": {
"allowed_tools": ["read_file", "write_file", "search", "db"],
"tools": {
"db": {
"allowed_operations": ["SELECT", "EXPLAIN", "DESCRIBE"],
"forbidden_operations": ["DROP", "DELETE", "TRUNCATE"]
}
},
"deny": {
"file_access": [".env*", "database.yml", "prod-credentials.*"]
}
}
}
Pattern 3: The Frontend-Only Mode
{
"security": {
"allowed_tools": ["read_file", "write_file", "search", "bash"],
"tools": {
"bash": {
"allowed_commands": ["npm *", "yarn *", "npx *", "node *"],
"forbidden_commands": ["curl *", "wget *", "sudo *"]
}
},
"deny": {
"file_access": ["backend/**", "api/**", "server/**"]
}
}
}
Troubleshooting
"Claude won't use a tool I allowed"
Check: Is there a deny rule overriding it?
{
"security": {
"allowed_tools": ["bash"],
"deny": {
"commands": ["bash"] // This overrides allowed_tools!
}
}
}
Fix: Remove conflicting deny rule or make it specific.
"Deny rules aren't working"
Check: Syntax and path patterns:
// Wrong
{
"deny": {
"file_access": ".env" // Missing array brackets
}
}
// Correct
{
"deny": {
"file_access": [".env"]
}
}
"Slash command permissions are ignored"
Check: Command name format:
// Wrong
{
"commands": {
"read-only": { // Missing leading slash
// ...
}
}
}
// Correct
{
"commands": {
"/read-only": {
// ...
}
}
}
The Bottom Line
Claude Code's permission system is powerful but requires careful configuration. The three layers — allowed-tools, deny rules, and slash commands — work together to create defense in depth.
Key takeaways:
- Start restrictive and add permissions as needed
- Use deny rules for defense in depth
- Create slash commands for scoped workflows
- Require confirmation for dangerous actions
- Audit regularly with SkillShield
Remember: The goal isn't to restrict Claude Code's usefulness — it's to ensure it operates within boundaries that match your security requirements.
Resources
- Claude Code Security Docs: docs.claude.com/security
- Claude Code IAM: docs.claude.com/iam
- SkillShield: skillshield.dev — Audit your Claude Code configuration
- AGENTS.md Guide: themorningclaw.com/agents-md — Project-level configuration
Questions? [email protected]
Catch risky skills before they run.
SkillShield scans skills, MCP servers, and prompt-bearing tool surfaces before they reach production.
Get early access