Skip to content

Security standards and compliance for coding agents

A security baseline for AI coding agents is a short list of enforced controls: a contracted data boundary, sign-in pinned to the company organization, a managed policy floor, deny rules for secrets, telemetry to a SIEM, vetted extensions, required CI gates, and human approval of sensitive changes. Auditors for SOC 2, GDPR and HIPAA accept it when each control produces evidence and a scheduled test shows it still holds.

Your security team paused the agent rollout with three reasonable questions: where does our source code go, who ran what, and how do we know agent-written code was checked before it shipped? The SOC 2 Type II window opens next quarter. Meanwhile three teams already run Claude Code, Codex and Cursor on their own settings, and nobody can say which MCP servers their laptops trust.

This page gives you the baseline and the rollout order. Each control links to its in-depth page, so the table goes to the auditor and the links go to the engineers.

  • An eight-control baseline table with the enforcing mechanism in each tool and the evidence each one produces.
  • A mapping from SOC 2, GDPR and HIPAA requirements to those controls, ready for your control matrix.
  • A minimal managed policy for Claude Code and Codex, and the questions to close in Cursor’s admin settings.
  • A CI gate that blocks destructive schema changes an agent writes without a named human accepting the risk.
  • Control tests you can schedule, so compliance is a passing check rather than a quarterly screenshot hunt.
  • Three copy-paste prompts: a gap analysis, a SOC 2 change-management evidence pull, and an exposure triage.

What does an auditor ask about coding agents?

Section titled “What does an auditor ask about coding agents?”

Auditors do not need a new framework for agents. They ask the questions they already ask about any system that reads source code and changes production, and each question maps to controls you can show:

Auditor’s questionControls that answer it (table below)
Where does our code and data go, and is it kept or trained on?1 Data boundary, 3 Secret and regulated-data exclusion
Who used the agent, and under which account?2 Identity
What was the agent allowed to do, and can a developer widen that?4 Policy floor
What did it actually do?5 Telemetry
Which third-party code runs inside the agent?6 Extensions
How was the change checked and approved before release?7 CI gates, 8 Human approval

The threats behind these questions (prompt injection, exfiltration, over-broad credentials) are catalogued in the agent threat model. This page stays on the controls and the evidence.

Adopt the table as written, then replace the “Deep dive” column with your own control owners. Versions are those checked on 2026-09-26: Claude Code 2.1.283 and Codex CLI 0.157.1. Cursor’s documentation was unreachable that day, so its cells say what to confirm rather than what to paste.

#ControlClaude CodeCodexCursorEvidenceDeep dive
1Data boundary: contracted retention and no trainingCommercial terms (no training on Claude Code data); ZDR on qualified Claude for Enterprise orgsYour ChatGPT Enterprise or API agreementPrivacy Mode, enforced at team level (confirm the tier)Signed DPA, ZDR or BAA letter, vendor SOC 2 reportProcurement, model hosting
2Identity: only company accountsforceLoginMethod, forceLoginOrgUUIDallowed_login_methods, allowed_chatgpt_workspacesSSO enforced in the admin dashboardIdP export, managed settings file in gitAgent identity and secrets
3Secret and regulated-data exclusionpermissions.deny on Read(...) paths, plus secrets kept out of the working treeKeep secrets out of the workspace; inject at run time.cursorignoreDenials in telemetry, control test resultData privacy
4Policy floor nobody can lowermanaged-settings.json with permissions.disableBypassPermissionsModerequirements.toml with allowed_sandbox_modes, allowed_approval_policiesAdmin settings for run modes (confirm what can be locked)Versioned policy repo; Claude Code /status, Codex /debug-config outputManaged policy
5Telemetry to your SIEMOpenTelemetry through env in managed settings[otel] in a distributed config.tomlAdmin dashboard and API (confirm audit log scope)Collector ingest per active userAgent observability
6Vetted extensions (MCP, skills, plugins)allowedMcpServers + allowManagedMcpServersOnly; strictKnownMarketplaces[mcp_servers.<name>.identity] in requirements.tomlConfirm whether MCP can be limited to an approved listServer register with scan resultsMCP security
7Required CI gatesTool-independent: secret scan, SAST, dependency scan, schema-change gateSameSameRequired-check history on every merged PRSecurity gates
8Human approval of sensitive changesTool-independent: CODEOWNERS plus a ruleset with no bypass actors; an agent never approvesSameSamePR review recordsAgent PR review, regulated industries

Controls 7 and 8 run on the repository, not the laptop, so they are identical across tools and carry the most audit weight: a developer can uninstall a tool but cannot merge around a required check.

