Permissions Policy skill for Claude Code

Fewer prompts for safe work, hard stops for the rest. Designs a layered permission setup instead of clicking yes all day. Claude classifies the actions your repo really performs, allows the routine read-only and workspace work, asks for anything that leaves the machine, denies secrets and irreversible commands, and commits the shared baseline. It also covers what rules cannot stop, such as reading .env through Bash, and adds hooks or the sandbox there.

Name
permissions-policy
Size
178 lines · ≈2,050 tokens
Files
1
License
MIT
Updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Configures Claude Code permissions with allow, ask, and deny rules across user, project, local, and managed settings scopes, covering Bash prefix rules, Read and Edit path patterns, WebFetch domains, MCP tools, default modes, sandboxing, and a committed team baseline. Use when the user is tired of permission prompts, wants to block secrets or destructive commands, sets up Claude Code for a team or organization, or asks why a rule is not matching.

Try asking

  • Stop asking me every time Claude runs tests or git diff in this repo
  • Make sure Claude can never read our .env files or force push
  • Set up a shared Claude Code permission baseline for the whole team

Use it when

  • Permission prompts interrupt routine work
  • Secrets or production tooling must be off limits
  • Rolling Claude Code out to a team or organization
  • A permission rule does not match the way you expected

Skip it when

How to check the result

Try every denied action, including the Bash route to the same secret, and confirm each is blocked, then run a routine task and confirm it finishes with zero prompts.

The common mistake it prevents

Assuming a Read deny on .env protects the file, when cat or grep through Bash can still print it unless a hook or the sandbox blocks that path.

~/.claude/skills/permissions-policy/SKILL.md

--- name: permissions-policy description: Configures Claude Code permissions with allow, ask, and deny rules across user, project, local, and managed settings scopes, covering Bash… license: MIT ---

A good policy produces no prompts for routine safe work, hard-blocks secrets and irreversible operations, asks for the risky-but-legitimate middle, and is layered so the team baseline is committed while personal additions stay local. It is verified by trying the blocked actions, not by reading the JSON.

Scopes and precedence

ScopeFileWhoCommit?
Managed policySystem path set by IT (e.g. /Library/Application Support/ClaudeCode/managed-settings.json on macOS, /etc/claude-code/managed-settings.json on Linux)Whole org; cannot be overriddenn/a
Command line--allowedTools, --disallowedTools, --permission-modeOne runn/a
Local project.claude/settings.local.jsonYou, this repoNo (auto-gitignored)
Shared project.claude/settings.jsonEveryone on the repoYes
User~/.claude/settings.jsonYou, every repoNo

Precedence for conflicting settings: managed > command line > local > project > user. Permission rules from all scopes are combined, and evaluation order is deny, then ask, then allow: a deny anywhere beats an allow anywhere.

Rule syntax

{
  "permissions": {
    "allow": ["Bash(npm run test:*)", "Bash(git diff:*)", "Edit(src/**)"],
    "ask":   ["Bash(git push:*)"],
    "deny":  ["Read(./.env)", "Read(./.env.*)", "Bash(rm -rf:*)"]
  }
}
RuleMatches
Edit (bare tool name)Every use of that tool
Bash(npm run build)That exact command
Bash(npm run test:*)Commands starting with npm run test
Read(./.env) / Read(./secrets/**)Paths relative to the current directory (gitignore-style globs)
Edit(/src/**)Relative to the settings file’s directory (project root for project settings)
Read(~/.ssh/**)Home-relative path
Read(//etc/passwd)Absolute path (note the double slash)
WebFetch(domain:docs.python.org)Fetches to that domain
mcp__githubAll tools from the github MCP server
mcp__github__create_issueOne MCP tool

Inspect and edit the effective rules, with their source file, using /permissions.

Workflow

- [ ] 1. Inventory: which prompts appear repeatedly, and what must never happen
- [ ] 2. Classify each action: allow / ask / deny
- [ ] 3. Write the team baseline in .claude/settings.json
- [ ] 4. Put personal conveniences in settings.local.json or user settings
- [ ] 5. Back up hard requirements with hooks or the sandbox
- [ ] 6. Test every deny and a sample of allows; commit; document in CLAUDE.md

2. Classify

ClassExamplesRule
Read-only, localgit status, git diff, git log, ls, test runners, linters, type checksallow
Workspace editsEdit/Write inside source dirsallow (or defaultMode: acceptEdits)
Reversible but visiblegit commit, package installs, dev serversallow or ask by team taste
Leaves the machine or shared stategit push, gh pr create, deploy scripts, DB migrations on shared DBsask
Secrets.env*, *.pem, ~/.aws/**, ~/.ssh/**, credential filesdeny (Read and Edit)
Irreversiblerm -rf, git push --force, git reset --hard, DROP, terraform apply on proddeny, or ask with a hook guard

3. Team baseline (.claude/settings.json)

{
  "permissions": {
    "allow": [
      "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(git show:*)",
      "Bash(pnpm test:*)", "Bash(pnpm lint:*)", "Bash(pnpm typecheck:*)", "Bash(pnpm build:*)",
      "Bash(ls:*)", "Bash(rg:*)",
      "Edit(/src/**)", "Edit(/test/**)", "Edit(/docs/**)",
      "WebFetch(domain:developer.mozilla.org)", "WebFetch(domain:nodejs.org)"
    ],
    "ask": [
      "Bash(git push:*)", "Bash(gh pr create:*)", "Bash(pnpm db:migrate:*)", "Bash(pnpm add:*)"
    ],
    "deny": [
      "Read(./.env)", "Read(./.env.*)", "Edit(./.env*)", "Read(./**/*.pem)",
      "Read(~/.ssh/**)", "Read(~/.aws/**)",
      "Bash(git push --force:*)", "Bash(git reset --hard:*)", "Bash(rm -rf:*)",
      "Bash(curl:*)", "Bash(wget:*)",
      "Edit(/infra/prod/**)"
    ]
  }
}

