Security Standards and Compliance
Security standards for AI-assisted development rest on four enforceable layers: a data boundary each tool exposes as a setting (privacy mode, managed deny rules, sandbox), rules files backed by those deny lists, the audit surfaces the tools actually emit, and CI gates that block unreviewed migrations, leaked secrets, and OWASP findings. Together they turn SOC 2, GDPR, and HIPAA evidence into a passing check.
Your security team just put the AI rollout on hold, and the questions are reasonable: where does our source code go, can a developer paste a customer’s PII into a prompt, and can you prove to a SOC 2 auditor exactly who ran what? Three weeks out from the Type II audit, the assessor also wants evidence that AI-generated code is reviewed before it ships and that the MCP servers your team installed last sprint are not quietly exfiltrating files. Meanwhile developers are merging agent-authored PRs at three times last year’s pace.
Generic “use clear prompts” advice answers none of that. What follows is the concrete, enforceable set: the settings you flip, the configs you commit, the audit surfaces these tools actually ship, and the scanners and policies you wire into CI — so compliance is a passing check rather than a quarterly fire drill, and the controls keep up without becoming the bottleneck everyone routes around.
What this compliance-gating setup gives you
Section titled “What this compliance-gating setup gives you”- The exact privacy and data-retention setting to enforce in Cursor, Claude Code, and Codex so source code is not retained or trained on
- A committed, machine-enforced policy file per tool that blocks
.envreads, dangerous shell, and unapproved MCP servers — not a wiki page people ignore - The real audit and telemetry configuration for each tool, with no invented settings keys, mapped to SOC 2, GDPR, and HIPAA control families
- A working MCP supply-chain scan using Snyk Agent Scan (formerly Invariant MCP-Scan) wired into all three tools
- A
PreToolUsehook and pre-commit setup that scans diffs for secrets and PII before anything leaves the machine, using real OSS (gitleaks,detect-secrets,trivy) - Copy-paste prompts that produce runnable artifacts: a Semgrep ruleset, an OWASP Top 10:2025 review, a CODEOWNERS-gated PR template, SOC 2 CC7.1 audit middleware, and an Open Policy Agent gate that blocks unreviewed AI-generated migrations
- A failure-modes section covering the gotchas auditors and red teams actually find
Where AI tooling touches your compliance boundary
Section titled “Where AI tooling touches your compliance boundary”Three surfaces matter for an audit:
- Data egress. Your prompts contain source code and sometimes secrets. For SOC 2 confidentiality and HIPAA, you need a documented zero-retention posture with each vendor. Cursor’s Privacy Mode and Enterprise plan guarantee zero data retention (see trust.cursor.com); Anthropic and OpenAI offer the same for API and enterprise tiers. Document which mode each tool runs in — that document is the control evidence.
- Audit trail. You need to show who ran what. Each tool exposes a different mechanism. None of them has a magic
audit_logging: trueflag — the real surfaces are OpenTelemetry, shell-command wrapping, and platform admin logs. - Supply chain. MCP servers run with your tools’ privileges. In Equixly’s 2025 assessment of popular open-source MCP server implementations, 43% contained command-injection flaws, 30% allowed unrestricted URL fetches, and 22% leaked files outside their intended directories. Treat every MCP server like an unvetted dependency.
Step 1: lock the data boundary in each tool
Section titled “Step 1: lock the data boundary in each tool”These controls differ meaningfully per tool, so configure each one. The goal is identical: prevent source and secrets from being retained, and stop sensitive files from ever entering context.
Enforce Privacy Mode at the team level so individuals cannot turn it off, then keep sensitive files out of indexing and context with .cursorignore.
- Team dashboard -> Settings -> enable Privacy Mode and toggle Enforce (members can no longer disable it). Privacy Mode is on by default for Enterprise and gives you ZDR with all model providers.
- On corporate devices, deploy the Allowed Team IDs MDM policy so users cannot sign into a personal account that lacks Privacy Mode.
- Commit a
.cursorignoreso secrets and infra never get indexed or sent as context:
# .cursorignore — never index or send to models.env.env.***/secrets/****/*.pem**/*.keyterraform.tfstate***/credentials.jsonFor regulated workloads, ask sales to enable CMEK (customer-managed encryption keys) so embeddings and any Cloud Agent data are encrypted with your key. If your policy forbids storing code at all, simply do not enable Cloud Agents — every other Cursor feature still works. Cursor Business and Enterprise carry SOC 2 Type II attestations, which is what you attach to the vendor-management file.
Ship a managed settings file that IT deploys system-wide. Users and projects cannot override it, so this is your hard floor.
managed-settings.json lives at /Library/Application Support/ClaudeCode/ (macOS), /etc/claude-code/ (Linux/WSL), or C:\Program Files\ClaudeCode\ (Windows):
{ "permissions": { "deny": [ "Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)", "Read(./**/*.pem)", "Bash(curl:*)", "WebFetch" ] }, "allowManagedPermissionRulesOnly": true, "disableBypassPermissionsMode": "disable", "deniedMcpServers": [{ "serverName": "filesystem" }]}Deny rules are evaluated before anything is read into context. disableBypassPermissionsMode: "disable" kills the --dangerously-skip-permissions escape hatch; allowManagedPermissionRulesOnly ignores any allow/ask/deny rules a developer adds locally; deniedMcpServers blocks risky servers org-wide.
Add a UserPromptSubmit hook to scan prompts themselves for secrets before they are sent. The matcher object holds a nested hooks array, and the command reads the event JSON from stdin — there is no $PROMPT variable:
{ "hooks": { "UserPromptSubmit": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "python scripts/check-sensitive-data.py" }] }] }}Authenticate through Claude for Enterprise for SSO, RBAC, and the compliance API. For commercial usage (Team, Enterprise, and API), Anthropic does not train models on your code or prompts, and the default retention is 30 days; zero data retention — where chat transcripts are not retained on servers — is available with appropriately configured API keys, not on by default.
Lock the agent down with sandbox_mode and approval_policy, and split local versus cloud access in ChatGPT Enterprise.
In ~/.codex/config.toml (or push it via your team config):
# Read-only by default; agent must ask before writing or running anything riskysandbox_mode = "read-only"approval_policy = "on-request"
[sandbox_workspace_write]network_access = falseSandbox values are read-only, workspace-write, and danger-full-access — never danger-full-access on a developer laptop touching a real repo. In Workspace Settings -> Settings and Permissions, use RBAC to enable Codex Local (ChatGPT desktop, CLI, and IDE; runs in the on-device sandbox) and Codex Cloud (hosted containers) for different groups. ChatGPT Enterprise gives ZDR for the CLI and IDE plus AES-256 at rest and TLS 1.2+ in transit.
Step 2: rules that guide, deny lists that enforce
Section titled “Step 2: rules that guide, deny lists that enforce”The legacy single-file .cursorrules is deprecated. Use Cursor Project Rules (.cursor/rules/*.mdc), Claude Code’s CLAUDE.md plus committed project settings, and Codex’s AGENTS.md. A rules file is guidance the model usually follows; the deny lists from Step 1 are what actually enforce it. Use both.
Create .cursor/rules/security.mdc. The frontmatter alwaysApply: true injects it into every session:
---description: "Security & data-handling rules"alwaysApply: true---
- Privacy Mode must stay enabled; never sign in with a personal account.- Never write API keys, passwords, tokens, or connection strings into code. Read them from `process.env`.- Never put customer PII (names, emails, SSNs, MRNs) in fixtures, logs, or prompts. Use `@faker-js/faker` for test data.- When discussing a schema with PII columns, use anonymized column names.- Reference `.env.example` for environment variable names, never `.env`.- Mark AI-assisted code with a `// AI-assisted` comment so review and CODEOWNERS can track it.- Redact secrets before logging; encrypt sensitive data at rest and in transit.Put the same rules in CLAUDE.md at the repo root (auto-loaded as memory), and commit team permission rules in .claude/settings.json:
{ "permissions": { "deny": ["Read(./.env)", "Read(./secrets/**)"], "ask": ["Bash(git push:*)"] }}CLAUDE.md steers the model; the committed deny/ask rules enforce the hard edges for everyone on the repo, on top of the managed settings IT deployed in Step 1.
Codex reads AGENTS.md from the repo root. Use the same content as the Cursor rule above — the format is plain Markdown and the guidance is identical across tools. Pair it with the sandbox_mode from Step 1 so the rules are backed by an actual sandbox, not just intent.
Guidance about handling secrets is the floor. The rule file earns its keep when it encodes your standards, so that generated code is secure by default rather than plausible by default:
// .cursor/rules/security.mdc, CLAUDE.md, or AGENTS.md — standards sectionSECURITY CODING STANDARDS:
Authentication:- All API endpoints must use the authMiddleware from /src/middleware/auth.ts- JWT tokens expire after 15 minutes, refresh tokens after 7 days- Password hashing uses bcrypt with cost factor 12
Input Validation:- All request bodies validated with Zod schemas before processing- File uploads limited to 10MB, allowed types: jpg, png, pdf- URL parameters must be validated as UUIDs where applicable
Database:- ALL queries must use parameterized statements (Drizzle ORM or prepared statements)- Never construct SQL strings with string concatenation- Database connections use least-privilege service accounts
Output:- All HTML output must be escaped (handled by React/template engine)- API responses must not include internal error details in production- Set Content-Security-Policy, X-Frame-Options, X-Content-Type-Options headersStep 3: turn on the audit evidence each tool actually emits
Section titled “Step 3: turn on the audit evidence each tool actually emits”This is the part most teams get wrong by pasting in config keys that do not exist. Here is what each tool actually supports.
Claude Code has no audit_logging setting. Observability is OpenTelemetry, and command-level auditing is a shell prefix. Enable both in ~/.claude/settings.json (managed settings on a controlled host for enterprise):
{ "env": { "CLAUDE_CODE_ENABLE_TELEMETRY": "1", "OTEL_METRICS_EXPORTER": "otlp", "OTEL_LOGS_EXPORTER": "otlp", "OTEL_EXPORTER_OTLP_ENDPOINT": "https://otel-collector.internal:4317", "CLAUDE_CODE_SHELL_PREFIX": "/usr/local/bin/audit-logger.sh" }}CLAUDE_CODE_ENABLE_TELEMETRY=1 streams metrics and logs to your collector (then to your SIEM); CLAUDE_CODE_SHELL_PREFIX wraps every Bash command so audit-logger.sh <command> records it. That command log is your SOC 2 access/activity evidence.
Cursor exposes no per-user cursor.audit.* or cursor.compliance.* keys — compliance is platform-level. Your evidence comes from the Enterprise plan and Trust Center, not a JSON file:
- Privacy Mode / zero data retention — enforced org-wide in Step 1. This is your data-handling control.
- SSO + SCIM provisioning and admin analytics/audit in the team dashboard, plus the Admin API for pulling access and usage logs — your access-control and activity evidence.
- SOC 2 Type II + GDPR attestations and subprocessor list at trust.cursor.com — attach these to your vendor-management file.
Document “Cursor Enterprise, Privacy Mode enforced, SSO via Okta” as the control; the Trust Center report is the third-party evidence.
Codex auditing lives in ~/.codex/config.toml (managed centrally for teams). There is no audit-logging key; you constrain and record behavior through the sandbox and approval policy from Step 1, plus your own shell wrapper.
Pair that with Codex Cloud’s per-task run history — every Cloud task is logged with its diff and command output — and with the Compliance API, which exports prompt text, responses, user, timestamp, model, and token usage straight into your SIEM or eDiscovery pipeline. For a hard egress boundary, run the CLI inside a container whose outbound traffic is logged at the network layer.
Mapping SOC 2, GDPR, and HIPAA to those surfaces
Section titled “Mapping SOC 2, GDPR, and HIPAA to those surfaces”Auditors do not accept “we trust developers.” Each surface above answers a specific control family:
-
SOC 2 (CC6 access, CC7.1 detection). Pull access and usage logs from each tool’s admin surface: Cursor’s Admin API and team analytics, Claude Code via Claude for Enterprise (SSO, RBAC, compliance API), and the Codex Compliance API. For your own services, generate the CC7.1 audit-logging middleware with the prompt below.
-
GDPR (data minimization, right to erasure). Keep PII out of prompts entirely —
.cursorignore,permissions.deny, and the sandbox from Step 1 handle that. For erasure, the Codex Compliance API and the Cursor and Anthropic enterprise exports let you locate and account for any record tied to a user. Never use production data as test fixtures; generate synthetic data instead. -
HIPAA (PHI handling). Never send Protected Health Information to a model without a signed BAA covering that specific surface. Default to synthetic patients and redact PHI before it reaches a prompt.
Generate synthetic test data with the maintained @faker-js/faker (the old standalone faker package is deprecated and its faker.datatype.* API no longer exists):
import { faker } from '@faker-js/faker';
export function syntheticPatient() { return { id: faker.string.uuid(), name: faker.person.fullName(), dob: faker.date.past({ years: 80 }), mrn: `TEST-${faker.number.int({ min: 100000, max: 999999 })}`, conditions: ['Synthetic Condition A', 'Synthetic Condition B'], };}Step 4: scan MCP servers before you trust them
Section titled “Step 4: scan MCP servers before you trust them”Before any MCP server reaches a developer’s machine, scan it. The tool to use is Snyk Agent Scan (the former Invariant Labs MCP-Scan, now maintained by Snyk). It runs through uvx — it is a Python tool, so do not npm install it.
-
Scan a candidate server’s config (or your whole
mcp.json) for tool-poisoning, prompt-injection, and toxic-flow patterns:Terminal window uvx snyk-agent-scan@latest ~/.cursor/mcp.jsonThe legacy entrypoint
uvx mcp-scan@lateststill works — it is now a redirect that installssnyk-agent-scanand forwards the CLI. -
Read the findings. A flagged server might show an unrestricted
fetchtool that accepts arbitrary URLs, or a tool description containing hidden instructions (a tool-poisoning attack). Do not install it until that is resolved. -
Register the scanner itself as an MCP server so the agent can re-scan on demand:
Add it in Settings → MCP → Add Server, or edit ~/.cursor/mcp.json directly so the config is reviewable in version control:
{ "mcpServers": { "security-analyzer": { "command": "uvx", "args": ["snyk-agent-scan"], "env": { "SNYK_TOKEN": "${SNYK_TOKEN}" } } }}Only SNYK_TOKEN (for authenticated scans) is consumed — do not invent SECURITY_SCAN_MODE or SEMGREP_APP_TOKEN here. Run Semgrep as its own step rather than as env on the scanner.
claude mcp add security-analyzer -- uvx snyk-agent-scanclaude mcp listPair this with deniedMcpServers in managed settings so a server that fails review cannot be re-added locally.
codex mcp add security-analyzer -- uvx snyk-agent-scanOr declare it in ~/.codex/config.toml:
[mcp_servers.security-analyzer]command = "uvx"args = ["snyk-agent-scan"]Step 5: block secrets and PII before they leave the machine
Section titled “Step 5: block secrets and PII before they leave the machine”Use real, maintained scanners — not a hand-rolled regex class. gitleaks (Go binary) and detect-secrets (pip install detect-secrets) catch credentials; trivy catches vulnerable dependencies and misconfiguration. Wire them in two places: a Claude Code PreToolUse hook that blocks a commit the agent is about to run, and CI as the backstop.
The hook is where the three tools diverge — Claude Code can block a tool call mid-flight; Cursor and Codex rely on pre-commit plus CI.
Cursor has no commit-blocking hook, so enforce at the git layer with a pre-commit config that every clone inherits:
repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.21.2 hooks: - id: gitleaks - repo: https://github.com/Yelp/detect-secrets rev: v1.5.0 hooks: - id: detect-secrets args: ['--baseline', '.secrets.baseline']Run pre-commit install once per clone (or enforce it via your repo bootstrap). Now any commit — AI-assisted or not — is scanned before it lands.
Add a PreToolUse hook that scans the staged diff before the agent is allowed to run git commit. In .claude/settings.json:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [{ "type": "command", "command": ".claude/hooks/scan-secrets.sh" }] } ] }}.claude/hooks/scan-secrets.sh reads the tool input on stdin and denies the commit if gitleaks finds anything:
#!/usr/bin/env bashcmd=$(jq -r '.tool_input.command // ""')case "$cmd" in *"git commit"*) if ! gitleaks git --staged --no-banner; then echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"gitleaks found a secret in the staged diff"}}' exit 0 fi ;;esacexit 0The agent literally cannot commit a secret — the hook returns permissionDecision: "deny" and Claude Code aborts the tool call.
Codex respects the pre-commit hooks above when it runs git commit inside the sandbox, so install them the same way. Keep approval_policy = "on-request" so Codex can ask before crossing the sandbox boundary, but do not treat it as a per-commit gate: an in-sandbox commit may proceed without a prompt. Enforce review with hooks, protected branches, and an explicit prompt that forbids push or merge until approval.
Prompts that produce runnable artifacts, not documents
Section titled “Prompts that produce runnable artifacts, not documents”The point of AI here is a shippable artifact — a ruleset, a fix, a policy file — not a Word doc describing one. Anchor prompts to a concrete stack so the output runs.
The Semgrep ruleset is the standing gate. For a full sweep of one module against the current OWASP list, ask for the review directly:
For a single pull request, the useful output is a verdict a reviewer can act on, not a checklist of categories:
The same discipline turns the quarterly compliance checklist into a repeatable pass:
Run any of these across tools — the workflow is identical; only the entry point differs. In Cursor, open the diff and invoke Agent mode on it, or hand it to a Background Agent on every PR. In Claude Code, run claude -p "<prompt>" on the branch so it reads the working tree, or wire it into a hook. With Codex, hand it the PR via the GitHub integration or codex exec in CI. Pin the model: use Claude Fable 5 for the highest-stakes reviews, Opus 5 for lower-cost premium reasoning, or the appropriate GPT-5.6 Sol/Terra/Luna tier in ChatGPT Codex. See model comparison for the full tier breakdown.
Enforcing policy in CI
Section titled “Enforcing policy in CI”Audit evidence is strongest when it is automatic. Keep the hard gate deterministic and let the AI review advise, exactly as you would for any scanner.
The deterministic backstop is identical regardless of which tool wrote the code:
name: Security Scanon: [pull_request]jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: { fetch-depth: 0 } - name: Secret scan uses: gitleaks/gitleaks-action@v2 - name: Dependency & misconfig scan uses: aquasecurity/trivy-action@v0.36.0 with: { scan-type: 'fs', severity: 'HIGH,CRITICAL', exit-code: '1' } - name: AI-attribution check run: | count=$(git diff --name-only origin/${{ github.base_ref }}... \ | xargs grep -l "AI-assisted\|AI-generated" 2>/dev/null | wc -l) if [ "$count" -eq 0 ]; then echo "::warning::No AI-assisted attribution found in changed files" fiThe AI review runs alongside it. Note how the gate is built: claude -p returns the model’s text response, not a severity-based exit code, so with --output-format json the answer lands in .result and a jq -e step is what fails the job:
security-review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0 - name: AI Security Review run: | claude -p --output-format json "Review the git diff for security issues: $(git diff origin/main...HEAD)
Check for: 1. SQL injection vulnerabilities 2. Missing authentication/authorization 3. Hardcoded secrets or credentials 4. Unvalidated user input 5. Insecure cryptographic operations 6. Missing rate limiting on new endpoints
Return ONLY JSON: {\"issues\": [{\"severity\", \"file\", \"line\", \"description\", \"fix\"}]}" \ | jq -e '.result | fromjson | all(.issues[]; .severity != "Critical" and .severity != "High")'With Codex, the equivalent is an automation triggered by PR events, which posts findings back as a review instead of failing a step:
When a PR is opened, perform a security review:1. Analyze all changed files for OWASP Top 10:2025 vulnerabilities2. Check new dependencies against known vulnerability databases3. Verify authentication is required on all new endpoints4. Confirm input validation exists for all new user-facing parameters5. Post findings as a PR review comment with inline annotationsThe highest-leverage gate for AI-assisted teams is the one nobody builds by hand: blocking unreviewed schema changes. The migrations an agent generates are exactly where a confused-deputy mistake becomes a data-exposure incident.
The job’s pass/fail history is your continuous-compliance evidence — point the auditor at the Actions log instead of assembling a spreadsheet by hand. That is also the answer to “our audits take just as long as before”: run the checks per PR and the quarterly audit becomes a formality.
When enterprise AI security controls break down
Section titled “When enterprise AI security controls break down”- “Telemetry is on but the SIEM is empty.”
CLAUDE_CODE_ENABLE_TELEMETRY=1must be set before the OTEL exporter variables take effect, and the collector endpoint must be reachable from the developer’s host. Test with a local collector first; a blocked egress firewall silently drops the data. uvx snyk-agent-scanreports nothing on a server you suspect. Static scans miss runtime behavior, and an over-broad MCP server can read the very files your deny list protects. Pair the scan with the sandbox controls above (no network, no filesystem scope), usedeniedMcpServers/allowedMcpServersin managed settings, and watch the server’s actual tool calls — a clean scan is necessary, not sufficient.- The secret scanner blocks every commit. High-entropy test fixtures and example keys trip
gitleaksanddetect-secrets. Generate a baseline (detect-secrets scan > .secrets.baseline) and commit it, or add a scoped.gitleaksignore— never disable the scan entirely. - Redaction mangles legitimate prompts. Aggressive regex redaction turns
user@example.comin a code sample into[REDACTED]and breaks the agent’s context. Redact in the diff and commit path, not in the prompt a developer is actively writing; prefer keeping PII out (ignore files) over scrubbing it after the fact. - The Rego gate blocks legitimate hotfixes. Build the escape hatch into the policy itself (the
MIGRATION-RISK: accepted byline), not an admin override. An override that bypasses the control is an audit finding; a documented, labeled exception is the control working. - A developer bypasses the policy. A personal account or
--dangerously-skip-permissionsmakes your committed rules irrelevant. This is why Step 1 matters: enforce Privacy Mode plus Allowed Team IDs (Cursor),disableBypassPermissionsModeplus managed settings (Claude Code), and RBAC (Codex) at the org level, not the repo level. - Cursor “audit settings” do not appear. They do not exist at the user level. If you need per-developer audit, you need the Enterprise plan’s admin surface plus host-level command logging — there is no client-side JSON for it.
- An agent leaks a secret into a prompt. This is a data-egress incident, not a code bug. The
UserPromptSubmithook and theRead(./.env*)deny rules prevent it at the source; treat any breach as reportable under your incident-response plan. - PHI slips through because “ZDR” was assumed to be a BAA. Zero Data Retention prevents training and storage; it is not a Business Associate Agreement. No BAA, no PHI — full stop.
- The security team will not approve AI tools at all. Bring the vendor’s SOC 2 report, the data processing agreement, and the zero-retention terms to the meeting rather than arguing from benefit. Enterprise plans provide contractual guarantees, and where absolute isolation is required, a local model keeps code on the machine.
- Every team encodes security requirements differently. Centralize the rule content from Step 2 in one shared repository and distribute it — a monorepo package or a Git submodule — so the standards drift in one place, not twelve.