# Security governance for rolling Claude Code out to an organization — permission modes, managed settings, sandboxing and CI

> What to decide before handing Claude Code to a team: which of the six permission modes to allow, the deny→ask→allow evaluation order, delivering managed settings and the keys users can't override, the sandbox that is off by default, hook governance, and the CI trust trap, per the official docs as of October 2026.

- Published: 2026-10-12
- Author: 友田 陽大
- Tags: Claude Code, AI駆動開発, セキュリティ, AI
- URL: https://tomodahinata.com/en/blog/claude-code-enterprise-security-governance-managed-settings-guide
- Category: AI-driven development & productivity
- Pillar guide: https://tomodahinata.com/en/blog/spec-driven-development-claude-code-ai-agent-production-workflow

## Key points

- Claude Code works on files and runs shell commands with the developer's own permissions. Organizational design means deciding what runs automatically, what needs confirmation and what is forbidden, then putting that in managed settings developers can't override
- Permission rules are evaluated deny, then ask, then allow, and a deny from any scope beats an allow from another. But the docs say outright that neither Bash rules nor Read denies are a security boundary; OS-level enforcement is the sandbox's job
- The sandbox is off by default, and even when on it can read ~/.ssh and ~/.aws/credentials by default. Hooks, local MCP servers and built-in tools such as Read and WebFetch run outside it
- Server-managed settings (policy delivered from claude.ai) require Team or Enterprise, are explicitly 'not a security boundary', and are not fetched when you use Bedrock or other third-party providers. Pair them with endpoint management (files or MDM)
- In CI, `claude -p` shows no trust dialog and runs the repository's hooks and .mcp.json. Narrow it with --bare, dontAsk and --allowedTools, and decide up front who can trigger the GitHub Action and how pull requests are handled

---

The short answer first. **When you bring Claude Code into an organization, the thing to design is not how clever the model is. It is what an agent running on a developer's machine, with that developer's permissions, may do without asking, what it must confirm, and what it may never do.** You then put those decisions into **managed settings** that developers can't override, and cover what permission rules can't stop with the sandbox and device management.

How an individual or a small team hardens its own `settings.json` is covered in [Running Claude Code in production — permissions, hooks, CLAUDE.md and security gates](/blog/claude-code-production-operations-security-gates-guide). This article picks up where that one stops: the design a **CTO, infrastructure lead or security lead** applies when handing Claude Code to a whole team. Whether to adopt a given MCP server is split out into [Trusting and governing third-party MCP servers](/blog/claude-code-mcp-server-security-third-party-trust-guide).

Every setting key, path and default here was checked against the official documentation (code.claude.com/docs) as retrieved on 2026-10-12. Claude Code ships quickly and many behaviors depend on the version, so confirm them against the version you actually deploy.

---

## 1. Threat model: an agent running with the developer's own permissions

**What you are protecting is not "the Claude Code tool" but the developer's machine and everything it can reach.** Claude Code runs locally, reads and writes files, and runs shell commands with the developer's user permissions. The official Security page says the working-directory boundary is only a permission prompt, and that "a Bash command you approve can still write anywhere your user account can."

Thinking about the blast radius in five channels keeps you from missing anything.

| Channel | What can happen | Main controls |
|---|---|---|
| Files | Reading secrets, rewriting files outside the working directory | Read/Edit rules, sandbox filesystem settings |
| Shell | Running arbitrary commands, getting around rules via child processes | Permission modes, Bash rules, the sandbox |
| Network | Exfiltration, connecting to unapproved hosts | Sandbox network settings, WebFetch rules |
| Credentials | Leaking `~/.ssh`, `~/.aws` or environment variables | denyRead, `sandbox.credentials`, scrubbing the environment |
| CI | Runs nobody is watching, instructions planted in untrusted pull requests | `--bare`, `dontAsk`, the Action's trigger rules |