Adapt package-manager commands to the repo (read package.json scripts, Makefile, CI). Denying curl/wget pushes web access through WebFetch, where domain rules work.

4. Other permission settings

KeyUse
permissions.defaultModedefault (prompt), acceptEdits (auto-accept file edits), plan (read-only planning), bypassPermissions (no checks; containers only)
permissions.additionalDirectoriesExtra directories Claude may work in beyond the project root
permissions.disableBypassPermissionsMode: "disable"In managed settings: forbid bypass mode org-wide

Cycle modes interactively with Shift+Tab. bypassPermissions (or --dangerously-skip-permissions) belongs only in disposable, network-restricted containers without credentials, and only after the user explicitly confirms.

5. Back up hard requirements

Permission rules match tool calls, so know their limits:

  • Read(./.env) blocks Claude’s Read tool, not cat .env or grep KEY .env through Bash, and not a script that reads the file. Add a Bash PreToolUse guard (see hooks-automation) or enable the sandbox for real isolation.
  • Bash prefix rules match the command string. Claude Code recognizes shell operators, so Bash(npm test:*) does not approve npm test && curl evil. But argument-level constraints are fragile: Bash(curl https://api.example.com:*) can be sidestepped with options, variables, or redirects. Do not rely on Bash patterns to restrict URLs or paths.
  • The Bash sandbox (/sandbox; OS-level isolation on macOS and Linux) restricts filesystem writes to the workspace and network to allowed hosts, and lets sandboxed commands run without prompts. It is the right tool when you want fewer prompts and real containment. Configure it through /sandbox or the sandbox settings block for the installed version.

Organization policy

For company-wide rules use managed settings deployed by MDM or configuration management: deny rules for secrets and production tooling, disableBypassPermissionsMode, and approved MCP servers. Users cannot override managed values. Keep the managed file small and stable; put repo-specific rules in each repo’s project settings.

Gotchas

  • Rules in settings.local.json are invisible to teammates; anything the team depends on belongs in .claude/settings.json.
  • Edit(src/**) and Edit(/src/**) mean different things (current directory vs. settings-file directory). Use the leading-slash form in project settings.
  • Over-broad allows (Bash, Bash(*), Edit) erase the policy. Allow specific prefixes.
  • An ask for something run 50 times a day trains people to click yes without reading. Allow it or deny it.
  • Headless runs (claude -p) do not prompt: un-allowed tools are denied silently. CI needs explicit --allowedTools.
  • JSON syntax errors make the whole settings file ignored. Validate with python3 -m json.tool .claude/settings.json.

Verification

  1. python3 -m json.tool on each edited settings file.
  2. /permissions shows the rules with the expected source scope.
  3. For every deny rule, ask Claude to perform the action (read .env, force push) and confirm it is blocked. Also try the Bash route to the same secret and confirm the hook or sandbox stops it.
  4. Run a normal task (tests, lint, edit in src/) and count prompts; routine work should prompt zero times.
  5. Re-review the policy when tooling changes (new package manager, new deploy script).

Output

**Scopes:** .claude/settings.json (team, committed); ~/.claude/settings.json unchanged
**Allow:** 14 rules (git read-only, pnpm test/lint/typecheck/build, edits in src/test/docs)
**Ask:** git push, gh pr create, db:migrate, pnpm add
**Deny:** .env*, *.pem, ~/.ssh, ~/.aws, force push, reset --hard, rm -rf, curl/wget, infra/prod
**Backstops:** PreToolUse Bash guard blocks shell reads of .env (tested)
**Tested:** 9/9 denies blocked; routine task ran with 0 prompts

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc