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
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
/statusreadout). 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.