Security Hardening Your Codex CLI Setup

Security Hardening Your Codex CLI Setup
Codex CLI gives agents broad reach into your filesystem, shell environment, and network. That power comes with real attack surface. This article covers the practical steps for hardening a Codex deployment: locking down what the agent can touch, preventing credential leakage, capturing a tamper-evident audit trail, and enforcing policy consistently across a team via requirements.toml. It assumes you are running Codex CLI v0.23.0 or later.1
Why Hardening Matters: CVE-2025-61260
Before discussing controls, understand the threat model with a concrete example. In August 2025, Check Point Research disclosed CVE-2025-61260 (CVSS 9.8 — Critical) against Codex CLI.2 The vulnerability exploited the CLI’s trust model: if a repository contained a .env file that redirected CODEX_HOME to a project-local .codex/ directory, the CLI would automatically load and execute MCP server entries from that directory — without any approval prompt, regardless of content.
An attacker with repository write access could embed a malicious MCP command that harvested credentials, exfiltrated secrets, or installed a persistent backdoor. The attack propagated silently through supply-chain vectors: compromised starter templates, open-source repos, or CI pipelines that run codex over checked-out code.
OpenAI patched this in v0.23.0 (released 20 August 2025) by blocking .env files from silently redirecting CODEX_HOME into project directories.3 The root lesson stands: Codex’s trust is resolved at the config location, not at the config contents. Upgrade immediately, then layer the controls below.
Layer 1: Sandbox Mode and Approval Policy
Every Codex session composes two independent security axes.4
graph TD
A[Incoming prompt] --> B{approval_policy}
B -->|needs approval| C[User gate]
B -->|auto-proceed| D{sandbox_mode}
C --> D
D -->|OS allows| E[Tool execution]
D -->|OS blocks| F[Denial + log]
sandbox_mode is enforced at the OS level — not by the agent itself — so a compromised or jailbroken model cannot override it.5
| Mode | What the OS allows |
|---|---|
read-only |
File reads only; all writes and shell execution blocked |
workspace-write |
Reads and writes inside the workspace; .git/ and .codex/ remain read-only; network off by default |
danger-full-access |
No restrictions — only use with an external container boundary |
approval_policy controls when the agent pauses for confirmation:
| Policy | Behaviour |
|---|---|
untrusted |
Most restrictive; only known-safe read operations proceed without a pause |
on-request |
Agent asks before any potentially mutating action |
never |
Fully automated — appropriate only for locked-down CI environments |
granular |
Per-category overrides for sandbox, rules, MCP, skills, and permissions |
Sensible defaults for a developer machine working on untrusted or third-party code:
# ~/.codex/config.toml
sandbox_mode = "workspace-write"
approval_policy = "untrusted"
allow_login_shell = false
[sandbox_workspace_write]
network_access = false
Platform Sandboxing Implementation
The sandbox is not a conceptual boundary — it uses OS primitives:6
- macOS:
sandbox-execwith a Seatbelt policy. - Linux: Bubblewrap + seccomp (or legacy Landlock on kernels that lack Bubblewrap).
- Windows: Native sandbox in unelevated mode by default; elevated mode available for workflows that require it.
Test your sandbox locally before relying on it in CI:
codex sandbox --log-denials -- <command>
This runs <command> under the active sandbox policy and prints any denials without executing them in production.
Layer 2: Shell Environment Policy — Preventing Credential Leakage
By default, Codex inherits the full environment of the shell that launched it. In a typical developer shell that means AWS_SECRET_ACCESS_KEY, GITHUB_TOKEN, DATABASE_URL, and whatever else your dotfiles export are all visible to every subprocess the agent spawns.
The shell_environment_policy key gives you surgical control:7
[shell_environment_policy]
# Start from a clean slate rather than inheriting everything
inherit = "none"
# Glob patterns are case-insensitive; order: inherit → include_only → exclude → set
include_only = [
"PATH",
"HOME",
"TMPDIR",
"TERM",
"LANG",
"NODE_ENV",
"VIRTUAL_ENV",
]
# Explicit deny-list as a belt-and-suspenders measure
exclude = [
"*KEY*",
"*SECRET*",
"*TOKEN*",
"*PASSWORD*",
"AWS_*",
"AZURE_*",
"GCP_*",
]
# Hard-set values that override anything else
[shell_environment_policy.set]
CI = "true"
inherit = "core" is a middle ground: the CLI drops most variables but preserves PATH, HOME, TMPDIR, TERM, and LANG. Use inherit = "none" in regulated environments or when the agent will be running over untrusted repositories.
Note: The default automatic exclusion of variables matching
KEY,SECRET, orTOKENis still applied before your custominclude_only/excluderules unless you setignore_default_excludes = true.
Layer 3: Named Permissions Profiles
For finer-grained network control, define permissions profiles in config.toml and reference them as defaults or per-profile overrides:8
# Allow only the domains the agent genuinely needs
[permissions.restricted]
network.allow_domains = [
"api.openai.com",
"registry.npmjs.org",
"pypi.org",
]
network.allow_unix_sockets = ["/var/run/docker.sock"]
# Assign this profile as the default for all sandboxed tool calls
default_permissions = "restricted"
Codex supports SOCKS5 proxy configuration inside a permissions profile, enabling you to route all agent traffic through an egress proxy for logging and DLP inspection:
[permissions.corporate]
network.socks5_proxy = "socks5://proxy.corp.example.com:1080"
network.allow_domains = ["*"] # proxy handles allowlisting
Layer 4: Audit Hooks for Session Logging
Hooks execute external commands at defined points in the agent lifecycle, giving you a lightweight audit trail without requiring a full SIEM integration.9
# ~/.codex/config.toml — audit logging via hooks
[[hooks]]
event = "SessionStart"
command = ["bash", "-lc", "echo \"$(date -u +%FT%TZ) SESSION_START user=$USER cwd=$PWD\" >> /var/log/codex-audit.log"]
timeout = 5000
[[hooks]]
event = "Stop"
command = ["bash", "-lc", "echo \"$(date -u +%FT%TZ) SESSION_STOP user=$USER\" >> /var/log/codex-audit.log"]
timeout = 5000
[[hooks]]
event = "AfterToolUse"
command = ["/usr/local/bin/codex-tool-auditor"]
timeout = 10000
AfterToolUse fires after each tool call resolves, with structured JSON piped to the hook’s stdin including the tool name, arguments, and the approval decision (policy-auto vs. user-explicit).10 This gives you per-action visibility without the overhead of a full conversation transcript.
userpromptsubmit fires before a user prompt enters history, enabling you to block or sanitise inputs that match a sensitive-data pattern before the model ever sees them.11
Layer 5: OpenTelemetry for Structured Audit Logging
For regulated environments that require centralised, tamper-evident logs, Codex CLI’s [otel] configuration sends structured traces to any OTLP-compatible endpoint:12
[otel]
endpoint = "https://otel-collector.corp.example.com:4318"
# Redact prompt content from traces (recommended for PII-sensitive environments)
include_prompts = false
# Include tool approval decisions and denial events
include_tool_decisions = true
Traces include:
- Session start / stop spans with user identity and working directory.
- Per-tool spans with tool name, arguments (redacted if configured), latency, and outcome.
- Approval events: whether the decision came from policy or an explicit user confirmation.
- Sandbox denial events, annotated with the blocked syscall or network destination.
Route the collector output to your SIEM (Splunk, Elastic, Datadog) for correlation with other developer tooling events.
Layer 6: Enterprise Policy Enforcement with requirements.toml
requirements.toml is the mechanism for enforcing security baselines that individual developers cannot override.13 Unlike config.toml, requirements are constraints, not defaults — if a user attempts to set sandbox_mode = "danger-full-access" and your requirements forbid it, Codex falls back to the nearest allowed value and notifies the user.
# /etc/codex/requirements.toml (distribute via MDM or config management)
# Users may not drop below workspace-write
[requirements.sandbox_mode]
allowed = ["workspace-write", "read-only"]
# Fully automated mode forbidden; at minimum on-request
[requirements.approval_policy]
allowed = ["untrusted", "on-request", "granular"]
# Web search locked to cached results only (no live exfil)
[requirements.web_search]
allowed = ["cached", "disabled"]
# MCP server allowlist — empty list disables all MCP
[requirements.mcp_servers]
allowed = [
{ type = "stdio", command = "/usr/local/bin/codex-approved-mcp" },
{ type = "http", url = "https://mcp.internal.corp.example.com/" },
]
# Forbid destructive commands
[[requirements.rules]]
glob = "rm -rf *"
decision = "forbidden"
[[requirements.rules]]
glob = "git push --force*"
decision = "prompt"
Requirements can also be cloud-managed for ChatGPT Business and Enterprise customers: admins push policies from the Codex Policies page and they are fetched automatically at session start, eliminating the need to distribute files to developer machines via MDM.14
graph TD
A[Codex session start] --> B[Load cloud-managed requirements]
B --> C[Load /etc/codex/requirements.toml]
C --> D[Load ~/.codex/config.toml]
D --> E[Load .codex/config.toml]
E --> F{Trust check on project}
F -->|trusted| G[Apply project config]
F -->|untrusted| H[Skip project config]
G --> I[Resolve final config]
H --> I
I --> J[Enforce requirements — reject violations]
Precedence rule: Requirements win over all config layers. Cloud-managed requirements and local
requirements.tomlare merged, with the most restrictive constraint for each key winning.
Regulated Environment Checklist
For teams in regulated sectors (finance, healthcare, defence), the following baseline covers the controls above:
# /etc/codex/requirements.toml — regulated baseline
[requirements.sandbox_mode]
allowed = ["workspace-write", "read-only"]
[requirements.approval_policy]
allowed = ["untrusted", "on-request"]
[requirements.web_search]
allowed = ["disabled"]
[requirements.mcp_servers]
allowed = [] # disable all MCP unless explicitly approved
[requirements.features]
hooks = true
otel = true
[[requirements.rules]]
glob = "curl *"
decision = "prompt"
[[requirements.rules]]
glob = "wget *"
decision = "prompt"
Pair this with the developer-side config.toml:
# ~/.codex/config.toml — regulated developer machine
sandbox_mode = "workspace-write"
approval_policy = "untrusted"
allow_login_shell = false
[shell_environment_policy]
inherit = "none"
include_only = ["PATH", "HOME", "TMPDIR", "LANG"]
[otel]
endpoint = "https://otel-collector.internal:4318"
include_prompts = false
include_tool_decisions = true
[[hooks]]
event = "SessionStart"
command = ["/opt/corp/codex-audit-hook", "start"]
timeout = 5000
[[hooks]]
event = "Stop"
command = ["/opt/corp/codex-audit-hook", "stop"]
timeout = 5000
[[hooks]]
event = "AfterToolUse"
command = ["/opt/corp/codex-audit-hook", "tool"]
timeout = 10000
Cloud Environments: Secrets Isolation
When using Codex cloud (the web interface or API), secrets follow a two-phase model that differs from local CLI behaviour:15
sequenceDiagram
participant Setup as Setup phase
participant Agent as Agent phase
participant Secret as Secret store
Secret-->>Setup: Secrets decrypted and available
Setup->>Setup: Install deps, configure env
Setup-->>Agent: Secrets removed before handoff
Agent->>Agent: Runs offline, no secret access
Secrets are available only during the setup script — they are removed before the agent phase starts. This prevents an agent from exfiltrating credentials even if it is manipulated via prompt injection. For the local CLI, environment variable filtering via shell_environment_policy provides the equivalent control.
Summary
A secure Codex deployment combines multiple independent layers:
| Layer | Mechanism | Key config |
|---|---|---|
| OS sandbox | Seatbelt / Bubblewrap / Windows sandbox | sandbox_mode = "workspace-write" |
| Approval gate | Per-action human review | approval_policy = "untrusted" |
| Env isolation | Variable allowlist | shell_environment_policy.inherit = "none" |
| Network boundary | Domain allowlists / egress proxy | permissions.* profiles |
| Audit trail | Hook scripts + OTLP traces | [[hooks]] + [otel] |
| Policy enforcement | Admin-managed constraints | requirements.toml |
| Supply-chain | MCP allowlist + upgrade to ≥ v0.23.0 | requirements.mcp_servers.allowed |
No single layer is sufficient — defence in depth means an attacker who circumvents the approval gate still faces the OS sandbox, and an agent that escapes the sandbox still produces an audit trail.
Citations
-
OpenAI Codex CLI — https://github.com/openai/codex ↩
-
Check Point Research, “OpenAI Codex CLI Command Injection Vulnerability”, December 2025 — https://research.checkpoint.com/2025/openai-codex-cli-command-injection-vulnerability/ ↩
-
CyberPress, “OpenAI Codex CLI Vulnerability Lets Attackers Execute Arbitrary Commands” — https://cyberpress.org/openai-codex-cli-command-injection-vulnerability/ ↩
-
OpenAI, “Agent approvals & security” — https://developers.openai.com/codex/agent-approvals-security ↩
-
SmartScope, “OpenAI Codex CLI Setup Guide” (Updated 2026-02) — https://smartscope.blog/en/generative-ai/chatgpt/openai-codex-cli-comprehensive-guide/ ↩
-
OpenAI, “Agent approvals & security” — https://developers.openai.com/codex/agent-approvals-security ↩
-
OpenAI, “Configuration Reference” — https://developers.openai.com/codex/config-reference ↩
-
OpenAI, “Advanced Configuration” — https://developers.openai.com/codex/config-advanced ↩
-
OpenAI, “Codex CLI Features” — https://developers.openai.com/codex/cli/features ↩
-
Blake Crosley, “Codex CLI: The Definitive Technical Reference” — https://blakecrosley.com/guides/codex ↩
-
GitHub Discussion #14626,
userpromptsubmithook — https://github.com/openai/codex/discussions/2150 ↩ -
OpenAI, “Advanced Configuration” — https://developers.openai.com/codex/config-advanced ↩
-
OpenAI, “Managed configuration” — https://developers.openai.com/codex/enterprise/managed-configuration ↩
-
OpenAI, “Admin Setup” — https://developers.openai.com/codex/enterprise/admin-setup ↩
-
OpenAI, “Cloud environments” — https://developers.openai.com/codex/cloud/environments ↩