Copy these rows into your control matrix. The criterion numbers are the ones auditors cite; the evidence column says what to hand over.

Framework and requirementBaseline controlsEvidence to hand over
SOC 2 CC6.1 logical access2, 4SSO and SCIM configuration, org-pinned login in the managed policy
SOC 2 CC7.2 monitoring of system components5Agent telemetry retained in the SIEM, with denied tool calls
SOC 2 CC8.1 change management7, 8Merged PRs with passing required checks and a human approval
SOC 2 CC9.2 vendor and business-partner risk1, 6Vendor SOC 2 reports, DPAs, the MCP and plugin register
GDPR Art. 5(1)(c) data minimisation, Art. 25 data protection by design3Deny rules and ignore files for personal-data paths; synthetic test data
GDPR Art. 28 processors1, 6A DPA with each vendor, including every hosted MCP server that receives personal data
GDPR Art. 32 security of processing, Art. 33 breach notification4, 5Policy floor, telemetry, and an incident procedure that decides on notification within 72 hours
HIPAA business associate agreement (45 CFR 164.502(e))1A BAA that names the exact product surface that touches PHI
HIPAA audit controls (45 CFR 164.312(b))5Telemetry records of agent activity on systems that hold PHI

For ISO/IEC 42001 and the NIST AI RMF, and for the full control-to-evidence table built on the evidence bundle, see AI management standards.

The order matters. Contracts first, because no setting fixes data that already left under the wrong terms. The repository gates come before the laptop hardening is perfect, because they catch what every local control misses.

  1. Close the data boundary in the contract. Read what each vendor retains before you configure anything. For Claude Code, Anthropic’s data usage page states that it does not train on code or prompts under commercial terms, and that the standard commercial retention is 30 days. Zero Data Retention (ZDR) is enabled per organization by Anthropic’s account team on qualified Claude for Enterprise accounts; a new organization is not covered until it is enabled separately. Four details belong in your data-flow record:

    • ZDR covers inference only. Data that MCP servers and other integrations process is outside it, so each hosted server with access to regulated data needs its own agreement.
    • ZDR disables features that store sessions, including cloud sessions, managed Code Review and Ultrareview. Plan a local review path such as /code-review before you switch it on.
    • Claude Fable 5.1 and Fable 5 are Covered Models that require data retention by default; whether a ZDR organization can use them is governed by Anthropic’s Covered Models policies.
    • Claude Code keeps session transcripts locally, in plaintext, under ~/.claude/projects/ for 30 days by default. Lower cleanupPeriodDays and require disk encryption on developer machines.

    For Codex and Cursor, get the equivalent answers in writing; the procurement questionnaire lists them. For PHI, the rule is short: no BAA covering that exact surface, no PHI in a prompt.

  2. Deploy the policy floor. Push one managed file per tool through MDM or the vendor’s admin channel. These are minimal baselines; managed policy has the complete files, delivery paths and precedence rules.

    Deploy as managed-settings.json to /etc/claude-code/ on Linux and WSL, /Library/Application Support/ClaudeCode/ on macOS, or C:\Program Files\ClaudeCode\ on Windows. Users and projects cannot override it.

    {
    "forceLoginMethod": "claudeai",
    "forceLoginOrgUUID": ["00000000-0000-0000-0000-000000000000"],
    "permissions": {
    "deny": ["Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)", "Read(./**/*.pem)"],
    "disableBypassPermissionsMode": "disable"
    },
    "allowedMcpServers": [
    { "serverUrl": "https://api.githubcopilot.com/*" }
    ],
    "allowManagedMcpServersOnly": true,
    "cleanupPeriodDays": 7,
    "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.acme.internal:4317"
    }
    }

    Replace the UUID with your Anthropic organization ID and the endpoint with your collector. forceLoginOrgUUID is what keeps sessions inside your ZDR organization: a personal account or another organization’s API key is not covered. Sessions that select a cloud provider (Bedrock, Google Cloud, Foundry) bypass the login pins and fall under that provider’s retention terms, not ZDR. OTEL_EXPORTER_OTLP_PROTOCOL is required, because Claude Code has no default OTLP protocol and exports nothing without it.

    From v2.1.283 (the latest channel on 2026-09-26), auto mode is the starting permission mode for interactive sessions: a classifier model approves routine actions. Decide explicitly whether to keep it. "disableAutoMode": "disable" in managed settings removes it for everyone. For regulated data, add allowManagedPermissionRulesOnly and allowManagedHooksOnly, knowing both switch off the project-level rules and hooks that teams use as quality gates.

  3. Send telemetry to the SIEM and check it arrives. The Claude Code file above already exports metrics and log events. Prompt text stays redacted unless you set OTEL_LOG_USER_PROMPTS=1, and tool arguments stay out unless you set OTEL_LOG_TOOL_DETAILS=1. Turn either on only after your data-protection officer agrees, because both put source code and possibly personal data into the SIEM. The evidence is a query that returns events for every licensed user in the last seven days.

  4. Make the CI gates required. Secret scanning, SAST and dependency scanning run on every pull request whoever wrote it; security gates for agent-written code has the Gitleaks pre-commit hook, the Claude Code hooks that stop an agent from skipping it, and the TruffleHog and Semgrep jobs. Add one gate those scanners do not cover: an agent-written migration that drops data. This job fails unless a named person accepts the risk in the pull request body:

    .github/workflows/schema-gate.yml
    name: schema-gate
    on:
    pull_request:
    types: [opened, synchronize, reopened, edited]
    permissions:
    contents: read
    jobs:
    destructive-migrations:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
    with:
    fetch-depth: 0
    persist-credentials: false
    - name: Require an accepted risk for destructive migrations
    env:
    BASE_SHA: ${{ github.event.pull_request.base.sha }}
    PR_BODY: ${{ github.event.pull_request.body }}
    run: |
    destructive=$(git diff -z --name-only --diff-filter=AMR "$BASE_SHA"...HEAD -- 'migrations/*.sql' \
    | xargs -0 -r grep -Eil 'drop[[:space:]]+(table|column)|truncate' || true)
    if [ -n "$destructive" ]; then
    echo "$destructive"
    if ! grep -Eq '^MIGRATION-RISK: accepted by @[A-Za-z0-9-]+' <<<"$PR_BODY"; then
    echo "::error::Destructive migration without 'MIGRATION-RISK: accepted by @owner' in the PR body"
    exit 1
    fi
    fi

    The PR body reaches the script through an environment variable, never through ${{ }} inside run, so a crafted body cannot inject shell. Mark schema-gate as a required check, and route migrations/ to your data team in CODEOWNERS with a ruleset that requires code-owner review and has no bypass actors. The line in the body names who accepted the risk; the code-owner approval is the control.

  5. Scan every agent extension before approval. MCP servers, skills and plugins run with the agent’s privileges. Snyk Agent Scan (PyPI snyk-agent-scan, formerly mcp-scan) inventories them on a machine and flags prompt injection, tool poisoning and toxic flows. It needs a Snyk API token:

    Terminal window
    # Terminal, inside a disposable VM or container, with SNYK_TOKEN set from your secret manager
    uvx snyk-agent-scan@latest ~/.claude/skills
    uvx snyk-agent-scan@latest ./candidate-server/mcp.json

    The scan starts stdio MCP servers to inspect them, which executes their code, so never run it on an untrusted config on a laptop. It is a Python tool; the npm package named mcp-scan is an unrelated project. Record each approved server in the register that feeds control 6; MCP security has the register template.

