ES · Leer en español Explore for free

Checklist · Intermediate · 15 min

Before you give Claude Code permissions: a security checklist

Ten checks before giving Claude Code more permissions or opening it in someone else's repo: modes, deny, ask and allow, trust, sandbox and bypass.

Cover: Before you give Claude Code permissions: a security checklist

Tested with Claude Code 2.1.316 · reviewed

See the files on GitHub Free · MIT license

What it's for

A list of ten checks to run before giving Claude Code more permissions or opening it in a repository that isn't yours. It organizes what the official docs say: where each rule lives and which one wins (deny → ask → allow, in any scope), what a project applies before and after you accept folder trust, why auto and bypassPermissions don't work from project settings, why blocking curl doesn't cut off the network and how to lock yourself out of bypass mode. Each item comes with its why and how to check it. For rule syntax, see the permissions recipe.

The checklist

# Real test: someone else's repo runs its hook with claude -p (Nov 1, 2026, Claude Code 2.1.316)
# Test repo with a SessionStart hook in .claude/settings.json that touches a file
$ claude -p "say hello" < /dev/null
Ignoring 2 permissions.allow entries from .claude/settings.json: this workspace has not been trusted. ...
Not logged in · Please run /login
$ ls ../HOOK-RAN
../HOOK-RAN          # the hook ran: no trust and no logged-in session

# The three ways to prevent it (the hook did NOT run in any of them):
$ claude -p "say hello" --setting-sources user
$ claude -p "say hello" --bare
$ claude -p "say hello" --settings '{"disableAllHooks": true}'
{
  "permissions": {
    "disableBypassPermissionsMode": "disable"
  }
}
{
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false,
    "network": {
      "allowedDomains": ["github.com", "*.github.com", "registry.npmjs.org"]
    },
    "filesystem": {
      "denyRead": ["~/.ssh", "~/.aws"]
    }
  }
}

Your personal locks, in a single file that applies to all your projects (~/.claude/settings.json):

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "disableBypassPermissionsMode": "disable",
    "blockWritesOutsideWorkingDirectories": true,
    "deny": [
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)",
      "Read(~/.config/gh/**)"
    ]
  }
}

And the review of someone else's repo before opening Claude in it:

# What Claude configuration does it bring?
ls -la .claude/ .mcp.json CLAUDE.md AGENTS.md 2>/dev/null
# Hooks, env and helpers that would run
python3 -m json.tool .claude/settings.json 2>/dev/null | grep -nE '"(hooks|env|apiKeyHelper|allow|additionalDirectories|enableAllProjectMcpServers)"'
# Skills with allowed-tools (they don't depend on trust)
grep -rn 'allowed-tools' .claude/skills 2>/dev/null
# MCP servers it would ask to connect
claude mcp list   # the ones in .mcp.json show "⏸ Pending approval" until you approve them

The repository includes this review as en/revisar-repo-ajeno.sh (it only reads files: instead of claude mcp list it prints the .mcp.json) plus the two sample JSON files in en/ejemplos/. If you're starting a project from scratch, the project template already has these permissions in place.

Common mistakes

What we tested and what's pending

On this PC (Linux, Claude Code 2.1.316), with an isolated configuration (CLAUDE_CONFIG_DIR) and without logging in: in a test repo with a project allow, claude -p and claude doctor show "Ignoring 2 permissions.allow entries… not been trusted", and the warning goes away after setting hasTrustDialogAccepted; a project SessionStart hook ran with claude -p without trust and before "Not logged in", and with --setting-sources user, --bare and --settings '{"disableAllHooks": true}' it didn't run (we repeated it on Nov 1 with the same result); a .mcp.json auto-approved by the repo shows "⏸ Pending approval" in claude mcp list. We couldn't test: the network sandbox (this PC has bwrap but not socat), that bypass and auto are ignored from the project, or disableBypassPermissionsMode and blockWritesOutsideWorkingDirectories in a real session (without a logged-in session the CLI stops earlier at "Not logged in"). Those points rely on the official docs as of Nov 1, 2026.

FAQ

Why does Claude Code ignore the deny in my settings.json?

It's almost always one of three things: the JSON isn't valid (comments or trailing commas; claude doctor shows "Invalid settings"), the path isn't written the way you think (in the user file, /path starts at ~/.claude/; use ~/path or //absolute/path) or the command doesn't match literally (Bash(curl *) doesn't stop /usr/bin/curl). Check with /permissions which file each rule comes from.

Can a repo approve its own MCP servers?

Not in an interactive session: enableAllProjectMcpServers or enabledMcpjsonServers in the repo's .claude/settings.json are ignored in an untrusted folder, and claude mcp list shows them as "⏸ Pending approval". We checked it on this PC. But with claude -p, in a folder you never trusted, the servers in .mcp.json connect without asking, approved or not; use --setting-sources user or --bare in other people's repos.

Is auto mode safe for working on my machine?

It's much safer than bypassPermissions, because a classifier reviews commands and network requests and your ask and deny rules still apply. But a model decides, not you, and the docs recommend adding a sandbox or a container as an extra layer of defense. For code or repos you don't know, start in plan or Manual.

Is --dangerously-skip-permissions in a container okay?

That's what it exists for: the docs point to containers, virtual machines or the sandbox runtime, and on Linux and macOS as a non-root user. Even so, if the container has your credentials or network access, a mistake or a prompt injection can use them.

What's the difference between deny and a PreToolUse hook?

Claude Code enforces both. Deny is declarative and matches the tool pattern; the hook is your own program that can look at anything (for example, which branch you're on) and block with exit 2. The hook blocks even if there's an allow, and an allow returned by the hook doesn't override a deny or an ask.

The whole library