{"@context":"https://schema.org","@type":"Article","headline":"How to Configure Claude Code Permissions Without Disabling Safety Checks","keywords":"claude code permissions configuration","description":"Comprehensive guide to claude code permissions configuration — covering definitions, best practices, tools, and FAQs.","author":{"@type":"Organization","name":"CLaude coe ","url":"https://gtm-rho.vercel.app/"},"publisher":{"@type":"Organization","name":"CLaude coe ","url":"https://gtm-rho.vercel.app/"},"datePublished":"2026-06-10T13:50:24.541Z","dateModified":"2026-06-10T13:50:24.541Z","mainEntityOfPage":{"@type":"WebPage"}}
{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Does the deny list in settings.json override tool-level allow rules?","acceptedAnswer":{"@type":"Answer","text":"See our full guide on claude code permissions configuration for a detailed answer to: Does the deny list in settings.json override tool-level allow rules?"}},{"@type":"Question","name":"Can I set different permission profiles for different projects?","acceptedAnswer":{"@type":"Answer","text":"See our full guide on claude code permissions configuration for a detailed answer to: Can I set different permission profiles for different projects?"}}]}
How to Configure Claude Code Permissions Without Disabling Safety Checks
Claude Code permissions configuration is the process of defining, at the settings.json level, which shell commands and file operations Claude Code may execute autonomously, which it must ask about, and which are blocked entirely — without resorting to the --dangerouslySkipPermissions flag that removes these boundaries wholesale. Done correctly, it lets teams move fast on routine tasks (running tests, linting, editing source files) while keeping a hard wall around irreversible or high-blast-radius operations.
Most engineering teams hit the same wall within a week of deploying Claude Code: the default permission prompts interrupt the flow on commands that are obviously safe, so someone opens the docs, finds --dangerouslySkipPermissions, and adds it to the startup script. Problem solved — until it isn't. This article explains why that shortcut is a mistake and how to build a permission profile that is both ergonomic and defensible.
Why Dangerous Mode Is Not the Answer for Production Workflows
--dangerouslySkipPermissions disables all permission checks. Claude Code will execute any shell command the model decides to run, with no user confirmation and no deny-list enforcement. The flag exists for sandboxed environments — automated CI pipelines running inside an isolated container with no network access and no persistent secrets. That is a narrow, specific use case.
Outside that sandbox, the risks compound quickly. Large language models make tool-selection errors. The Claude Code documentation acknowledges this directly: the model can misread context, call the wrong tool, or be steered by malicious content in a file it just read. A 2024 analysis of agentic LLM systems found that prompt injection via repository files — crafted commit messages, README content, test fixtures — caused unintended command execution in roughly 17% of observed task runs when no deny list was in place. With dangerous mode active, those unintended executions go through unchecked.
The second problem is audit trails. Skipping permissions means skipping the approval log. When something goes wrong — a branch deleted, a credential accidentally echoed, a test database wiped — there is no record of what was approved versus what ran silently. For teams subject to SOC 2 or internal security review, that gap is a finding.
Anatomy of the Claude Code Permissions Configuration in settings.json
Claude Code reads from two settings files: a user-level file at ~/.claude/settings.json that applies to every project, and a project-level file at .claude/settings.json in the repository root that applies only to that project. Project-level settings take precedence over user-level for matching rules, which is what makes per-project permission profiles possible.
The relevant block looks like this:
{
"permissions": {
"allow": [
"Bash(npm run test:*)",
"Bash(eslint *)",
"Edit(*)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force*)",
"Bash(curl * | bash*)"
]
}
}
Each entry is a tool specifier followed by an optional glob pattern in parentheses. The tool names map directly to Claude Code's internal tool identifiers: Bash for shell execution, Edit for file modification, Read, Write, and so on. The glob patterns use standard shell-style matching — * matches any string, including slashes.
When Claude Code evaluates whether to run a command, it checks the deny list first. A deny match blocks the action regardless of any allow rule. If no deny rule matches, it checks the allow list. A match there means the action runs without prompting. If neither list matches, the default behavior kicks in: Claude Code prompts the user for approval. Understanding this ordering is essential — see the FAQ below for a common misconception about rule precedence.
Building Allow Lists for Common Claude Code Permissions Configuration Scenarios
The goal is to enumerate the commands your workflow actually needs so the model can execute them without interruption, while leaving everything else in the prompt-required state. Start narrow and expand as you learn what gets blocked.
Test Runners
For a Node.js project using Jest or Vitest, a reasonable allow set is:
-
"Bash(npm run test*)"— coversnpm run test,npm run test:unit,npm run test:coverage-
"Bash(npx jest *)"— direct Jest invocations with any flags "Bash(npx vitest *)"
-
Do not write "Bash(npm *)". That allows npm publish, npm deprecate, and anything else npm can do. Be specific.
Linting and Formatting
-
"Bash(eslint *)""Bash(prettier --write *)""Bash(tsc --noEmit*)"
These are read-and-report operations or deterministic formatters. They have no meaningful blast radius. Allowing them removes a lot of prompt noise during refactoring sessions.
File Editing
By default, Edit and Write operations prompt for confirmation. For most development work, you want to allow edits within the project directory while still prompting for anything outside it:
-
"Edit(src/**)""Write(src/**)"-
"Read(**)"— reading files is low-risk; prompting on every read is more noise than signal
Avoid "Edit(*)" at the user level. That allows editing files anywhere on the filesystem, including ~/.ssh/authorized_keys or /etc/hosts.
Deny List Patterns That Block the Highest-Risk Shell Commands
A targeted deny list is your last line of defense against both model errors and prompt injection attempts. These patterns should go in your user-level ~/.claude/settings.json so they apply everywhere, regardless of what project-level settings allow.
Destructive File Operations
-
"Bash(rm -rf *)"— recursive forced deletion; almost never the right tool for an AI agent"Bash(shred *)""Bash(truncate *)"
Remote Execution Patterns
-
"Bash(curl * | bash*)"— the canonical supply-chain attack vector"Bash(wget * | bash*)""Bash(eval *)"
Credential and Secret Exposure
-
"Bash(cat ~/.ssh/*)"-
"Bash(env | *)"— piping environment variables anywhere "Bash(printenv*)"
-
Irreversible Git Operations
-
"Bash(git push --force*)""Bash(git reset --hard*)""Bash(git clean -f*)"
You cannot anticipate every dangerous command, but these categories cover the majority of serious incidents in practice. The deny list is not a replacement for running Claude Code in a sandboxed environment for high-stakes automation — it is a backstop for interactive development sessions.
Putting It Together: A Baseline Configuration
At Enkrypt AI, we recommend shipping a project-level .claude/settings.json alongside your codebase so every developer starts with a consistent, reviewed permission profile rather than relying on individual user-level configurations that diverge over time. Treat this file like you treat your linter config: commit it, review changes to it in pull requests, and document the reasoning behind non-obvious allow entries.
A starting template for a typical Node.js or TypeScript project:
{
"permissions": {
"allow": [
"Read(**)",
"Edit(src/**)",
"Edit(tests/**)",
"Write(src/**)",
"Write(tests/**)",
"Bash(npm run lint*)",
"Bash(npm run test*)",
"Bash(npm run build*)",
"Bash(npx tsc --noEmit*)",
"Bash(git status)",
"Bash(git diff*)",
"Bash(git log*)"
],
"deny": [
"Bash(rm -rf *)",
"Bash(git push --force*)",
"Bash(git reset --hard*)",
"Bash(git clean -f*)",
"Bash(curl * | bash*)",
"Bash(wget * | bash*)",
"Bash(eval *)",
"Bash(cat ~/.ssh/*)",
"Bash(printenv*)"
]
}
}
This configuration lets Claude Code operate autonomously on the tasks that make up 90% of a development session — reading code, editing source files, running tests, checking types — while requiring explicit approval for anything that touches git remotes, deletes files, or reaches out to the network. For a deeper look at how this fits into a secure deployment architecture, see the Claude Code product overview.
Frequently Asked Questions: Claude Code Permissions Configuration
Does the deny list in settings.json override tool-level allow rules?
Yes. The deny list is evaluated first, before the allow list. If a command matches a deny rule, it is blocked regardless of whether an allow rule also matches it. You cannot use an allow rule to whitelist a command that appears in the deny list. This is intentional — it means you can write broad allow rules (like "Bash(npm *)") and then carve out specific dangerous subcommands via deny rules, though a narrower allow list is generally the better approach.
Can I set different permission profiles for different projects?
Yes. Place a .claude/settings.json file in the root of each repository. Project-level settings are merged with user-level settings, with the deny list from either source taking precedence. If a command is denied at the user level, it stays denied in the project — the project file cannot lift a user-level deny. If a command is allowed at the project level but not present in the user-level allow list, it is allowed for that project only. This lets you grant broader permissions in a sandboxed internal tool while keeping a strict baseline for projects that touch production systems.
What is the difference between user-level and project-level settings.json?
User-level settings live at ~/.claude/settings.json and apply to every Claude Code session on that machine, regardless of which directory you're working in. Project-level settings live at .claude/settings.json inside a repository and apply only when Claude Code is invoked from within that project. The intended pattern is: put your universal deny list and low-risk global allows at the user level, and put project-specific allow rules (your test runner, your build script) at the project level. Commit the project-level file to version control so the whole team shares the same baseline.
Can I use --dangerouslySkipPermissions in CI?
Only if the CI environment is genuinely isolated: no secrets mounted as environment variables or files, no network access to production systems, ephemeral container that is destroyed after the run. Most CI pipelines do not meet this bar — they have AWS credentials, npm tokens, deployment keys, or access to internal APIs. If your CI runner has any of those, do not use --dangerouslySkipPermissions. Build an explicit allow list for the commands your CI workflow needs instead. The short-term convenience is not worth the exposure if the model misreads a malicious string in a test fixture or a dependency's package.json.
How do I debug why a command is being blocked or prompted?
Run Claude Code with --verbose to see permission evaluation logs. The output will show which rule matched (or that no rule matched, triggering the default prompt). If a command is being blocked unexpectedly, check your user-level deny list first — project-level allow rules cannot override it. If a command is prompting when you expect it to be silently allowed, verify the glob pattern matches the exact command string being evaluated, including any arguments. Glob matching is applied to the full command string, not just the binary name.
Are there community-maintained permission profile templates?
The Claude Code blog publishes configuration guides for specific stack types as they become available. The official Anthropic documentation includes a reference for all available tool specifiers and glob syntax. For teams building a shared baseline, the most practical approach is to start with a minimal allow list, run Claude Code for a week, collect the commands that triggered prompts, and promote the safe ones into the allow list after review — rather than trying to anticipate everything upfront.
Top comments (0)