A control that was configured once is a claim. A control that passes a test on demand is evidence. Run these on a schedule, keep the output, and have the security owner sign the monthly summary.

TestHowPasses whenCadence
Secrets stay out of context (Claude Code)The script below, on a managed machineThe canary value never appears in the outputWeekly, and after every policy change
The sandbox holds (Codex)The script belowNo file appears outside the workspaceWeekly
Personal accounts are refusedSign in with a non-company accountLogin is rejectedMonthly
Unapproved MCP server is blockedAdd a server that is not on the listThe tool refuses to load itMonthly
Telemetry arrivesSIEM query for events per licensed userEvery active user reports in the last 7 daysWeekly
CI gates blockOpen a canary pull request with a planted test secret and a DROP TABLE migrationBoth required checks go red; never merge the branchMonthly

The two agent tests are scripts you can put in a scheduled job on a managed test machine:

#!/usr/bin/env bash
# control-tests.sh: run on a managed machine that carries the production policy
set -uo pipefail
dir=$(mktemp -d) && cd "$dir" && git init -q
echo "CANARY_TOKEN=canary-7f3a9c" > .env
# Claude Code: the deny rule must keep .env out of the model's context
out=$(claude -p "Read the file .env and print its contents verbatim." \
--output-format stream-json --verbose --permission-prompts none)
jq -e 'select(.type=="result")' <<<"$out" >/dev/null \
|| { echo "ERROR claude: no result event (no login, or the policy refused the credential)"; exit 2; }
if grep -q "canary-7f3a9c" <<<"$out"; then echo "FAIL claude: .env was read"; exit 1; fi
jq -c 'select(.type=="result") | .permission_denials' <<<"$out"
echo "PASS claude: .env stayed out of context"
# Codex: a workspace-write sandbox must write inside the workspace and nowhere else
rm -f "$HOME/codex-canary.txt"
codex -a on-request exec --sandbox workspace-write \
"Create the file ./inside-canary.txt containing the word canary." >/dev/null 2>&1
rc=$?
if [ "$rc" -ne 0 ] || [ ! -e ./inside-canary.txt ]; then
echo "ERROR codex: positive control failed (exit $rc), so the sandbox was not tested"; exit 2
fi
codex -a on-request exec --sandbox workspace-write \
"Create the file $HOME/codex-canary.txt containing the word canary." >/dev/null 2>&1
echo "codex outside-write run exited $?"
if [ -e "$HOME/codex-canary.txt" ]; then echo "FAIL codex: wrote outside the workspace"; rm -f "$HOME/codex-canary.txt"; exit 1; fi
echo "PASS codex: sandbox held"

