C Claude Cert Prep All Claude Certifications

settings.json and Permissions

How settings.json configures permissions, hooks, and environment variables, and how to design a correct allow/deny/ask permission set.

Claude Code·Lesson 2 of 6·7 min

settings.json is the configuration file for Claude Code's behavior — it lives at the user level or the project level and covers permissions, hooks, environment variables, and other runtime behavior. Where CLAUDE.md tells Claude what it should know, settings.json tells Claude Code what it is allowed to do.

Permission rules

Permissions are expressed as rules that fall into three categories: allow, deny, and ask. Each rule targets a tool or a specific command pattern — for example, allowing Bash(npm test:*) while denying Bash(rm -rf:*). When Claude Code is about to use a tool, it checks these rules to decide whether to proceed silently, block outright, or pause and prompt the user for confirmation.

The exam frames correct permission design as a specific design principle: the narrowest permission set that still lets the workflow complete. This is not the same as "deny everything by default" and it is not the same as "allow everything for convenience." Both extremes are wrong answers in different ways.

Exam trap A common distractor pattern presents two options: one that allows broad tool categories "to reduce interruptions" and one that denies almost everything "to be safe," and asks you to pick the better practice. Neither is correct — the right answer is almost always the option describing scoped, workflow-specific allow rules combined with deny rules for genuinely dangerous operations, leaving ask as the fallback for the gray area in between. If an option describes permissions as all-or-nothing, it is wrong.

Where settings live

settings.json can exist at the user level (applies across all your projects) and at the project level (checked into the repo, shared with the team, typically the place for permissions that reflect what's actually safe in this specific codebase). Project-level settings.json is how a team standardizes what an agent may do in their repo without relying on every individual engineer remembering to configure it themselves — this is distinct from CLAUDE.md, which shapes what Claude knows, not what it's allowed to execute.

settings.json is also where hooks get registered and where environment variables for the Claude Code process get set — permissions are the piece most heavily tested, but don't lose sight of the fact that this one file is the general behavior-configuration surface, not a permissions-only file.

Scenario: A team wants Claude Code to autonomously run their test suite and linter, read any file in the repo, and propose git commits, but they never want it to push directly to any remote branch or install new npm packages without asking. What's the correct settings.json design? Allow Bash(npm test:*), Bash(npm run lint:*), and file-read tools; set Bash(git push:*) to deny (not ask — "never" means never, and ask still permits a tired reviewer to approve it by habit); set package installation commands to ask, since it's a judgment call that depends on which package. This mixes all three rule types deliberately rather than defaulting to one.

Rule specificity

Permission rules can be scoped narrowly, down to a specific command pattern, rather than only at the level of a whole tool. Allowing all of Bash is very different from allowing only Bash(npm test:*) — the exam expects you to recognize that a rule written at the tool level (all Bash) is almost never the narrowest workable set, because "Bash" covers everything from running a linter to deleting a partition. Well-designed permission sets tend to have many small, specific allow rules rather than one broad one, precisely because that specificity is what lets you allow the 90 percent of commands that are routine while still asking or denying the ones that aren't.

Try it

Open (or create) .claude/settings.json in a real project. Add an allow rule for your test command, a deny rule for a command you never want run automatically (like git push --force), and leave everything else on the default ask behavior. Start a session and try to trigger each rule to confirm the permission system responds the way you configured it.

← CLAUDE.md and the Memory Hierarchy Slash Commands vs. Skills →