Tips & Tricks (Updated: 7/22/2026)

Claude Code Permissions Guide: A Safe settings.json for Beginners

Set up Claude Code permissions safely with a copy-ready settings.json, three use cases, and a tested Node.js check.

Claude Code Permissions Guide: A Safe settings.json for Beginners

You ask Claude Code to run a test, but an approval dialog appears for every command. Then you allow all of Bash to save time and realize that the same rule could cover a force push or a destructive delete.

You do not have to choose between constant prompts and unrestricted access. A practical starting point is to auto-approve reads and tests, ask before edits and external actions, and deny access to secrets and destructive commands. Those three layers live in settings.json.

Key takeaways

  • Rules are evaluated in this order: deny → ask → allow. A more specific allow rule does not override a matching deny or ask rule.
  • Project reads are already prompt-free by default. Allow only inspected test commands, ask before edits, external access, or pushes, and deny secrets and destructive actions.
  • Bash(git *) is too broad for most repositories. It also matches commands such as git reset --hard and git push --force.
  • Read(.env) does not stop every child process from opening that file. Use the sandbox as well when you need OS-level isolation.
  • After changing the file, use /permissions and /status to check which rules and settings sources are active.

What Claude Code can handle, and what a person should decide

Let Claude Code handleRequire human approvalAlways block
File search, diff review, testsFile edits, commits, pushes, dependency installationSecret reads, force pushes, hard resets, bulk deletion
Read, Grep, git diffEdit, git commit, npm install.env, git push --force, git reset --hard, rm -rf

Anything that is hard to reverse, sends data outside the machine, or touches credentials should stay in ask or deny.

Start with this settings.json

Create .claude/settings.json at the repository root and start with the following team-safe baseline.

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "defaultMode": "default",
    "allow": [
      "Bash(npm test *)",
      "Bash(npm run lint *)"
    ],
    "ask": [
      "Edit",
      "WebFetch",
      "Bash(git add *)",
      "Bash(git commit *)",
      "Bash(git push *)",
      "Bash(git clean *)",
      "Bash(git restore *)",
      "Bash(npm install *)",
      "Bash(npm uninstall *)"
    ],
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(**/secrets/**)",
      "Edit(.env)",
      "Edit(.env.*)",
      "Edit(**/secrets/**)",
      "Bash(git push --force *)",
      "Bash(git reset --hard *)",
      "Bash(rm -rf *)",
      "Bash(rm *)",
      "PowerShell(Remove-Item *)"
    ]
  }
}

This baseline assumes you have inspected the repository’s package.json scripts. In an unfamiliar repository, leave allow empty until you know what the test command executes. Bash(npm test *) matches both npm test and forms with trailing arguments.

Where settings.json belongs

ScopeLocationUse it for
User~/.claude/settings.jsonDefaults shared by all of your projects
Project.claude/settings.jsonTeam rules committed to Git
Local.claude/settings.local.jsonPersonal settings for this machine; do not commit it
ManagedSettings distributed by an administratorOrganization rules that users cannot override

The settings precedence is Managed, command line, Local, Project, then User. Array settings such as permissions.allow are concatenated across scopes rather than replaced wholesale. A matching deny is still evaluated before ask and allow. Put shared safeguards in Project settings and non-negotiable policy in Managed settings.

How to read allow, ask, and deny rules

RuleMeaning
ReadMatches every built-in read; a bare allow is normally unnecessary for files inside the project
Bash(npm test)Matches exactly npm test
Bash(npm test *)Matches npm test with or without trailing arguments
Bash(ls*)Matches ls -la, but also lsof, so it is broader than it looks
Read(//Users/me/secrets/**)Matches an absolute filesystem path
Edit(/src/**/*.ts)In Project settings, matches files below the repository’s src directory
WebFetch(domain:docs.anthropic.com)Matches WebFetch requests to that domain

Bash(git *) also covers git push origin main and git reset --hard. Allow individual safe commands instead of the whole command family.

Why Read and Edit cannot fully protect secrets

Paths in Read and Edit rules follow gitignore-style matching.