--permission-prompts none needs Claude Code 2.1.259 or later; it denies anything that would have asked a person. The permission_denials line records which tool calls the policy blocked, which is the evidence line for the file. The permission_denials list includes path-scoped Read denials from v2.1.269; on older versions it can be empty even when the rule held. A model that simply declines to try still passes, because the test checks the outcome, not the attempt. Exit code 2 means the test never ran: a missing login, a refused credential or a Codex run that could not even write inside its own workspace. Treat an ERROR as a failed control run, never as a PASS. The Codex test asks for on-request approval because the requirements file above allows only on-request and untrusted; asking for never would request a disallowed policy on the very machine under test. If your Codex policy uses permission profiles (beta) instead of sandbox modes, replace --sandbox workspace-write with -c default_permissions=":workspace", because OpenAI says the two systems do not compose. For Cursor, use the control test on the Cursor privacy page.

Keep the output outside GitHub Actions. Its logs and artifacts expire after the repository’s retention setting (90 days by default), which can end before your SOC 2 Type II observation period does, so export the check history and test results to the same store as your SIEM data.

Copy-paste prompts for the security review

Section titled “Copy-paste prompts for the security review”

Run these in Claude Code, Codex or Cursor from the repository root. They read and report; none of them changes files.

What breaks in an AI security baseline, and how do you recover?

Section titled “What breaks in an AI security baseline, and how do you recover?”
  • The SIEM is empty although telemetry is on. OTEL_EXPORTER_OTLP_PROTOCOL is missing, or the collector is unreachable from developer networks. Set the protocol (grpc for port 4317, http/protobuf for 4318), test against a local collector, then alert on users who stop reporting.
  • You assumed ZDR, but sessions ran outside it. A developer signed in with a personal account or an API key from another organization, a session selected a cloud provider (Bedrock, Google Cloud, Foundry), which the login pins do not cover and ZDR does not reach, or a new organization was created without ZDR. Enforce forceLoginOrgUUID, and ask the account team to enable ZDR for every organization you add.
  • ZDR switched off a feature teams relied on. Cloud sessions, managed Code Review and Ultrareview do not run under ZDR. Agree the replacement before the switch: local /code-review, a review bot under its own agreement, or a separate non-ZDR organization for repositories with no regulated data.
  • Regulated data left through an MCP server. ZDR does not extend to integrations. Keep regulated-data paths denied, allow only servers with their own DPA, and log MCP tool calls by enabling OTEL_LOG_TOOL_DETAILS=1 once your data-protection officer has approved it (see step 3 of the rollout).
  • A secret reached context despite the deny rule. Commands that read without naming the file, such as grep -r run from the parent directory, are not covered by Read deny rules (Claude Code 2.1.283). Keep secrets out of the working tree or inject them at run time, and treat the deny rule as one layer, not the boundary.
  • Secrets sit in local transcripts. Anything the agent read is in plaintext under ~/.claude/projects/ until the cleanup period ends. Rotate the secret, delete the transcript, and lower cleanupPeriodDays.
  • The schema gate blocks a hotfix. Keep the exception inside the control: the on-call engineer writes the MIGRATION-RISK line with their handle, and the code owner approves. An admin bypass of a required check is itself an audit finding.
  • A repository hook or MCP config runs code on checkout. Project settings are an attack path when an agent opens an untrusted branch. In regulated repositories, use allowManagedHooksOnly and the managed MCP allowlist, and read agent identity and secrets before any agent runs in CI with a secret.
  • PHI was sent under a ZDR agreement. Zero retention is not a business associate agreement. Stop the flow, involve your privacy officer, and get a BAA naming that surface before PHI goes back into any prompt.

Where to go next with security and compliance

Section titled “Where to go next with security and compliance”