Skip to content

dylancaponi/claude-code-permissions

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Claude Code Permissions

My living permissions config for Claude Code. 3-layer defense that auto-approves safe commands, prompts for dangerous ones, and enforces OS-level sandboxing.

The Problem

Out of the box, Claude Code prompts for permission on nearly every bash command. Compound commands (&&, |, ;) are always unique strings that never match glob patterns, so you end up approving everything manually.

How It Works

Layer 1: Permission Rules

settings.json auto-allows read tools (Read, Glob, Grep, WebSearch, WebFetch, Task), Bash, and all first-party Claude MCP servers (mcp__claude_ai_*). Denies reads to ~/.ssh, ~/.aws, ~/.gnupg. File edits (Edit) and third-party MCP servers still prompt.

Layer 2: PreToolUse Hook

check-bash-command.sh runs before every bash command. It splits compound commands into subcommands (&&, ||, ;, |, $()) and checks each against ~90 deny regex patterns. Dangerous commands prompt for approval (you can still say yes). Everything else auto-approves.

What it catches: rm -rf, sudo, force push, git reset --hard, gh repo delete, brew uninstall, 1Password mutations, cloud delete ops (terraform destroy, kubectl delete, aws deletions), database drops, curl | sh, osascript, keychain access, ssh/scp, curl -X DELETE, eval, npx, and ~70 more patterns.

What it allows through: ls, git status/diff/log/add/commit/push, brew install, pip install, npm install/run, op item list/get, docker ps/build, gcloud auth, and all other non-destructive commands.

Layer 3: OS Sandbox (macOS Seatbelt)

Kernel-level enforcement. Even if layers 1 and 2 are bypassed:

  • Filesystem: write only to working directory and /tmp
  • Network: only allowed domains (GitHub, npm, PyPI, Google, Anthropic, Homebrew, 1Password)
  • Reads blocked: ~/.ssh, ~/.aws, ~/.gnupg

Setup

1. Copy the hook

mkdir -p ~/.claude/hooks
cp check-bash-command.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/check-bash-command.sh

2. Copy settings

cp settings.json ~/.claude/settings.json

That's it. ~/.claude/settings.json applies globally to all Claude Code sessions on your machine. No per-project config needed.

3. Verify

Start a new Claude Code session and run /permissions to confirm your rules are active.

File Overview

File Purpose
settings.json Permissions, sandbox config, hook registration. Copy to ~/.claude/settings.json
check-bash-command.sh PreToolUse hook with ~90 deny patterns. Copy to ~/.claude/hooks/
test-hook.sh 168-test suite. Run to verify the hook works
analysis.md Research notes, tradeoffs, red team analysis

Running Tests

./test-hook.sh

Expected output: 168 passed, 0 failed

Customizing

Add a deny pattern

Edit check-bash-command.sh and add a tuple to DENY_PATTERNS:

(r'\bmy-dangerous-command\s', 'my-dangerous-command'),

Then add a test to test-hook.sh:

check ask "my-dangerous-command" "my-dangerous-command --help"

Add an allowed domain

Edit settings.json and add to sandbox.network.allowedDomains:

"my-api.example.com"

Exclude a tool from sandboxing

Some tools (like gh, op, pyenv) don't work inside the sandbox due to TLS or socket issues. Add them to sandbox.excludedCommands:

"excludedCommands": ["gh", "op", "pyenv"]

They still go through the hook.

Known Limitations

The hook is a convenience safety net, not a security boundary. The OS sandbox is the real security boundary.

Can bypass the hook:

  • Variable indirection: CMD=rm; $CMD -rf .
  • Brace expansion: {rm,-rf,.}
  • Scripting languages: python3 -c "shutil.rmtree('.')"
  • Build tools: make, npm run execute arbitrary code from project files
  • Cross-tool attacks: edit a Makefile via Edit tool, then run make

Caught by OS sandbox (even if hook is bypassed):

  • Writes outside working directory
  • Network to non-allowed domains
  • Reading sensitive directories

Acceptable risk: Working directory damage is recoverable via git checkout.

Settings Precedence

Claude Code merges settings from multiple sources (highest priority first):

  1. Managed settings (enterprise/MDM)
  2. CLI arguments
  3. .claude/settings.local.json (per-project, gitignored)
  4. .claude/settings.json (per-project, committed)
  5. ~/.claude/settings.json (global) <-- this repo

Project-level settings can override your global settings. This is useful for stricter rules on sensitive repos.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages