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.
What this security baseline gives you
Section titled “What this security baseline gives you”- 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 question | Controls 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.
The eight-control security baseline
Section titled “The eight-control security baseline”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.
| # | Control | Claude Code | Codex | Cursor | Evidence | Deep dive |
|---|---|---|---|---|---|---|
| 1 | Data boundary: contracted retention and no training | Commercial terms (no training on Claude Code data); ZDR on qualified Claude for Enterprise orgs | Your ChatGPT Enterprise or API agreement | Privacy Mode, enforced at team level (confirm the tier) | Signed DPA, ZDR or BAA letter, vendor SOC 2 report | Procurement, model hosting |
| 2 | Identity: only company accounts | forceLoginMethod, forceLoginOrgUUID | allowed_login_methods, allowed_chatgpt_workspaces | SSO enforced in the admin dashboard | IdP export, managed settings file in git | Agent identity and secrets |
| 3 | Secret and regulated-data exclusion | permissions.deny on Read(...) paths, plus secrets kept out of the working tree | Keep secrets out of the workspace; inject at run time | .cursorignore | Denials in telemetry, control test result | Data privacy |
| 4 | Policy floor nobody can lower | managed-settings.json with permissions.disableBypassPermissionsMode | requirements.toml with allowed_sandbox_modes, allowed_approval_policies | Admin settings for run modes (confirm what can be locked) | Versioned policy repo; Claude Code /status, Codex /debug-config output | Managed policy |
| 5 | Telemetry to your SIEM | OpenTelemetry through env in managed settings | [otel] in a distributed config.toml | Admin dashboard and API (confirm audit log scope) | Collector ingest per active user | Agent observability |
| 6 | Vetted extensions (MCP, skills, plugins) | allowedMcpServers + allowManagedMcpServersOnly; strictKnownMarketplaces | [mcp_servers.<name>.identity] in requirements.toml | Confirm whether MCP can be limited to an approved list | Server register with scan results | MCP security |
| 7 | Required CI gates | Tool-independent: secret scan, SAST, dependency scan, schema-change gate | Same | Same | Required-check history on every merged PR | Security gates |
| 8 | Human approval of sensitive changes | Tool-independent: CODEOWNERS plus a ruleset with no bypass actors; an agent never approves | Same | Same | PR review records | Agent 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.
Map SOC 2, GDPR and HIPAA to the baseline
Section titled “Map SOC 2, GDPR and HIPAA to the baseline”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 requirement | Baseline controls | Evidence to hand over |
|---|---|---|
| SOC 2 CC6.1 logical access | 2, 4 | SSO and SCIM configuration, org-pinned login in the managed policy |
| SOC 2 CC7.2 monitoring of system components | 5 | Agent telemetry retained in the SIEM, with denied tool calls |
| SOC 2 CC8.1 change management | 7, 8 | Merged PRs with passing required checks and a human approval |
| SOC 2 CC9.2 vendor and business-partner risk | 1, 6 | Vendor SOC 2 reports, DPAs, the MCP and plugin register |
| GDPR Art. 5(1)(c) data minimisation, Art. 25 data protection by design | 3 | Deny rules and ignore files for personal-data paths; synthetic test data |
| GDPR Art. 28 processors | 1, 6 | A DPA with each vendor, including every hosted MCP server that receives personal data |
| GDPR Art. 32 security of processing, Art. 33 breach notification | 4, 5 | Policy floor, telemetry, and an incident procedure that decides on notification within 72 hours |
| HIPAA business associate agreement (45 CFR 164.502(e)) | 1 | A BAA that names the exact product surface that touches PHI |
| HIPAA audit controls (45 CFR 164.312(b)) | 5 | Telemetry 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.
Roll out the baseline in five steps
Section titled “Roll out the baseline in five steps”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.
-
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-reviewbefore 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. LowercleanupPeriodDaysand 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.
-
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.jsonto/etc/claude-code/on Linux and WSL,/Library/Application Support/ClaudeCode/on macOS, orC:\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.
forceLoginOrgUUIDis 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_PROTOCOLis required, because Claude Code has no default OTLP protocol and exports nothing without it.From v2.1.283 (the
latestchannel 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, addallowManagedPermissionRulesOnlyandallowManagedHooksOnly, knowing both switch off the project-level rules and hooks that teams use as quality gates.Codex reads admin constraints from
requirements.toml, separate from theconfig.tomldefaults users edit. On Linux the system file is/etc/codex/requirements.toml./etc/codex/requirements.toml allowed_login_methods = ["chatgpt"]allowed_chatgpt_workspaces = ["CHATGPT_WORKSPACE_ID"]allowed_approval_policies = ["on-request", "untrusted"]allowed_sandbox_modes = ["read-only", "workspace-write"][mcp_servers.github.identity]url = "https://api.githubcopilot.com/mcp/"Replace
CHATGPT_WORKSPACE_IDwith your workspace ID. Leavingdanger-full-accessout ofallowed_sandbox_modesis the policy floor; the list must includeread-only. The first entry ofallowed_approval_policiesis the fallback when someone requests a disallowed policy. Telemetry is not a requirements key: ship an[otel]section in theconfig.tomlyou distribute, and watch the collector for machines that stop reporting.Cursor’s team controls live in its admin dashboard, and cursor.com could not be reached on 2026-09-26. Close these questions in the dashboard and record each answer with the date:
- Is Privacy Mode enforced for every member, and which models need an admin exception because they require retention?
- Is SSO enforced, so no one signs in with a personal account on company repositories?
- Which run mode can members choose, and can the team lock it?
- Can MCP servers be limited to an approved list?
Commit a
.cursorignorefor secret and regulated-data paths. Privacy and security in Cursor covers the file, a hooks-based guard and a test for both. -
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 setOTEL_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. -
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-gateon:pull_request:types: [opened, synchronize, reopened, edited]permissions:contents: readjobs:destructive-migrations:runs-on: ubuntu-lateststeps:- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1with:fetch-depth: 0persist-credentials: false- name: Require an accepted risk for destructive migrationsenv: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" ]; thenecho "$destructive"if ! grep -Eq '^MIGRATION-RISK: accepted by @[A-Za-z0-9-]+' <<<"$PR_BODY"; thenecho "::error::Destructive migration without 'MIGRATION-RISK: accepted by @owner' in the PR body"exit 1fifiThe PR body reaches the script through an environment variable, never through
${{ }}insiderun, so a crafted body cannot inject shell. Markschema-gateas a required check, and routemigrations/to your data team inCODEOWNERSwith 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. -
Scan every agent extension before approval. MCP servers, skills and plugins run with the agent’s privileges. Snyk Agent Scan (PyPI
snyk-agent-scan, formerlymcp-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 manageruvx snyk-agent-scan@latest ~/.claude/skillsuvx snyk-agent-scan@latest ./candidate-server/mcp.jsonThe 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-scanis an unrelated project. Record each approved server in the register that feeds control 6; MCP security has the register template.
How do you prove the controls hold?
Section titled “How do you prove the controls hold?”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.
| Test | How | Passes when | Cadence |
|---|---|---|---|
| Secrets stay out of context (Claude Code) | The script below, on a managed machine | The canary value never appears in the output | Weekly, and after every policy change |
| The sandbox holds (Codex) | The script below | No file appears outside the workspace | Weekly |
| Personal accounts are refused | Sign in with a non-company account | Login is rejected | Monthly |
| Unapproved MCP server is blocked | Add a server that is not on the list | The tool refuses to load it | Monthly |
| Telemetry arrives | SIEM query for events per licensed user | Every active user reports in the last 7 days | Weekly |
| CI gates block | Open a canary pull request with a planted test secret and a DROP TABLE migration | Both required checks go red; never merge the branch | Monthly |
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 policyset -uo pipefaildir=$(mktemp -d) && cd "$dir" && git init -qecho "CANARY_TOKEN=canary-7f3a9c" > .env
# Claude Code: the deny rule must keep .env out of the model's contextout=$(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; fijq -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 elserm -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>&1rc=$?if [ "$rc" -ne 0 ] || [ ! -e ./inside-canary.txt ]; then echo "ERROR codex: positive control failed (exit $rc), so the sandbox was not tested"; exit 2ficodex -a on-request exec --sandbox workspace-write \ "Create the file $HOME/codex-canary.txt containing the word canary." >/dev/null 2>&1echo "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; fiecho "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_PROTOCOLis missing, or the collector is unreachable from developer networks. Set the protocol (grpcfor port 4317,http/protobuffor 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=1once 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 -rrun 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 lowercleanupPeriodDays. - The schema gate blocks a hotfix. Keep the exception inside the control: the on-call engineer writes the
MIGRATION-RISKline 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
allowManagedHooksOnlyand 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.