Permissions & Tool Allowlisting in Claude Code: A Beginner's Guide
The Scenario Remember the setup from the first article — a project with CLAUDE.md, an api-conventions skill, and a code-reviewer subagent, all working together to keep new API endpoints consistent? Same project. But Cl
The Scenario
Remember the setup from the first article — a project with CLAUDE.md, an api-conventions skill, and a code-reviewer subagent, all working together to keep new API endpoints consistent? Same project. But Claude Code doesn't only touch files — it can also run terminal commands and, if you've connected one, query your actual database.
So now picture this: mid-session, Claude decides the fix for a bug is to push the branch straight to remote before you've looked at it. Or it runs a "quick" database command to test something, and that command is a DROP. Nothing in the skill or the subagent stops either of those — they only cover how code should look and who reviews it. Neither one ever asked "should this specific action be allowed to happen at all?"
That question is what permissions answer.
Permission, in One Sentence
A permission is a gate: before Claude runs a tool call, a rule decides whether it's allowed, denied, or needs your OK first. It's not reasoning about risk — it's checking a pattern you wrote in advance.
Three Outcomes
Every tool call lands in one of three buckets:
- Allow → runs instantly, no prompt
- Ask → pauses, waits for you to say yes
- Deny → blocked, full stop
Back to the Scenario: One Real Example
Permission rules live in .claude/settings.json, right alongside the skill and subagent files from the first article:
your-project/
├── CLAUDE.md
├── .claude/
│ ├── settings.json ← permission rules go here
│ ├── skills/
│ └── agents/
└── src/
Commit that file to the repo, and the project can share the same permission configuration across the team.
Here's a rule set that directly stops both close calls from the opening — one rule per tool, so you can see this isn't just a Bash thing:
{
"permissions": {
"allow": ["Bash(git *)"],
"ask": ["Bash(git push *)"],
"deny": ["Database(DROP *)"]
}
}
-
git status,git diff→ allowed, they're harmless -
git push→ asks first — the more specificgit pushrule takes precedence over the broadergit *rule - Any
DROPquery against the database, through whatever tool gives Claude that access → denied, no exceptions
Two different tools, same three-outcome idea. Once you understand the pattern, you can use the same allow/ask/deny idea across the tools Claude Code can use.
📦 Full example: https://github.com/Alimurrazi/claude-demo-book-api/releases/tag/article-2-permissions — the repo as it stood for this article, including
.claude/settings.jsonabove.
The same rules aren't only useful for teams, either — they also protect you, three hours into a solo debugging session, from your own tired typo turning into a dropped table.
The Trade-off
More "ask" rules ≠ more safety. If Claude asks about everything, you stop reading the prompts and just click yes — and now the gate isn't protecting anything.
A permission only helps if someone actually reads it.
Takeaways
- Permission = a gate checked before a tool call runs, not a reasoning step.
- Three outcomes — allow, ask, deny — and they apply across every tool Claude has, not just Bash.
- A narrow, specific rule (like blocking just
DROPon the database) can coexist with a broad allow on everything else. - Useful for trusting a teammate less and for catching your own mistakes.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.

