Securing Agentic Coding

Definition

The control set for the generative-coding deployment shape, mapped to the six planes of the AAI-S RA and the nine domains of the AAI-S CMM.

Every configuration key and control name on this page belongs to Claude Code. The catalog is single-harness-deep by choice: Anthropic documents Claude Code’s security model in public at a level its competitors do not match, so it is the only harness where a control can be named, graded, and checked rather than inferred. The structure transfers to Cursor, Codex, and the open-source harnesses; the keys do not, and an organization running one of those must locate each equivalent and will sometimes find it absent. Read a key below as an instance of its control, not as a portable instruction.

The organizing claim is a ranking. Controls that constrain the process outrank controls that inspect a string, and both outrank controls that instruct the model. The GuardFall audit is the empirical basis: ten of eleven surveyed harnesses had string-inspecting guards that could be walked past with shell syntax older than the tools themselves.

Applicability

Whenever an agentic coding harness reads a repository. Which controls are load-bearing depends on which of the five shapes in Generative Coding Deployment Shapes is in play; read that page first if the deployment is not obviously interactive.

The Control Catalog

Reading guide

Control states the security property in vendor-neutral terms, so the row survives a change of harness.

Instrument names the thing that implements it. Instruments are of four kinds, distinguished by how a practitioner touches them: a settings key, given in dotted JSON-path form and set in a settings file; a session surface, such as the /sandbox slash command; a package or product, installed or purchased; or a practice with no product behind it. Keys are written once in full and then by their last segment. Minimum Claude Code versions appear where a key has one.

Grade is availability as of July 2026.

RA capability → CMM anchors the row to the AAI-S RA capability it implements and the CMM domain that scores it. The rows in this catalog are not restatements of the RA’s capability rows. The RA lists technology-neutral capabilities for agentic systems in general; this catalog lists controls for one deployment shape, at the granularity of the person configuring a harness. Some rows map onto an RA capability exactly, some map onto one at a coarser granularity, and nine map onto no RA row at all. Those nine carry and are analyzed in Controls with no RA capability.

Settings-key locations

Every settings.json key below resolves through five scopes, highest wins: managed, then command-line arguments, then the repository’s local override, then the repository’s checked-in settings, then the user’s own. Managed settings arrive two ways and do not merge with each other — a cached server-managed policy replaces the device-managed file outright:

