Security for startups

Claude Code and SOC 2: The Evidence Your Auditor Wants

Engineers run Claude Code and the auditor wants proof. The managed settings that work as controls, the SOC 2 criteria they support, and the evidence to give.

In this post
  1. Can engineers use Claude Code at a SOC 2 Type II company?
  2. The control map
  3. What makes a Claude Code setting a control?
  4. What audit trail does Claude Code produce?
  5. Is every MCP server a vendor?
  6. What Claude Code won’t cover
  7. What I’d hand the auditor

TL;DR: Yes, your engineers can use Claude Code at a SOC 2 Type II company, as long as you treat a setting as capability and not as a control. A setting becomes a control when it is enforced, so the developer can’t override it, and when it produces evidence an auditor can sample. Claude Code gives you the enforcement (managed settings, MCP allowlists, a hooks lock) and the evidence (OpenTelemetry events, a per-machine /status readout). It does not give you change management, credential storage, or offboarding. This post is the control map: setting, criterion, artifact.

Can engineers use Claude Code at a SOC 2 Type II company?

Yes, under a handful of controls you have to turn on and prove yourself. An auditor or an enterprise customer will put it as “your engineers use Claude Code, show me the controls,” and the answer has two halves: what is enforced, and what evidence it leaves. A repo-level .claude/settings.json that a developer can edit locally is a suggestion. A managed policy the developer can’t override, plus a log that shows it working, is a control.

I haven’t put coding agents in front of an auditor or an enterprise security review, and I’m not going to pretend otherwise. What follows is what I’d sign off on as a security lead, reasoning from the criteria and from Anthropic’s own documentation. As a former CISO, I know an audit is a sampling exercise, so every row below ends in an artifact you could hand over. I wrote the same kind of control map for Omarchy on a company laptop.

The control map

Risk / criterion Claude Code setting (enforced) Evidence the auditor samples
Agent acting without authorization (CC6.1 / CC6.3) Managed permissions.deny and ask rules, plus allowManagedPermissionRulesOnly: true The managed-settings file with its change history, plus /status output per machine
Bypassing approvals (CC6.1) permissions.disableBypassPermissionsMode and the separate disableAutoMode The same file, plus tool_decision events showing where approvals came from
Unvetted third-party code: MCP servers (CC6.1, vendor risk) allowManagedMcpServersOnly with allowedMcpServers, and deniedMcpServers (v2.1.273+) An MCP allowlist with an approver and a date per server
Tampered guardrails (CC6.1) allowManagedHooksOnly Managed hook definitions under change control
Monitoring and anomaly detection (CC7.2 / CC7.3) Telemetry enabled through managed env, destination locked tool_decision and tool_result events in your SIEM, plus an alert rule
Change management for agent-written code (CC8.1) Nothing in Claude Code. It is a repo control PR records showing a human approved agent-authored changes
Network egress (CC6.6 / CC6.7) sandbox.network.allowManagedDomainsOnly plus allowed domains The managed domain allowlist
Scope gap: cloud sessions Server-managed settings A written statement of which surfaces the policy covers

The criterion column is my reading of the 2017 Trust Services Criteria (AICPA, TSP Section 100), not an auditor’s sign-off. Confirm the mappings with yours, starting with the CC6.6 / CC6.7 egress row.

What makes a Claude Code setting a control?

A setting is a control when a lower-ranked source can’t override it. Claude Code applies managed settings above every other level, so no user, project, local, or --settings value overrides them. The documented precedence, highest first, is managed settings, then --settings on the command line, then .claude/settings.local.json, then .claude/settings.json, then ~/.claude/settings.json (settings docs). The exceptions are a few security-sensitive keys where a stricter lower value still counts, which only helps you.

That ladder is why a checked-in .claude/settings.json proves nothing: settings.local.json outranks it. Only the top rung can’t be overridden.

Managed settings arrive three main ways: a managed-settings.json file, an MDM policy, or server-managed settings from the claude.ai console (managed settings). The file lives in a system directory: /Library/Application Support/ClaudeCode/ on macOS, /etc/claude-code/ on Linux and WSL, and C:\Program Files\ClaudeCode\ on Windows. Here is the docs’ own example shape, trimmed. It is illustrative, an example policy and not a recommendation for yours:

{
  "permissions": {
    "deny": [
      "Read(./.env)",
      "Read(./secrets/**)"
    ],
    "disableBypassPermissionsMode": "disable"
  },
  "allowManagedPermissionRulesOnly": true
}

Read that as an auditor would: two blocked reads, bypass mode off, and allow rules limited to the managed source, so a developer can’t grant themselves a permission the policy didn’t.

The per-machine proof

Claude Code gives you one. /status shows a Setting sources line, for example Enterprise managed settings (file), and claude doctor lists what it dropped (managed settings). A screenshot or captured output of /status per machine is the cheapest evidence artifact in this whole post. Pair it with the policy file in version control, with its change history, and you’ve covered “what was the rule, who changed it, and was it applied on this laptop.”

Two locks, not one

The bypass row names two keys because they are two locks. permissions.disableBypassPermissionsMode blocks bypassPermissions mode and not auto mode; blocking auto mode takes the separate disableAutoMode key (settings reference). If your policy says “no approval-skipping modes” and you set only one key, the policy and the machine disagree, which is exactly what an auditor is paid to find.

Also, permissions.defaultMode values auto and bypassPermissions don’t take effect from project or local settings; they have to come from user or managed settings (settings docs). I covered the human side of skipping approvals in the Omarchy launcher post, and the guardrail side in four Claude Code hooks. The tie to the table: allowManagedHooksOnly runs “only the hooks your organization deploys” (settings reference). That matters because a hook can approve a tool call, and the hook source on tool_decision, below, records exactly that. It is a lock, so a looser local value doesn’t win.

