Claude Code settings.json: secure configuration patterns
Claude Code reads a small JSON file before it does anything else, and that file decides how much of your machine the agent is allowed to touch. settings.json merges layers from enterprise policy down to a per-project file, and every layer can carry permissions, hooks, and environment variables. A mistake in any of them is not a cosmetic issue: it is the difference between an agent that can read one directory and an agent that can read everything, between a credential that stays in your password store and one that ends up in a model’s context window.
What settings.json actually controls
Claude Code looks for settings in several places — the user-level ~/.claude/settings.json, project-level .claude/settings.json, and managed enterprise files — and merges them in order. The file carries:
- Permissions. Allow and deny lists for tools like Read, Write, Edit, and Bash.
- Hooks. Shell commands that run on events such as PreToolUse, PostToolUse, and Stop.
- Environment. Variables injected into the agent’s subprocess environment.
- Model and flags. Which model runs, output styles, and behavioral toggles.
Because it is plain JSON, the file is easy to read, easy to edit, and easy to get wrong in invisible ways.
Three failure modes worth knowing
Secrets in the env block. The most common pattern is dropping an API key into env so the agent can call a service. That key is now readable by the model, inherited by every subprocess the agent spawns, and recoverable from tool output whenever a command prints its environment or fails with a verbose error.
Overly broad permissions. A one-time Read grant for a config file becomes an allow list that covers the whole home directory. Agents act on what the file permits, not on what you meant to permit.
Unreviewed hooks. Hooks execute shell commands on agent events. They are a legitimate automation surface and an obvious place for a compromised project to plant a command that runs on every session.
Secure configuration patterns
Keep secrets out of settings.json entirely. The file should reference credentials by name, never by value. A credential broker can inject the actual value into the subprocess environment at run time, so the agent uses the secret without ever seeing it. That keeps settings.json clean, diffable, and safe to version control.
Grant permissions narrowly. Start from a deny-heavy posture and add the minimum tool access each task needs. Review allowedTools periodically — stale grants accumulate.
Treat hooks like code. Read every hook before you run it. Prefer hooks that are short, idempotent, and call well-known binaries over inline scripts. Never let a hook read a file that untrusted content can write.
Separate user and project layers. Project settings travel with the repository. Anything secret, or anything that grants broad access, belongs in the user layer, not in a file that ships inside a pull request.
Diff the merged result. Because settings merge across layers, review what the agent actually sees, not just the layer you edited. Syntax validation catches malformed JSON; only a human catches a permission that is broader than intended.
The settings file as an attack surface
settings.json sits inside the trust boundary of every session. If a prompt injection convinces the agent to call a tool, the permission list decides what happens next. If a hook is malicious, it runs on every event. If a key is in the env block, it is in the context window. All three are configuration, which means all three are reviewable.
When credentials do need to reach a session, a credential broker such as trustless keeps them out of the file: values are injected into the subprocess environment and scrubbed from stdout and stderr, so settings.json holds names, not secrets. The command reference is in the documentation.
When did you last read your settings.json top to bottom, and would you be comfortable pasting the merged result into a pull request?