PatternAnchorExample
//pathFilesystem rootRead(//Users/me/secrets/**)
~/pathHome directoryRead(~/.ssh/**)
/pathLocation associated with the settings sourceIn Project settings, Edit(/src/**) targets the repository’s src directory
path or ./pathCurrent working directoryRead(.env)

A single leading slash is not an absolute filesystem path in a Read or Edit rule. More importantly, these rules cover Claude Code’s built-in file tools and recognized file commands. They do not stop an arbitrary Node.js or Python child process from opening a file directly.

If the repository handles credentials, combine these rules with the Claude Code approval and sandbox guide so the operating system also limits child processes. The sandbox runs on macOS, Linux, and WSL2, not native Windows; use WSL2 or an isolated container there and retain PowerShell deny rules.

Three use cases

Use case 1: Automate tests in a personal project

Input: source files, tests, and the Git diff. Output: a proposed fix and test results. Human decision: edits, dependency changes, commits, and pushes.

Start with the baseline configuration above and leave edits in ask. Move a command to allow only after you have seen it run repeatedly without changing external state.

Use case 2: Share blocked operations with a team

Input: the commands the team needs and the operations it never wants automated. Output: a Git-managed permission policy. Human decision: new deny rules and exceptional maintenance work.

Keep the .env, force-push, hard-reset, and recursive-delete rules in Project settings. Move rules that team members must not change into Managed settings.

Use case 3: Investigate a production repository without editing it

Input: incident logs and Git history. Output: likely causes and a remediation plan. Human decision: edits, deployment, and any external communication.

Start the session in Plan mode:

claude --permission-mode plan

If a write becomes necessary, switch to an isolated worktree or disposable environment before approving it.

Four concrete failure cases

FailureWhy it happensFix
Allowing Bash(git *)The same pattern covers push and hard resetAllow only commands whose side effects you have inspected
Adding allow: Bash(aws s3 ls) under deny: Bash(aws *)Deny wins before ask and allow; specificity creates no exceptionNarrow the deny rule or separate the safe operation behind another command boundary
Treating Read(.env) as an OS boundaryRead/Edit rules do not stop arbitrary Node.js or Python subprocessesAdd sandbox denyRead or credential rules
Removing a Local allow but seeing it remainPermission arrays concatenate across User, Project, and Local scopesInspect rule sources in /permissions and loaded scopes in /status

Read the Claude Code security failure cases for incident patterns and the security best practices for a broader rollout checklist.

Choosing a permission mode

ModeGood fitImportant limit
defaultA repository you are still learningPrompts when approval is required
acceptEditsDevelopment where the edit scope is already understoodFile edits and common filesystem operations can be auto-approved
planInvestigation, design, and read-only production reviewDoes not edit source files
autoTrial use of background safety checksAuto-approves calls judged to align with the request; verify the current release behavior
dontAskUnattended work limited to pre-approved operationsDenies unapproved tools instead of opening a prompt
bypassPermissionsA disposable container or VMDo not use it on a normal workstation or production host

With sandboxing enabled, sandbox.autoAllowBashIfSandboxed defaults to true. Sandboxed Bash commands may therefore run without the whole-tool Bash prompt. Content-scoped ask rules such as Bash(git push *) still prompt, explicit deny rules still apply, and Plan mode keeps its own restrictions.

Pitfall: using a broad allow rule and relying on one hook

Allowing all of Bash and expecting a PreToolUse hook to catch every destructive variant turns one hook into the only safety boundary. A misplaced hook or an incomplete pattern can then expose the entire command surface.

Cause: the allowed command set is broad, while the dynamic check is the only control.

Fix: write explicit deny rules and narrow allow rules first. Use hooks only as an additional layer for decisions that genuinely depend on runtime state. A hook’s allow result does not override a matching deny or ask rule.

Copy-ready configuration check

The following script verifies that .claude/settings.json is valid JSON and contains four minimum deny rules.

// scripts/check-claude-permissions.mjs
import { readFileSync } from "node:fs";

const path = ".claude/settings.json";
const settings = JSON.parse(readFileSync(path, "utf8"));
const deny = new Set(settings.permissions?.deny ?? []);
const required = [
  "Read(.env)",
  "Edit(.env)",
  "Bash(git push --force *)",
  "Bash(git reset --hard *)",
  "Bash(rm *)",
  "PowerShell(Remove-Item *)",
];

const missing = required.filter((rule) => !deny.has(rule));
if (missing.length > 0) {
  console.error(`Missing deny rules: ${missing.join(", ")}`);
  process.exit(1);
}

console.log("Minimum permission check: OK");
node scripts/check-claude-permissions.mjs

This is not proof that a configuration is safe. It is a small CI guard that detects when a minimum deny rule disappears.

When the settings appear not to work

  1. Open /permissions and inspect each rule and its source file.
  2. Run /status and check which settings scopes were loaded.
  3. Recheck spaces and anchors such as Bash(ls *) versus Bash(ls*), and /path versus //path.
  4. Check sandbox auto-approval and any deny or ask rule from a higher-precedence source.
  5. Move rules the team needs back into .claude/settings.json instead of leaving them as temporary CLI choices.

Summary

Create .claude/settings.json, paste the baseline, and inspect it with /permissions. Keep only inspected tests in allow, edits and external actions in ask, and secrets and destructive actions in deny. Expand automatic access one reviewed command at a time.

For a repository-ready permission template and rollout checklist, use the Claude Code product guides as the single next step.

Official references

What was actually tested

On July 22, 2026, the JSON block in all ten locales was parsed with JSON.parse, and the Node.js checker ran in a temporary project. With all six required deny rules present, it exited with code 0. Removing Bash(git reset --hard *) produced code 1 and named the missing rule. This detects configuration drift; it is not evidence from production or proof that a policy is secure. Verify active behavior with /permissions and /status.

#claude-code #permissions #settings-json #security #beginner
Free

Free PDF: Claude Code Cheatsheet

Enter your email and download the one-page Claude Code cheatsheet for commands, review habits, and safe workflows.

We handle your data with care and never send spam.

Level up your Claude Code workflow

Start with the free PDF, use Gumroad guides when you need repeatable workflows, and book consultation when rollout or revenue paths need human judgment.

Masa

About the Author

Masa

Engineer focused on practical Claude Code workflows. Runs claudecode-lab.com, a 10-language technical media site.