What audit trail does Claude Code produce?

Claude Code emits OpenTelemetry events you can route to your own collector, and one of them answers the question auditors care about most. Turn it on with CLAUDE_CODE_ENABLE_TELEMETRY=1, set OTEL_METRICS_EXPORTER and OTEL_LOGS_EXPORTER to otlp, and point it at an OTLP endpoint (monitoring usage). The events that matter here include claude_code.user_prompt, assistant_response, tool_result, tool_decision, api_request, api_error, and api_refusal. Every event carries session.id, user.id, user.email when available, organization.id, prompt.id, and event.sequence, among others.

That’s the documented stream, not a collector I’ve run.

The strongest single artifact is tool_decision. It carries tool_name, tool_use_id, a decision of accept or reject, and a source (monitoring usage). The documented source values are config, hook, user_permanent, user_temporary, user_abort, and user_reject. It tells you, per tool call, whether a policy rule allowed it, a hook allowed it, or a person approved it, and for how long. “Was this access authorized” becomes a query instead of an interview.

CC6.1 and CC6.3 are about who is authorized to act, and a per-call decision with an actor and a source is that record. CC7.2 and CC7.3 are about detecting anomalies and evaluating whether they are security incidents, which is what an alert rule over these events does; I walked through that wording in the LLM audit trail post and won’t re-quote it. An example detection, labeled as one: alert when user_permanent approvals of the Bash tool appear, or when reject decisions spike. Those are my picks for a starting rule, not anything the docs prescribe.

Redaction, and the lock that makes it a control

Prompt and response content is redacted by default. Logging it takes explicit flags: OTEL_LOG_USER_PROMPTS, OTEL_LOG_ASSISTANT_RESPONSES, OTEL_LOG_TOOL_DETAILS, and OTEL_LOG_TOOL_CONTENT (monitoring usage). Whether you flip any of them is a retention and PII decision, and the audit-trail post owns that reasoning. Default-redacted events with identity and decision metadata are enough for most of the table.

The part that turns telemetry from capability into control: admins can enforce it through the managed settings env block. When you set an OTEL_EXPORTER_OTLP_* variable there, Claude Code “removes conflicting developer-set variables at startup” (monitoring usage), and setting OTEL_EXPORTER_OTLP_HEADERS also strips the developer-set endpoints, so those credentials can’t reach a collector you didn’t choose. Set OTEL_METRICS_EXPORTER and OTEL_LOGS_EXPORTER in the same managed env block too, or a developer can still switch a signal off. Without all of that, your audit trail is optional. With it, the destination is yours.

Is every MCP server a vendor?

Treat it as one. An MCP server is third-party code with access to tools and data, so the vendor-management instinct applies: approved list, named approver, date. Claude Code gives you the lock. allowManagedMcpServersOnly together with allowedMcpServers is an allowlist the developer can’t extend, and deniedMcpServers plus disableClaudeAiConnectors apply from any admin source (managed settings). Their any-admin-source behavior requires Claude Code v2.1.273 or later, and since docs change, re-check that floor before you write it into a policy.

The evidence is a list, not a log: each approved server, who approved it, when, and why. The field-level format for that allow-list is in the agent evidence pack post. If you’re building your own servers, the Phoenix MCP server walkthrough is the other side of the same list. Note that lists like deniedMcpServers combine across sources, while the allowlist lock does not widen from below. That difference is worth a sentence in your policy, because it decides who can add an entry.

What Claude Code won’t cover

Four gaps, and the first one is the biggest.

Change management (CC8.1). The agent writes code, and code flows through change control. I’d map CC8.1 to the repo, not the tool: branch protection, required human review, and PR records showing a person approved each agent-authored change. My own rule is that agents never commit unsupervised, which I laid out in my daily agentic workflow. The artifact is the PR history.

Cloud sessions. The managed-settings page is explicit that a managed-settings.json file or MDM profile on your device doesn’t reach a cloud session; server-managed settings do (managed settings). So your scope statement has to say which surfaces the policy covers. An auditor who finds an unmanaged surface you didn’t disclose writes a finding. One you disclosed and bounded is a scoped control.

The WSL path. The docs note that on WSL, /etc/claude-code is user-writable as well (managed settings). A file a developer can edit isn’t managed in any sense an auditor will accept, so either keep WSL out of scope in writing or deliver settings another way.

Credentials and offboarding. Nothing in the settings above stores credentials per developer or revokes a departing engineer’s access. That’s your identity provider and your secrets process; I wrote about the agent side in AI agent secrets management. Write the policy, because the tool won’t.

One boundary the table can’t show: a vendor’s SOC 2 report describes the vendor’s controls, not yours, so Anthropic’s assurance stands in for no row above.

What I’d hand the auditor

The pack is short. A managed-settings file in version control with change history. /status captured per machine. An MCP allowlist with approvers and dates. A sample of tool_decision events and the alert rule that watches them. PR review records for agent-authored changes. A one-page scope statement naming the surfaces, the WSL position, and the gaps you’ve closed in policy instead of in software. The framing underneath it is the one from why SOC 2 is a revenue tool and not a security tool: the point is evidence a buyer accepts, and the shortest path there is controls that leave their own paper trail.

If you’re a founder or CTO who has an engineering team on Claude Code, an enterprise deal waiting on a security questionnaire, and no one who has mapped the two together, that mapping is the work I do as a vCISO. Talk to me about a consulting engagement, and bring the questionnaire. I’d rather find the gaps before your customer’s auditor does.