Template · Intermediate · 5 min
Claude Code settings.json checker: find permission risks
Paste your Claude Code settings.json and see every permission risk with its level, a plain explanation and the link to the official documentation.
Tested with Claude Code 2.1.316 · reviewed
See the files on GitHub Free · MIT license
Try it here
It runs in your browser: nothing is sent to any server.
What's included
The tool is above: you paste a .claude/settings.json, ~/.claude/settings.json or settings.local.json, choose which file it is and in a second you see every risk sorted by level (critical, high, medium, low, info), with its explanation and the link to the official page that backs it. There are 21 rules, each based on a specific sentence from the Claude Code 2.1.316 pages on permissions, permission modes, sandbox and settings reference, and all of them have automated tests. It runs entirely in your browser: the text never leaves your machine. The findings are written in Spanish; the level names are crítico (critical), alto (high), medio (medium), bajo (low) and info. The English version in the repository's en/ folder shows them in English.
The code (a single JavaScript file with no dependencies, an example index.html, its Node tests and the example files) is in the free repository ctrlia-comprobador-settings.
Critical and high rules
| ID | Level | What it detects |
|---|---|---|
| R01 | critical (user) · info (project/local) | defaultMode: "bypassPermissions": in the user file, every session with no checks (the docs limit it to isolated containers or VMs); in project or local it only reports that it is ignored |
| R03 | high | skipDangerousModePermissionPrompt: true: removes the bypass confirmation |
| R05 | critical | Bash or Bash(*) in allow: the docs say it equals every command |
| R06 | high | Wildcards that amount to running code: interpreters and launchers (python3, node, bash -c, npx, uvx, uv run, docker exec, make, sudo, ssh, git…) followed only by * or by one option and * |
| R09 | high | No Read deny for .env |
| R13 | high | enableAllProjectMcpServers: true |
| R14 | high | mcp__* in allow |
| R16 | high | additionalDirectories with / or the home folder |
| R17 | critical (project) · high | Keys in the env block; critical if the file is a project file, because it is committed to git |
On R06, the docs explain that wrappers such as devbox run or docker exec are not stripped from the command and that Bash(git * main) includes git -c, which runs a program.
Medium, low and informational rules
| ID | Level | What it detects |
|---|---|---|
| R07 | medium | * before the subcommand (Bash(git * main), Bash(* --version)): Claude Code already warns at startup since 2.1.246 because it approves inserted options |
| R08 | low | No space before the *: Bash(ls*) approves lsof |
| R10 | low | A .env deny without .env.* (leaving .env.local, .env.production) |
| R11 | medium | A curl/wget deny or ask used as a network block: the docs show it does not stop /usr/bin/curl or sh -c; the real block is the sandbox |
| R12 | medium | Write(path), Glob(path), NotebookEdit(path) or MultiEdit(path) rules: accepted but they never match; use Edit(path) and Read(path) |
| R15 | medium | WebFetch without a domain in allow |
| R02 | info | defaultMode: "auto" in project or local (ignored) |
| R04 | info | Suggestion of disableBypassPermissionsMode: "disable" in the user file |
| R18 | info | hooks, apiKeyHelper or statusLine in a project file: they run even with claude -p in an untrusted folder |
| R19 | info | Sandbox turned off |
| R20 | low | Sandbox on but allowUnsandboxedCommands not set to false |
| R21 | info | Reminder: allow rules in a project file only apply once you accept to trust the folder |
Honest limits
It reviews the text of the rules; it does not analyze your system:
- It does not read your hooks or scripts, it only warns that they exist.
- The list of wrappers is fixed and may miss some (for example your own script that accepts commands).
- It does not merge the four scopes the way Claude Code does; that is why you should also check
/permissions. Readdeny rules only protect against Claude's tools and the file commands Claude Code recognizes (cat, head, sed…), not against a Python or Node program that opens the file on its own, as the docs say; that is what the sandbox is for.- "No findings" does not mean safe.
Each finding links to the official page so you do not have to take our word for it. The rules are reviewed every 30 days against the CHANGELOG, because the permission system changes in almost every version.
Privacy and technical design
A single script served from ctrlia.dev, loaded with defer, with no inline code, no fetch, no storage and no dependencies. The JSON is parsed with JSON.parse (never eval) and the results are rendered with textContent, so a file with HTML or scripts inside cannot run. The analyze(text, scope) function in the repository's en/checker.js is pure and returns {error} or {findings: [{id, level, text, doc}]}, which makes it testable with Node and reusable, for example in a hook. Accessibility: a label on the selector and on the text area, a real button, results in a list with aria-live="polite" and levels shown as text, not only as color. Even though nothing is sent, remove keys before pasting.
How to use it
1. Choose which file it is
It matters, because Claude Code treats each scope differently. bypassPermissions or auto in .claude/settings.json or settings.local.json has no effect (the session starts in Manual), but in ~/.claude/settings.json it does; and allow rules in a project file only apply once you accept to trust the folder, while deny and ask always apply.
2. Paste the content and press "Comprobar" (Check)
If it is not valid JSON it tells you where the error is: the file does not allow comments or trailing commas.
3. Read the findings from critical to info
Open the link on each one: it is the official page that explains it.
4. Fix and check again
Fix things with the Claude Code permissions recipe and paste the file again. To see what is really loaded (adding up every scope), run this inside Claude Code:
/permissions
It lists every rule and the file it comes from. The tool looks at one file at a time; Claude Code merges the user, project, local and managed ones.
The files
A project .claude/settings.json with typical mistakes (en/example.json in the repository):
{
"permissions": {
"allow": ["Bash(npm run *)", "Bash(python3 *)", "Bash(git * main)", "WebFetch"],
"deny": ["Bash(curl *)", "Write(./secrets/**)"]
},
"enableAllProjectMcpServers": true,
"env": { "STRIPE_SECRET_KEY": "sk_test_1234567890" }
}
Real output from the repository's en/checker.js with that example, project scope (the tool on this page still shows the findings in Spanish):
R06 · high: "Bash(python3 *)" amounts to running arbitrary code: python3 runs whatever comes after it. Write the exact command (e.g. Bash(python3 -m pytest *)).
R07 · medium: "Bash(git * main)" has a * before the subcommand: it also approves options like git -c, which run programs. Put the * at the end.
R09 · high: No deny for .env: add "Read(.env)" and "Read(.env.*)" (a bare name covers any depth). It blocks Claude's tools and cat/head/sed, not a script that opens the file: for that, use the sandbox.
R11 · medium: "Bash(curl *)" is not a network block: it does not stop /usr/bin/curl, sh -c "curl …" or other programs. To really block the network, turn on the sandbox with its domain list.
R12 · medium: "Write(./secrets/**)" never matches: paths are only checked with Edit(...) and Read(...). If it was a deny, it does not protect you.
R13 · high: enableAllProjectMcpServers: true approves without asking every server in .mcp.json (in the project file, once you accept to trust the folder).
R15 · medium: "WebFetch" lets it download any URL without asking: a door for injected instructions. Limit it to specific domains.
R17 · critical: env.STRIPE_SECRET_KEY has a value that looks like a secret in a file that is committed to git. Use system environment variables or apiKeyHelper.
R19 · info: Sandbox turned off. It is the only thing that blocks file reads and network for every process, not just for Claude's tools (/sandbox).
R21 · info: The allow rules in this file only apply once you accept to trust the folder; deny and ask always apply.
In short: Bash(python3 *) runs arbitrary code (R06); the * before the subcommand also approves git -c (R07); there is no .env deny (R09); a curl deny is not a network block (R11); Write(...) never matches (R12); every .mcp.json server is approved (R13); WebFetch without a domain (R15); a secret in a file committed to git (R17); sandbox off (R19); and the folder-trust reminder (R21). On the page the findings are sorted from critical to info, each with its link.
A starting point that passes the checker with no high or critical findings (en/starting-point.json, for .claude/settings.json): allow with exact commands, ask for git push and git commit, deny for .env, .env.*, secrets/** and keys, and the sandbox on in strict mode.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm run test)",
"Bash(npm run lint)",
"Bash(python3 -m pytest *)",
"Bash(git status *)",
"Bash(git diff *)",
"Bash(git log *)"
],
"ask": ["Bash(git push *)", "Bash(git commit *)"],
"deny": [
"Read(.env)",
"Read(.env.*)",
"Read(./secrets/**)",
"Read(**/*.pem)",
"Read(**/*.key)"
]
},
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": false
}
}
Before copying it, adapt the commands to your project: a rule that does not match neither protects you nor saves you prompts. With the sandbox on and autoAllowBashIfSandboxed at its default, the docs say commands inside the sandbox run without asking: the boundary becomes the sandbox, not the allow list. In your user file (~/.claude/settings.json) also add:
{
"permissions": { "disableBypassPermissionsMode": "disable" }
}
The repository has all the tool's files:
| File | What it is |
|---|---|
en/checker.js | The analyze function (21 rules) and the interface, no dependencies and no inline code |
en/index.html | Example page with the form, loading checker.js with <script src> |
en/tests.js | Node checks |
en/example.json | The file with typical mistakes shown above |
en/starting-point.json | The starting configuration shown above |
The Spanish originals (comprobador.js, pruebas.js, ejemplo.json…) are at the repository root.
To try it on your machine:
git clone https://github.com/carlosp5751258-lgtm/ctrlia-comprobador-settings
cd ctrlia-comprobador-settings/en
node tests.js # → ok: checker
Then open en/index.html in your browser. To understand each rule in depth, see the security hooks and MCP for developers recipes. If you also want a CLAUDE.md, use the CLAUDE.md generator. Sources: permissions, permission modes, settings, settings reference, sandboxing, headless mode, security and the CHANGELOG.
FAQ
Why is Bash(python3 *) dangerous if I only want to run my scripts?
Because it also approves python3 -c "…" with any code. Write the rule with a fixed argument: Bash(python3 -m pytest *) or Bash(python3 scripts/build.py).
I put bypassPermissions in .claude/settings.json and it does nothing. Why?
That is the documented behavior: from that file and from settings.local.json it has no effect and the session starts in Manual. It only works from ~/.claude/settings.json or the command line, and the docs limit it to isolated environments.
Does a Bash(curl *) deny block the internet?
No. The docs show it does not stop /usr/bin/curl or sh -c 'curl …'. To really block the network, use the sandbox with its domain list.
Does Read(.env) protect my .env from everything?
From Claude's tools and from commands like cat or sed, yes. From a script that opens the file on its own, no: that is what the sandbox is for.
Is my file sent to any server?
No. The analysis happens in your browser. Even so, delete keys before pasting: they should not be in settings.json.
I get no warnings. Am I safe?
Not necessarily: the tool reviews the text of the rules, not your hooks or scripts, and it looks at one file at a time. Also check /permissions inside Claude Code.