Dev.to Security 🔐 Cybersecurity 👁 0 📖 2 min read

3 permission presets for Claude Code (strict, balanced, autonomous) and the gap none of them close

Every "Allow this command?" prompt in Claude Code is a small interruption. Click "always allow" enough times and you end up with a settings.json nobody has read. Here are three presets I keep in .claude/settings.json, fr

Every "Allow this command?" prompt in Claude Code is a small interruption. Click "always allow" enough times and you end up with a settings.json nobody has read. Here are three presets I keep in .claude/settings.json, from most to least careful, and the one thing none of them can do.

Rules are matched in this order: deny beats ask, and ask beats allow. Anything not listed falls back to defaultMode.

1. Strict: new or sensitive repos

Read-only git is free. Commits, pushes and installs need a confirmation, and network tools are off.

{
  "permissions": {
    "defaultMode": "default",
    "allow": [
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(git log:*)"
    ],
    "ask": [
      "Bash(git commit:*)",
      "Bash(git push:*)",
      "Bash(npm install:*)",
      "Bash(pnpm add:*)",
      "Bash(yarn add:*)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)",
      "Bash(curl:*)",
      "Bash(wget:*)",
      "WebFetch"
    ]
  }
}

2. Balanced: day-to-day work

Edits are accepted automatically (acceptEdits). Lint, tests and type checks run without asking, and anything that leaves your machine or changes dependencies still asks.

{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Bash(git status)",
      "Bash(git diff:*)",
      "Bash(git log:*)",
      "Bash(git add:*)",
      "Bash(npm run lint:*)",
      "Bash(npm run test:*)",
      "Bash(npm test:*)",
      "Bash(npx tsc --noEmit:*)"
    ],
    "ask": [
      "Bash(git push:*)",
      "Bash(npm install:*)",
      "Bash(pnpm add:*)",
      "Bash(yarn add:*)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}

3. Autonomous: throwaway branches and spikes

All of git and npm are allowed. Use it only where a bad command costs you a branch, not a production database.

{
  "permissions": {
    "defaultMode": "acceptEdits",
    "allow": [
      "Bash(git:*)",
      "Bash(npm:*)",
      "Bash(npx:*)",
      "Bash(pnpm:*)",
      "Bash(node:*)",
      "WebFetch",
      "WebSearch"
    ],
    "deny": [
      "Read(./.env)",
      "Read(./.env.*)",
      "Read(./secrets/**)"
    ]
  }
}

The gap

Look at the autonomous preset again: Bash(git:*) allows git push --force and git reset --hard too. Permission rules match a command by its prefix. They can't tell a safe git push from a destructive one.

There's a second gap. Read(./.env) blocks Claude's Read tool, but it isn't a rule about the shell. A cat .env through Bash is a different tool and needs its own handling.

That's what hooks are for. A PreToolUse hook is a script that sees the full command before it runs and can block it with exit 2, so it can reject git push --force while allowing a normal push, or refuse any command that touches .env. Two free ones (one blocks destructive commands, tested against bypasses like git push origin +main; one stops secrets from being written into files) are here: https://github.com/kailucho/claude-code-hooks-seguridad (free, MIT).

Presets for convenience, hooks for safety. You want both.

The three presets, the hooks, 6 skills (/commit, /pr, /review, /tests, /plan, /debug) and CLAUDE.md templates for React, Node and GraphQL come together in the Starter Kit for Claude Code (US$9, unofficial): https://luijhy.gumroad.com/l/starter-kit-for-claude-code

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.