Guides / Claude Code
Claude Code permissions: deny rules and a safe settings.json
Claude Code reads permissions from .claude/settings.json (shared, usually committed) and .claude/settings.local.json (personal). The permissions block decides which tools and shell commands run without asking you.
Data snapshot: 2026-10-20. Counts are unique public GitHub configs, identical files counted once. Aggregates only; no repos are named.
What the settings in .claude/settings.json do
- permissions.allow
- Rules that run without a prompt, e.g. Bash(npm run test:*), Read, WebFetch(domain:docs.example.com).
- permissions.deny
- Rules that are always blocked. Claude Code checks deny, then ask, then allow, and the first match wins, so Read(./.env) in deny blocks the read even if a broader allow matches. Deny rules apply in every mode, including bypassPermissions.
- permissions.ask
- Rules that always prompt, even if an allow rule matches.
- permissions.defaultMode
- default prompts on first use; acceptEdits auto-approves file edits and basic filesystem commands; plan explores without editing; auto uses a background classifier; dontAsk denies anything that would prompt; bypassPermissions skips prompts.
What public Claude Code configs look like
See the full dataset and leaderboard methodology. Public repos skew toward demos and templates, so treat these as a lower bound on what is possible, not a census.
Common risky patterns
- No deny rules
Without deny, nothing stops the agent reading .env files or pushing to git once a broad allow matches.
- Bash(python3:*), Bash(node:*), Bash(npx:*)
A wildcard interpreter can run any code, so it is effectively an unrestricted shell. Allow named scripts instead.
- Bash(curl:*) / Bash(wget:*)
Lets the agent send data to any URL. Use WebFetch(domain:...) for the domains you need.
- Broad Edit/Write without denying .claude/**
The agent can rewrite settings.json, including your deny rules. Only 31 of 694 configs that have deny rules protect .claude. Deny Edit(./.claude/**) and Write(./.claude/**).
- defaultMode: bypassPermissions
Skips permission prompts. Current Claude Code ignores bypassPermissions in project settings (set it in ~/.claude/settings.json if you really want it), but older versions honored it from the repo, and deny rules still apply in every mode.
A safer starting config
{
"permissions": {
"allow": [
"Bash(npm run test:*)",
"Bash(npm run lint:*)",
"Bash(git status)",
"Bash(git diff:*)"
],
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Edit(./.claude/**)",
"Write(./.claude/**)",
"Bash(git push:*)",
"Bash(rm -rf:*)"
]
}
}Adjust the allowed commands to your project. Rotate any key that was ever committed, even if you remove it later.
Claude Code permissions FAQ
Where do Claude Code permissions live?
In the permissions block of .claude/settings.json (shared with the project), .claude/settings.local.json (personal, usually gitignored) or ~/.claude/settings.json (user-wide). Managed settings set by an organization override all of them.
Do deny rules override allow rules in Claude Code?
Yes. Claude Code evaluates deny, then ask, then allow, and the first match wins. An allow rule can't carve an exception out of a deny rule, and deny rules still apply in bypassPermissions mode.
How many Claude Code configs have deny rules?
In the index, 14.4% of 4,831 unique public configs with a permissions block have at least one deny rule; 85.6% have none.
Is Bash(python3:*) safe to allow?
No. A wildcard interpreter can run any code, so it is effectively an unrestricted shell. Allow named scripts such as Bash(npm run test *) instead.
Related: How to add deny rules in Claude Code (settings.json examples)
Related: How to stop Claude Code from reading your .env file
Related: A safe Claude Code settings.json template (copy and paste)
Related: Is it safe to commit .mcp.json? (API keys in MCP configs)