The entry point that deserves the most attention is prompt injection: instructions hidden in outside content (an issue body, a web page, an MCP server's response) that try to take over the agent's decisions. The basic stance for an organization is not to rely on the model's judgment, but to **use settings to define a range the agent can't reach even if its judgment is hijacked**.

## 2. Permission modes: which of the six to allow, and for whom

**As of October 2026 there are six permission modes. The first organizational decisions are to keep bypassPermissions off developer machines, and whether to allow auto mode.**

| Mode | What runs without asking | Intended use (per the docs) | Example organizational policy |
|---|---|---|---|
| `default` (shown as Manual) | Reads only | Reviewing every action yourself, sensitive work | Allowed for everyone; the default for sensitive repositories |
| `acceptEdits` | Reads, file edits and common filesystem commands such as `mkdir` | Iterating on code you're reviewing | Allowed for day-to-day development |
| `plan` | Reads (plus classifier-approved commands when auto mode is available) | Exploring before changing anything | Allowed for everyone |
| `auto` | Everything, with background safety checks | Long tasks, less prompt fatigue | A policy call; to block it, `disableAutoMode` |
| `dontAsk` | Reads in the working directories and pre-approved tools; anything that would prompt is denied | Locked-down CI and scripts | CI only |
| `bypassPermissions` | Everything | Isolated containers and VMs only | Disabled on developer machines |

In `auto` mode, a separate classifier model reviews actions instead of the user and blocks the ones it judges unsafe. Note that from v2.1.283, `auto` is the starting mode for sessions launched in a terminal. To block it organization-wide, set `disableAutoMode` (also accepted as `permissions.disableAutoMode`) to `"disable"` in managed settings.

`bypassPermissions` removes the prompts and safety checks. The docs warn to use it only in isolated environments such as containers, VMs or dev containers without internet access. Setting `permissions.disableBypassPermissionsMode` to `"disable"` also makes Claude Code reject the `--dangerously-skip-permissions` flag.

Deny rules work regardless of mode: the docs state that "deny rules block in every mode, including `bypassPermissions`." Also, `defaultMode: "bypassPermissions"` or `"auto"` in a project's `.claude/settings.json` doesn't take effect from that file, so a cloned repository can't loosen the mode on its own.

## 3. The allow / ask / deny order, and denies that cross scopes

**Rules are evaluated deny, then ask, then allow, and the first match decides the outcome. Writing a more specific rule doesn't change that order.**

The order also crosses scopes. In the docs' words, "a user-level deny blocks a project-level allow, because deny rules from any scope are evaluated before allow rules." A deny in managed settings can't be overridden by `--allowedTools`, while `--disallowedTools` can add further restrictions.

That has two implications for an organization.

- **Anything you put in deny loses to no allow anywhere below it.** Put the operations you must never let through, such as reading secrets or `git push --force`, in the managed deny list.
- **Allow widens access, so you may want to control where it comes from.** With `allowManagedPermissionRulesOnly: true`, Claude Code ignores allow, ask and deny rules from user, project, local and `--settings` files, ignores `--allowedTools`, and hides the "always allow" choices. It is powerful, but it increases prompt fatigue, so rolling it out first to the most regulated teams is the realistic path.

Note also that `permissions.allow` rules in a project's `.claude/settings.json` apply only after someone accepts the workspace trust dialog for that folder. Deny and ask rules aren't affected, since they only restrict.

## 4. Where rule syntax stops: neither Bash rules nor Read denies are a boundary

**Permission rules match the form of command Claude usually writes. They are not a fence around the program itself.** Misread this and you leave holes you think you have closed.

For Bash rules, the docs say plainly that a deny or ask rule "isn't a security boundary around the program." A deny on `Bash(curl *)` doesn't match `/usr/bin/curl https://example.com` or `sh -c 'curl https://example.com'`. And `curl` and `wget` are merely not auto-approved by default; they are not on a built-in blocklist. If you want them stopped, put them in deny, and if the restriction must hold, combine that with the sandbox network allowlist.

Read and Edit denies are similar. They apply to the built-in file tools, to file commands Claude Code recognizes such as `cat`, `head` and `sed`, and to redirection targets like `> file`. They don't apply to `grep -r pattern .`, which reads files without naming them, or to a Python or Node script that opens files itself. A `.claudeignore` file has no effect, so if you rely on one, move its entries into Read deny rules.

Path syntax has its own trap. `/path` is anchored at the settings file's location, not the filesystem root. `Read(/secrets/**)` in user settings blocks `~/.claude/secrets/**`. Write absolute paths as `//path` and home-relative paths as `~/path`.

## 5. The settings hierarchy and how to deliver managed settings

**Managed settings are the top layer: no user setting, project setting or `--settings` value overrides them.** From highest to lowest, the order is managed settings → `--settings` on the command line → `.claude/settings.local.json` → `.claude/settings.json` → `~/.claude/settings.json`. List keys such as `permissions.allow` are merged rather than overridden.

There are four delivery mechanisms.

- **File**: `/Library/Application Support/ClaudeCode/managed-settings.json` on macOS, `/etc/claude-code/managed-settings.json` on Linux and WSL, and `C:\Program Files\ClaudeCode\managed-settings.json` on Windows. The legacy `C:\ProgramData\ClaudeCode\managed-settings.json` is not read. Split files in a `managed-settings.d/` directory next to it are merged in alphabetical order.
- **MDM / OS policy**: the `com.anthropic.claudecode` managed preferences domain on macOS, and a `REG_SZ` or `REG_EXPAND_SZ` value named `Settings` under `HKLM\SOFTWARE\Policies\ClaudeCode` on Windows.
- **Server-managed settings**: delivered from the claude.ai admin console. They require Claude for Teams or Claude for Enterprise, and the Owner or Primary Owner role to edit.
- **HKCU registry**: user-writable, so it doesn't count as an admin source.

The easily missed part is **what happens when you use more than one mechanism**. By default (`managedSourcesBehavior` set to `"first-wins"`), Claude Code uses only the highest-ranked source that contains a policy key and ignores the rest instead of merging them. The ranking is server-managed → MDM → file → HKCU. So you can try out server-managed settings briefly and find that the sandbox policy you were delivering by file has stopped applying. If you need both, consider `managedSourcesBehavior: "merge"`, and use `/status` to see which source is in effect.

Server-managed settings come with two more constraints.

- The docs state that they "operate as a client-side control, not a security boundary. On unmanaged devices, a user doesn't need admin or sudo access to bypass them."
- If a provider variable such as `CLAUDE_CODE_USE_BEDROCK`, or a non-default `ANTHROPIC_BASE_URL`, is exported in the shell, the fetch is skipped. Organizations on Amazon Bedrock, Google Cloud's Agent Platform (formerly Vertex AI) or Microsoft Foundry deliver policy through endpoint management or a self-hosted Claude apps gateway instead.

## 6. Managed-only keys versus keys any file can set

**Keep two kinds of keys apart: keys that are enforced because they sit in managed settings, and keys any file can set, which become non-overridable only once you place them in managed settings.** The Scope line in the settings reference is the authority.

| Kind | Main keys | What it means |
|---|---|---|
| Managed only | `allowManagedPermissionRulesOnly`, `allowManagedHooksOnly`, `allowManagedMcpServersOnly`, `managedMcpServers`, `strictKnownMarketplaces`, `blockedMarketplaces`, `disableSideloadFlags`, `sandbox.network.allowManagedDomainsOnly`, `sandbox.filesystem.allowManagedReadPathsOnly`, `forceRemoteSettingsRefresh` | Locks that have no effect outside managed settings |
| Any file | `permissions.disableBypassPermissionsMode`, `disableAutoMode`, `allowedMcpServers`, `deniedMcpServers`, `enableAllProjectMcpServers`, `sandbox.enabled`, `sandbox.allowUnsandboxedCommands`, `cleanupPeriodDays`, `env` | Can't be overridden when set in managed settings, but also work from lower files |
| Exceptions where a stricter lower value wins | `permissions.blockReadsOutsideWorkingDirectories: true`, `disableClaudeAiConnectors: true` and others | A `true` from any scope applies, even over a managed `false` |

That `allowedMcpServers` is not managed-only is especially easy to get wrong. Unless you also set `allowManagedMcpServersOnly`, users can widen the allowlist in their own settings. The [MCP article](/blog/claude-code-mcp-server-security-third-party-trust-guide) covers this in detail.

## 7. The sandbox: off by default, and ~/.ssh is readable even when on

**The sandbox is off by default. Even when it is on, by default it can read most of the machine, including `~/.ssh` and `~/.aws/credentials`.** For organizational use, put enabling it and blocking those reads into managed settings together.

The sandbox wraps the shell commands Claude runs, and their child processes, with Seatbelt on macOS and bubblewrap on Linux and WSL2. Its defaults are:

- **Writes**: the working directory, a per-user temp directory and directories you've added.
- **Reads**: most of the machine. Close paths off with `sandbox.filesystem.denyRead`; paths in your Read deny rules are merged in as well.
- **Network**: no direct route out. A local proxy checks each host against `allowedDomains`, which starts empty.
- **Environment variables**: inherited from Claude Code, so any secrets there are visible.

Know what runs outside it, too. Built-in tools such as Read, Edit, Write, WebFetch and WebSearch follow permission rules rather than the sandbox (`denyRead` doesn't stop the Read tool). Command hooks, local MCP servers, LSP servers, the status line command and `apiKeyHelper` run with the user's full access.

The docs' configuration for enforcing it is three keys: `enabled: true`, `failIfUnavailable: true` and `allowUnsandboxedCommands: false`. Without the last one, Claude can retry a command that failed inside the sandbox outside it, using `dangerouslyDisableSandbox`. With `failIfUnavailable`, Claude Code exits at startup on native Windows, where the sandbox doesn't run, so either move Windows developers to WSL2 or a container, or deliver a different policy per operating system.

Keep allowed domains narrow. The proxy doesn't terminate or inspect TLS by default, and the docs warn that allowing a broad domain such as `github.com` can create paths for exfiltration, including through domain fronting. Allowing `/var/run/docker.sock` via `allowUnixSockets` effectively hands over access to the host. To keep developers from widening the policy, consider `allowManagedDomainsOnly` and `allowManagedReadPathsOnly`.

## 8. Secrets: where they live and how to hide them

**Think about secrets in three layers: don't let the tools read them, don't pass them to child processes, and don't leave them in plaintext.**

1. **Don't let tools read them**: write both a Read deny (for the built-in tools) and `sandbox.filesystem.denyRead` (for shell commands). If a secret file has to stay usable while its contents stay hidden, `sandbox.credentials` lets you set `deny` or `mask` per file or variable.
2. **Don't pass them on**: with `CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1`, credentials are stripped from the environments of the subprocesses Claude Code starts, such as Bash commands, hooks and stdio MCP servers (GitHub tokens and proxy settings are left in place).
3. **Don't leave them in plaintext**: values in a settings file's `env` block are plaintext and reach every subprocess. Fetch API credentials from a vault on demand with `apiKeyHelper`. Session transcripts are also stored in plaintext under `~/.claude/projects/`, for 30 days by default; shorten that with `cleanupPeriodDays`.

Claude Code's own API keys and tokens are stored in the macOS Keychain when available, and on Linux in a file with mode `0600`.

## 9. Hooks: run only the ones the organization ships

**Hooks run shell commands with the user's full permissions, so whose hooks run is itself something to govern.** With `allowManagedHooksOnly: true` in managed settings, only managed hooks, Agent SDK hooks and hooks from plugins that managed settings force-enable will run; user, project and local hooks are blocked.

Conversely, hooks the organization ships can't be turned off by `disableAllHooks` in a lower scope. A PreToolUse hook returning `"allow"` doesn't override a deny rule, so hooks are safest as reinforcement in the blocking direction.

For auditing configuration changes, use a `ConfigChange` hook. It fires when user, project or local settings files, or skill files, change during a session, and it can log or block the change. It can't block a change to managed files, though, and it doesn't fire at all for changes to server-managed settings, macOS managed preferences or the Windows registry.

## 10. Data handling: training, retention and ZDR

**Under commercial terms (Team, Enterprise, API and third-party platforms), code and prompts sent to Claude Code are not used for training unless the customer chooses to provide them.** When checking against internal policy, cover these points.

- Standard commercial retention is 30 days.
- Transcripts sent with `/feedback` and similar commands are retained for 5 years. Decide how to treat that when they contain work code.
- Zero data retention (ZDR) is for qualified Claude for Enterprise accounts. It is not included in the standard Enterprise plan and cannot be enabled from admin settings; it needs separate enablement by Anthropic and applies only to Anthropic's direct platform.
- Traffic is encrypted with TLS 1.2 or later. The usage metrics Claude Code sends never include code, prompts or file paths, and `DISABLE_TELEMETRY=1` turns them off.

## 11. Observability and audit

**Usage can be exported to your own collector with OpenTelemetry, and administrators can set this for everyone through the `env` block in managed settings.** Set `CLAUDE_CODE_ENABLE_TELEMETRY=1` and the `OTEL_*` exporters. When managed settings name the destination, developer-set destinations are removed at startup, and a repository's settings files can't set the exporter variables at all.

On the privacy side, logging prompt text (`OTEL_LOG_USER_PROMPTS`) is off by default, as is logging tool arguments (`OTEL_LOG_TOOL_DETAILS`). Decide what to record together with your employment and personal-data policies.

On Team and Enterprise, Admins and Owners can see usage in the claude.ai analytics dashboard. For audit logs of settings changes, the docs say they are available through the compliance API or audit log export, and direct you to your Anthropic account team.

## 12. CI: the `-p` trust trap and GitHub Actions

**The most dangerous thing in CI is that `claude -p` never shows the workspace trust dialog.** Without `--bare`, it runs the hooks in the repository's `.claude/settings.json` and connects the servers in its `.mcp.json` without asking, even in a folder you have never trusted. The docs recommend `--bare` for scripted calls and the SDK. The project's `env` block still applies under `--bare`, though, so don't rely on it alone.

The basic shape for a CI job is `--permission-mode dontAsk` plus `--allowedTools` listing exactly the tools it may use, so anything that would prompt is denied.

With GitHub Actions (`anthropics/claude-code-action`), settle these points first.

- **Who can trigger it**: on issue and pull request events, the triggering user must have write access to the repository. Bypassing that with `allowed_non_write_users` is described as "a significant security risk." Bots are rejected unless listed in `allowed_bots`.
- **Configuration on pull requests**: when it runs against a pull request, the action restores configuration paths such as `.claude/`, `.mcp.json` and `CLAUDE.md` from the base branch before starting Claude, so Claude doesn't run with configuration the pull request rewrote.
- **`pull_request_target` and `workflow_run`**: these run with the base repository's secrets, so never check out an untrusted ref into the workspace root before the action.
- **Hidden instructions**: the action strips HTML comments, invisible characters and similar content, but its docs acknowledge that new bypass techniques may emerge. Keep permissions minimal for workflows where Claude reads issues or pull requests written by outsiders.
- **API keys**: never commit them; store them as GitHub Secrets.

## 13. What each setting prevents, and what it doesn't

**No setting is complete on its own.** In a rollout review, use this table to find the gaps.

| Setting | What it prevents | What it doesn't prevent |
|---|---|---|
| `Read(...)` in `permissions.deny` | Reads through the built-in file tools and recognized commands | Reads by scripts or subprocesses that open files themselves |
| `Bash(curl *)` in `permissions.deny` | `curl` in the form Claude usually writes | A full path, `sh -c`, or a different network tool |
| `disableBypassPermissionsMode` | Bypass mode, including `--dangerously-skip-permissions` | Auto mode (that needs `disableAutoMode`) |
| `sandbox.enabled` + `allowUnsandboxedCommands: false` | Shell commands and their children going beyond allowed files and hosts | Hooks, local MCP servers, built-in tools, commands the user types at `!` |
| `sandbox.filesystem.denyRead` | Sandboxed commands reading secret files | The Read tool (needs a Read deny) |
| `sandbox.network.allowedDomains` | Connections to hosts outside the list | Exfiltration through a broadly allowed domain, TLS contents |
| Server-managed settings | Overrides on managed devices | Bypass on unmanaged devices, sessions on third-party providers |
| `allowManagedHooksOnly` | Hooks brought in by individuals or repositories | Defects in the hooks the organization ships |
| `--bare` (CI) | Auto-loading the repository's hooks, skills and `.mcp.json` | The project's `env` block |

## 14. An example baseline managed-settings.json

**The example below is a starting point built only from keys confirmed in the official docs.** Don't ship it as is: change the values to match your package registry and internal MCP servers, and verify it with a pilot group before widening it.

```json
{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(.env.*)",
      "Read(secrets/**)",
      "Read(~/.ssh/**)",
      "Read(~/.aws/**)",
      "Bash(curl *)",
      "Bash(wget *)"
    ],
    "ask": [
      "Bash(git push *)"
    ],
    "disableBypassPermissionsMode": "disable"
  },
  "sandbox": {
    "enabled": true,
    "failIfUnavailable": true,
    "allowUnsandboxedCommands": false,
    "filesystem": {
      "denyRead": ["~/.ssh", "~/.aws"],
      "allowManagedReadPathsOnly": true
    },
    "network": {
      "allowedDomains": ["registry.npmjs.org", "packages.example.internal"],
      "allowManagedDomainsOnly": true
    }
  },
  "allowManagedHooksOnly": true,
  "allowManagedMcpServersOnly": true,
  "allowedMcpServers": [
    { "serverUrl": "https://mcp.example.internal/*" }
  ],
  "env": {
    "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
    "OTEL_METRICS_EXPORTER": "otlp",
    "OTEL_LOGS_EXPORTER": "otlp",
    "OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
    "OTEL_EXPORTER_OTLP_ENDPOINT": "https://otel.example.internal:4317"
  },
  "cleanupPeriodDays": 14
}
```

Some notes on the intent.

- The `Bash(curl *)` deny only stops the usual form. What reliably stops exfiltration is the sandbox's network allowlist.
- `github.com` is deliberately not in the allowed domains, because a broad domain can become an exfiltration path. If git operations need it, either add it knowing the trade-off, or move specific commands out with `excludedCommands` (excluded commands run with full access).
- `disableAutoMode`, which blocks auto mode, is not included. Decide that as a policy matter and add it here if you block it.
- Because of `failIfUnavailable`, deliver a different file to native Windows machines or move those users to WSL2.
- After delivering it, open `/status` on one machine and confirm the `Setting sources` line shows `Enterprise managed settings (file)`.

## 15. A staged rollout: pilot → policy → managed → CI

**Pushing a strict policy to everyone on day one multiplies exception requests and workarounds in personal settings.** Four stages are the realistic path.

1. **Pilot**: with a small team, work mainly in Manual and acceptEdits, and record which commands, domains and MCP servers are actually needed. Review the real permissions regularly with `/permissions`.
2. **Policy**: from the pilot's record, decide what everyone may do, what needs confirmation and what is forbidden. Also decide here whether to allow auto mode, which MCP servers to approve and where secrets live.
3. **Managed settings**: deliver the policy by file or MDM. If you also use server-managed settings, plan around the first-wins behavior and the fact that unmanaged devices can bypass them. Watch that it is in effect with `ConfigChange` hooks and OpenTelemetry.
4. **CI**: finally, lock down unattended runs with `--bare`, `dontAsk`, `--allowedTools` and the Action's trigger rules.

---

## Summary: governing the tool and reviewing what it built are different jobs

Rolling Claude Code out to an organization means deciding **how far an agent running with the developer's permissions may go**. Write the denies first, put them in managed settings so they can't be overridden, and cover what permission rules can't reach with the sandbox and device management. Above all, don't treat as a boundary anything the docs themselves say is not one.

This is about using the tool safely. Finding vulnerabilities in the app you built with Claude Code is a separate job; if you need the app's authorization or RLS checked, see [Security reviews for AI-generated code](/security-audit/ai-generated-code).

If you want outside help turning your policy into managed settings, sandbox configuration, an MCP allowlist and CI settings, I take enquiries at [Claude Code security governance for organizations](/security-audit/claude-code).
