iTranslated by AI
After Claude Code Deleted My Files, I Started Taking Permissions Seriously
Introduction
When I asked Claude Code to "refactor," it rewrote my .env file.
When I asked it to "fix the tests," it modified my production code instead of the test code.
I was tired of repeating "Don't touch this file" every single day.
That's when I took a deep dive into permission settings, and I discovered that Claude Code has a much more granular control mechanism than I imagined. In this article, based on the official documentation, I will explain the overall picture of permission settings and how to use them effectively.
Overview of the Permission Model
Claude Code tools have three levels of permissions.
| Tool Type | Examples | Approval Required? |
|---|---|---|
| Read-only | File Read, Grep, Glob | No |
| Bash Commands | Shell execution | Yes |
| File Changes | Edit, Write | Yes |
In short, Claude can read files silently, but any modifications or execution require approval. This is the default safety mechanism.
Permission Modes
The strictness of approval depends on the Claude Code launch mode.
| Mode | Description | Best For |
|---|---|---|
| default | Standard. Confirms on first use of each tool | Normal development |
| acceptEdits | Skips confirmation for file edits | Trusted projects |
| plan | File analysis only. No changes allowed | Code reviews, research |
| dontAsk | Automatically denies non-preapproved tools | CI/CD, automation |
| bypassPermissions | Skips all confirmations | Container environments only |
Check Current Settings with /permissions
You can enter the following during a session:
/permissions
A list of current Allow / Deny rules will be displayed, and you can add or edit them right there.
How to Write Allow / Deny Rules
Basic Syntax
ToolName
ToolName(specifier)
Three Rule Levels
| Level | Description |
|---|---|
| Allow | Permits execution without confirmation |
| Ask | Requests confirmation every time |
| Deny | Completely rejects execution |
Evaluation order: Deny → Ask → Allow (Deny is always the top priority)
Practical Configuration Example
{
"permissions": {
"allow": [
"Bash(npm run lint)",
"Bash(npm run test *)",
"Bash(git commit *)"
],
"deny": [
"Bash(git push *)",
"Bash(curl *)",
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)"
]
}
}
Meaning of this configuration:
-
npm run lint,npm run test, andgit commitcan be executed without confirmation -
git pushandcurlare completely blocked - Even reading
.envfiles and thesecrets/folder is blocked
Wildcard Patterns
You can use * for glob matching.
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(* --version)",
"Bash(* --help *)"
]
}
}
File Path Patterns
There are four ways to write paths for Read and Edit rules.
| Pattern | Meaning | Example |
|---|---|---|
//path |
Absolute path on the filesystem | Read(//Users/alice/secrets/**) |
~/path |
Path from the home directory | Read(~/Documents/*.pdf) |
/path |
Path relative to the config file | Edit(/src/**/*.ts) |
path |
Path relative to the current directory | Read(*.env) |
-
*matches files in a single directory -
**matches directories recursively
Location and Priority of Configuration Files
| Scope | Location | Shared |
|---|---|---|
| User settings | ~/.claude/settings.json |
No |
| Project shared | .claude/settings.json |
Yes (Git-managed) |
| Local only | .claude/settings.local.json |
No |
| Admin settings | System level | IT deployment |
Priority (Top is highest):
- Admin settings (non-overrideable)
- Command-line arguments
- Local settings
- Project shared settings
- User settings
Permission Settings for MCP Tools
You can also apply permission rules to tools added via MCP.
{
"permissions": {
"allow": [
"mcp__github__search_repositories"
],
"deny": [
"mcp__puppeteer__*"
]
}
}
MCP tool names follow the mcp__server_name__tool_name pattern. mcp__puppeteer__* can block all tools in the Puppeteer server.
Common Configuration Patterns
Pattern 1: Secure Development Environment
{
"permissions": {
"allow": [
"Bash(npm run *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(git status)",
"Bash(git diff *)"
],
"deny": [
"Bash(git push *)",
"Bash(rm -rf *)",
"Bash(curl *)",
"Bash(wget *)",
"Read(./.env)",
"Read(./.env.*)",
"Edit(./.env)",
"Edit(./.env.*)"
]
}
}
Pattern 2: Read-Only Review
{
"permissions": {
"deny": [
"Edit(*)",
"Write(*)",
"Bash(*)"
]
}
}
Blocks all modifications and execution. Only allows file reading and Grep. An environment dedicated to code review.
Pattern 3: Restricting Web Access
{
"permissions": {
"allow": [
"WebFetch(domain:github.com)",
"WebFetch(domain:docs.anthropic.com)"
],
"deny": [
"WebFetch(*)",
"Bash(curl *)",
"Bash(wget *)"
]
}
}
Allows access only to specific domains. Blocks curl and wget to seal off potential workarounds.
Integration with Hooks
Combining this with Hooks makes it even more powerful.
-
Permission Settings: "Deny reading of
.env" (static rule) -
Hooks: "Return a warning message if someone attempts to edit
.env" (dynamic processing)
If permission settings are a "wall," then Hooks are the "guard." For complex conditions that cannot be covered by static rules, use the PreToolUse hook in Hooks.
Summary
| Point | Content |
|---|---|
| Check command | /permissions |
| Rule priority | Deny → Ask → Allow |
| Config files |
~/.claude/settings.json or .claude/settings.json
|
| Wildcard |
* (be careful with space position) |
| Absolute path |
// prefix required |
| MCP tool | mcp__server_name__tool_name |
Things to remember:
Instead of telling it "don't touch" repeatedly,
make it "unable to touch" via permission settings.
Discussion