DeliveryLocationReaches
Device-managed, deployed by MDM/Library/Application Support/ClaudeCode/ (macOS), /etc/claude-code/ (Linux and WSL), C:\Program Files\ClaudeCode\ (Windows) — managed-settings.json plus managed-settings.d/*.json merged alphabeticallyAny device the MDM enrolls, before first login
Server-managed, deployed from claude.aiDelivered to the authenticated sessionOnly accounts already authenticated into the organization

The distinction that decides whether a managed key is authoritative: boolean keys take the managed value and ignore local settings; array keys merge entries from every scope. So sandbox.enabled and failIfUnavailable cannot be relaxed locally, while excludedCommands and allowRead can be appended to by any developer. allowManagedReadPathsOnly and allowManagedDomainsOnly restore managed-only semantics for allowRead and for domains respectively. excludedCommands has no such lockdown.

Identity plane

ControlInstrumentGradeRA capability → CMM
Session pinned to a known organizationforceLoginMethod + forceLoginOrgUUID in managed settingsFirst-party, GA; full enforcement v2.1.212+— → D2
Federated identity behind that organizationSSO — Claude for Enterprise on the claude.ai side, or Claude Console SSO for API-billed organizations. Not available on Claude for TeamsCOTS, GA, plan-gatedAgent identity & lifecycle → D2
Per-workflow credential scopingOne token per environment and workflow, minimum permissionPractice; no productNHI governance → D2
Credential held outside the agent boundaryVendor credential proxy in the delegated-cloud shape; credential proxy elsewhereFirst-party for cloud; assembled elsewhereCredential proxy → D2
Attribution of an action to an agent and a humanEndor Labs AI Code GovernanceCOTS; Endor Labs is the only sourced vendorAction-to-identity tracing → D2

The organization pin covers fewer login paths than it appears to. Terminal, IDE-extension, and SDK logins are held. The two token-minting commands check only the login method, not the organization, so both can produce a credential in a different tenant. Gateway sign-in never authenticates against an Anthropic organization at all, which makes the gateway’s own identity provider the control. Bedrock, Vertex, and Foundry sessions authenticate against the cloud provider and are not blocked, so cloud IAM policy is the control there. Deploy the pin through device management: server-managed settings only reach accounts already inside the organization, and so cannot govern a first login.

The attribution row is the weakest link in the plane. An organization that cannot separate agent-authored from human-authored change cannot scope a review policy or trace a defect to the tool that introduced it, and no first-party feature covers it across harness vendors.

Control plane

ControlInstrumentGradeRA capability → CMM
Managed permission policy that local settings cannot overridemanaged-settings.json by MDM, or server-managed settingsFirst-party, GAPolicy engine, degenerate case → D3
Deny rules that survive autonomous modespermissions.deny; explicit deny is honored even under sandbox auto-allow and --dangerously-skip-permissionsFirst-party, GALeast-agency tier engine, block tier → D3
Content-scoped prompts that autonomy cannot suppresspermissions.ask with an argument pattern — Bash(git push *), Bash(dangerouslyDisableSandbox:true)First-party, GALeast-agency tier engine, confirm tier → D3
Lock developers out of widening policyallowManagedReadPathsOnly, allowManagedDomainsOnlyFirst-party, GA— → D3
Deterministic per-action reference monitorCedar-routed lifecycle hooks — see MaiselPractitioner prototypePolicy language / PDP engine → D3
Audit or block in-session settings changesConfigChange hooksFirst-party, GA— → D3

The permissions.ask row survives contact with an autonomous mode, and its behavior is specific enough to test. A bare Bash or Bash(*) ask rule is skipped for any command that runs sandboxed, so an organization that wrote one and then enabled the sandbox has no prompt left. An ask rule scoped to an argument pattern still fires. An ask rule on Bash(dangerouslyDisableSandbox:true) catches the escape hatch in flight.

The distinction that matters for scoring is the merge semantics described above: excludedCommands merges across scopes and has no managed-only lockdown, so a developer can always append entries that run commands outside the sandbox. Keep the managed list narrow and treat it as a reviewed artifact.

Runtime plane

ControlInstrumentGradeRA capability → CMM
OS isolation of shell subprocessessandbox.enabled, configured through the /sandbox session surface — Seatbelt on macOS, bubblewrap plus socat on Linux and WSL2, optional seccomp for Unix-socket blockingFirst-party, GA; no native Windows supportSandbox / containment → D4
OS isolation of the whole session, including MCP servers and hookssandbox-runtime`FOSS, beta research previewSandbox / containment → D4
Container isolationPublished dev container with default-deny iptables firewall; runs Claude Code as a non-root userFOSS reference configSandbox / containment → D4
Kernel-level isolationVM, microVM (Firecracker), vendor-managed cloud VMGA, varies by providerSandbox / containment → D4
Write confinement inside the sandboxallowWrite, denyWrite; default is the working directory and session temp directory onlyFirst-party, GASandbox / containment → D4
Read confinement inside the sandboxdenyRead, re-opened selectively by allowRead; exact deny beats wider allowFirst-party, GASandbox / containment → D4
Authoring-time dangerous-pattern warningSecurity Guidance plugin — pre-tool hook on Write, Edit, MultiEditFirst-party, free, warns onlyCode-output static analysis → D4
Hard failure when isolation is unavailablefailIfUnavailable, allowUnsandboxedCommandsFirst-party, GA— → D4

Four configuration facts an assessor should verify rather than assume.

Sandbox read access defaults to the whole filesystem minus explicit denies, so ~/.aws and ~/.ssh remain readable until sandbox.credentials or denyRead entries exist. The escape hatch is a tool parameter, dangerouslyDisableSandbox, which the model may set to retry a command that failed under isolation; setting allowUnsandboxedCommands makes the parameter ignored entirely, which the /sandbox panel labels strict sandbox mode. The built-in sandbox covers Bash and its child processes only — Read, Edit, and Write run under the permission system instead, which is how the Microsoft finding reached /proc/self/environ while the Bash boundary held.

The fourth is newer than the rest of this catalog and inverts a control an assessor may already have scored. filesystem.disabled (v2.1.216 and later) turns the filesystem layer off while leaving network isolation on. Setting it also disables the sandbox.credentials.files read protections, the denyRead rules, and the automatic write-deny on settings.json at every scope, while sandboxed commands stay auto-allowed. The vendor documentation flags that combination as self-escalating: a command can write a shell startup file or settings.json that widens its own access on the next run. The key is honored from user, managed, and --settings sources but not from project or local settings, and managed settings can reserve it exclusively by configuring filesystem or listing any sandbox.credentials.files entry. Verify the resolved value rather than the presence of sandbox.enabled.

Egress plane

ControlInstrumentGradeRA capability → CMM
Domain allowlist for agent subprocessesallowedDomains, deniedDomains; no domain is pre-allowed, and WebFetch(domain:...) allow rules pre-allow as wellFirst-party, GAEgress destination filtering → D5
Deny rather than prompt outside the allowliststrictAllowlist (v2.1.219+, ignored from project and local settings); allowManagedDomainsOnly for the managed-only variantFirst-party, GAEgress destination filtering → D5
TLS-terminating inspectiontlsTerminate (v2.1.199+); custom proxy via httpProxyPort with its CA installed inside the sandboxExperimental first-party; custom proxy supported— → D5
Credential masking on outbound requestssandbox.credentials.envVars with mode: mask and injectHosts (v2.1.199+); the command sees a per-session sentinel and the proxy substitutes the real valueFirst-party, GA; fails closed without TLS terminationCredential proxy, inverted → D5
Corporate proxy for all trafficHTTPS_PROXY / HTTP_PROXYFirst-party, GAMCP / A2A / LLM proxy → D5

Masking is the row most likely to be configured into a false sense of coverage. It is honored only from user, managed, and --settings sources — mask entries in a repository’s settings.json are ignored — and every injectHosts entry must itself be covered by allowedDomains. Without tlsTerminate the sentinel reaches the server unchanged and authentication fails rather than leaking, which is the right failure direction but means the control is either working or visibly broken, never silently partial.

The default proxy makes its allow decision from the client-supplied hostname without inspecting TLS. The vendor documentation states the consequence directly: a broad allowlist entry such as github.com is reachable by domain fronting. A hostname allowlist without TLS termination is a misconfiguration control, not an exfiltration control, and it should be graded that way in D5.

Data plane

ControlInstrumentGradeRA capability → CMM
Credential file and environment-variable denialsandbox.credentials.files and sandbox.credentials.envVars with mode: deny (v2.1.187+); files are blocked for reads, variables are unset before each sandboxed commandFirst-party, GA; sandboxed Bash only— → D6
Subprocess credential scrubbing regardless of sandboxingCLAUDE_CODE_SUBPROCESS_ENV_SCRUB — strips Anthropic and cloud-provider credentials from every subprocess, and forces filesystem isolation to stay onFirst-party, GA— → D6
Self-modification protectionSandbox denies writes to settings.json at every scope and to the managed settings directory, resolving symlinks (v2.1.210+)First-party, GA; lost if filesystem.disabledCognitive file integrity → D6
Instruction-file integrityCognitive file integrity over CLAUDE.md, skills, subagentsPractice; partial product coverageCognitive file integrity → D6
Harness-configuration auditAgentShield; Endor LabsFOSS single instrument; COTS single vendorSupply-chain scanning → D8
Untrusted-input exclusion in CIDisable fork-PR execution; exclude issue and comment bodiesPractice— → D6

There is no built-in credential deny list. Only the paths and variables explicitly listed are restricted, which makes this the single most commonly skipped configuration step in the catalog. The two rows above it divide along a boundary worth stating: sandbox.credentials protects sandboxed Bash commands and nothing else, while CLAUDE_CODE_SUBPROCESS_ENV_SCRUB reaches every subprocess whether sandboxed or not. An organization that configured only the first has left MCP servers and hooks holding the credential.

Observability plane

ControlInstrumentGradeRA capability → CMM
Session telemetry to a SIEMOpenTelemetry export — CLAUDE_CODE_ENABLE_TELEMETRY=1 plus OTEL_LOGS_EXPORTER, emitting one claude_code.* event per prompt, tool decision, tool result, permission-mode change, MCP connection, and login, correlated by session and prompt IDFirst-party, GASIEM / SOAR with agent playbooks → D7
Security-relevant content inside that telemetrythe four OTEL_LOG_* gates — tool details (shell command strings, MCP tool names, skill names), user prompts, assistant responses, raw API bodiesFirst-party, GA, off by defaultOTel gen_ai.* SemConv → D7
Per-user usage and contribution analyticsClaude Enterprise Analytics API (read:analytics key, Enterprise only) or Claude Code Analytics API (Admin API key, Console organizations)GA, plan-split; not available on Claude for Teams— → D7
Fleet inventory of harnesses, MCP servers, skills, hooksEndor Labs AI Code GovernanceCOTS; Endor Labs is the only sourced vendorAI-SPM / runtime AI-BOM → D7
Pull-request security review as a gateclaude-code-security-review GitHub ActionFOSS, first-partyCode-output static analysis → D8

Content redaction is the default, and it is the finding that separates the appearance of agent audit from the fact of it. An organization that enables telemetry and stops there receives token counts, costs, durations, permission-mode transitions, and MCP connection status — and no shell command strings, no prompt text, no tool arguments. That stream cannot answer what an agent ran, which makes it a usage feed rather than a security feed. Turning it into a security feed means deciding to export prompt and tool content, with the data-handling consequences that decision carries.

The two analytics APIs are different products with different scopes, and neither substitutes for the telemetry stream. Contribution metrics are Teams and Enterprise only, in public beta, dependent on the GitHub app, unavailable to organizations running Zero Data Retention, and documented as excluding Claude Console API usage and third-party integrations. The Console dashboard covers Console-billed API usage and shows spend rather than contribution. Sessions routed through Amazon Bedrock, Google Cloud’s Agent Platform, Microsoft Foundry, or a self-hosted gateway fall outside both.

Correcting an earlier reading of the gateway regression

OpenTelemetry export is not disabled under Bedrock, Vertex, or Foundry — only the claude_code.internal_error event is suppressed there. The D7 regression an inference gateway causes is therefore narrower than a total loss of visibility: it removes the first-party analytics dashboards, and OpenTelemetry remains available as the rebuild path. Step 8 of the deployment order below depends on this.

Controls with no RA capability

Nine rows anchor to a CMM domain but to no RA capability. They fall into three groups, and the groups have different implications.

Properties of a control rather than controls. failIfUnavailable and allowUnsandboxedCommands, allowManagedReadPathsOnly and allowManagedDomainsOnly, and ConfigChange auditing do not add a capability. They determine whether an existing capability fails closed, whether a local scope can widen it, and whether a change to it is visible. The RA’s capability vocabulary has no slot for these, and the omission is not specific to generative coding: every plane has controls that can be silently disabled locally or that degrade to a warning when a dependency is missing.

The RA has no representation for fail-closed and lockdown properties

A capability row records that a control exists, not that it holds when its dependency is absent or that a lower scope cannot relax it. This affects the whole RA rather than this shape. Resolving it means either a per-capability property column or a cross-cutting section, and the choice needs to be made against the RA as a whole, not decided here.

Process-environment hygiene. sandbox.credentials deny entries, CLAUDE_CODE_SUBPROCESS_ENV_SCRUB, and per-workflow token scoping cover the case where a credential is already inside the agent’s process. The RA’s Identity plane assumes the credential-proxy pattern, where the credential never enters the process at all, and so has no row for containing one that has. For a local coding harness inheriting a developer’s shell environment, the proxy assumption does not hold, and the hygiene controls are what remains.

Genuinely shape-specific. Pinning a session to a known Anthropic organization, excluding untrusted input from CI triggers, and the first-party analytics APIs are artifacts of how this particular product is licensed, triggered, and instrumented. They belong in a catalog and not in a reference architecture.

Method

A deployment order that follows the control ranking rather than the plane order:

  1. Establish the isolation boundary before reducing prompts. Enable the sandbox with failIfUnavailable and allowUnsandboxedCommands through managed settings, and only then allow autonomous modes. The reverse order — suppressing prompts first and hardening later — is the sequence that produces incidents.
  2. Close the read side. Add sandbox.credentials deny entries for ~/.aws, ~/.ssh, and the secret environment variables in use. Nothing is denied by default.
  3. Match the boundary to the shape. Bash-only sandboxing is insufficient for unattended runs; wrap the whole process (sandbox runtime, container, or VM) wherever prompts are suppressed.
  4. Apply the Rule of Two to every non-interactive workflow. For a CI-triggered agent, remove untrusted input, or remove secrets from the process environment, or remove push and egress. Choose one explicitly and record which.
  5. Lock the network before widening it. strictAllowlist plus managed-only domains; terminate TLS if the threat model includes exfiltration rather than misconfiguration.
  6. Put the harness configuration under source control and scan it. The config tree is a supply-chain artifact; hooks, MCP manifests, and CLAUDE.md belong in review and in CI.
  7. Gate on the pull request, not on the keystroke. The authoring-time plugin warns; only the CI review action can hold a merge.
  8. Export telemetry before the fleet grows. Session-correlated OpenTelemetry to the SIEM, and an inventory mechanism that survives more than one harness vendor.

Mechanism

Each step in the ordering removes a dependency on a weaker layer. OS enforcement holds regardless of how a command was spelled, which is the closure the guard canonicalization gap demands. Removing a Rule-of-Two property removes the precondition for the attack chain rather than detecting its execution. Scanning the configuration tree catches the persistence mechanism — an injected hook survives the session that installed it, which is what made CVE-2026-25725 a sandbox escape rather than a session compromise.

Limits

The single-harness scope stated at the top has a sharper edge than convenience. GuardFall’s finding suggests the open-source equivalents are weaker rather than merely different, so an organization that maps this catalog onto another harness should treat a missing key as a missing control until it has evidence otherwise, not as a naming difference.

Several load-bearing instruments are single-sourced. Sandbox runtime is a beta research preview. The COTS control plane in the attribution, harness-audit, and fleet-inventory rows is Endor Labs AI Code Governance in all three cases — one vendor’s product material, with no dated GA announcement and no independent evaluation. AgentShield is one open-source scanner. No competing implementation of the fleet-inventory capability is sourced here, so that row records a market gap as much as a control.

Nothing here addresses code quality. Every control in the catalog governs what the agent may do. Whether the code it writes is correct is a separate problem, addressed by review capacity that the throughput data suggests is already the binding constraint.

Promotion Path

If a standards body publishes a control set for agentic software development — the natural candidates being an SSDF extension or an OWASP agentic-coding guide — this page becomes a crosswalk to it rather than a standalone catalog.