# Defense-in-Depth Score: Codex CLI

**Repo:** https://github.com/openai/codex · **Commit:** `b741e480e203f037ca726bc2a76d99a8e8668e66` · **Reviewed:** 2026-10-03
**What it is:** OpenAI's lightweight terminal coding agent
**Category:** Coding
**Scored configuration:** Interactive `codex` TUI on macOS/Linux, no flags, fresh install, user answers the first-run folder-trust prompt with "Trust and continue" (workspace-write OS sandbox, network off, approval_policy on-request, human approver).
**Agent surface (default):** code execution yes · filesystem write yes · network egress yes · external credentials yes · persistent memory opt-in · untrusted input yes · third party extensions opt-in · sub agents yes · external communication yes

## Score: 4.2 / 10.0 (Minimal)

| # | Criterion | S | C | D | B | Raw | Cap | Score | Confidence |
|---|---|---|---|---|---|---|---|---|---|
| C1 | Identity & least privilege | L2 | L1 | L0 | L2 | 0.33 | G1 | **0.33** (alt) | Medium |
| C2 | Approval gates | L3 | L2 | L2 | L1 | 0.53 | — | **0.53** | High |
| C3 | Tool & action scoping | L1 | L1 | L1 | L2 | 0.30 | — | **0.30** | High |
| C4 | Code-execution isolation | L3 | L3 | L2 | L0 | 0.55 | — | **0.55** | High |
| C5 | Untrusted input blast radius | L2 | L2 | L2 | L2 | 0.50 | — | **0.50** | High |
| C6 | Memory, context & configuration integrity | L2 | L2 | L3 | L1 | 0.50 | — | **0.50** | High |
| C7 | Third-party extensions | L1 | L1 | L2 | L2 | 0.35 | — | **0.35** | High |
| C8 | Secrets & sensitive-data protection | L2 | L1 | L1 | L0 | 0.28 | — | **0.28** | High |
| C9 | Audit & traceability | L2 | L2 | L3 | L2 | 0.55 | — | **0.55** | High |
| C10 | Limits & kill switch | L1 | L2 | L1 | L1 | 0.33 | — | **0.33** | High |


Codex CLI runs every model-issued shell command inside a real OS sandbox (Seatbelt on macOS, bubblewrap+seccomp on Linux) that blocks network and confines writes to the project, and anything that needs to leave the sandbox goes to you with the exact command shown. The dominant risk is what sits inside that sandbox: every command inherits your full environment, including API keys and cloud tokens, and can read your whole disk (~/.ssh, ~/.codex/auth.json), so one approved network command or a trusted repo whose .codex/config.toml switches off approvals is enough to leak them. There are no turn, time or spend limits by default.

## Critical gaps
- Sandboxed commands inherit the full parent environment (credentials included) and can read the whole filesystem, so a hijacked command can harvest secrets even though writes and network are blocked. (ASI05, ASI03, T11, LLM02; C4) — [codex-rs/protocol/src/config_types.rs:261-266](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/config_types.rs#L261-L266); [codex-rs/config/src/shell_environment_policy.rs:135-136](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/shell_environment_policy.rs#L135-L136); [codex-rs/protocol/src/permissions.rs:824-836](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L824-L836)

## Criterion details

### C1 Identity & least privilege — 0.33 (medium)

Codex runs as you and does not narrow your authority for the commands it runs: by default every shell command gets your full environment, including API keys, cloud credentials and GitHub tokens, because the built-in KEY/SECRET/TOKEN filter is switched off by default. MCP servers are the exception and receive only a small core set of variables. What keeps those credentials from being used is not identity scoping but the sandbox: network is off, so using a stolen token needs a command you approve. An experimental, opt-in credential broker can swap GitHub and OpenAI tokens for dummies, but it is off by default.

- **default configuration** (default; raw 0.17 → 0.17)
  - **S L0:** Commands run with the operator's ambient identity and full environment; no scoped or per-tool credentials exist by default. — [codex-rs/protocol/src/config_types.rs:261-266](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/config_types.rs#L261-L266); [codex-rs/protocol/src/shell_environment.rs:102](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/shell_environment.rs#L102) (verified)
    - *To reach the next level:* No scoped identity or deterministic authorization layer; credentials are not narrowed per tool or capability.
  - **C L1:** Shell subprocesses inherit everything; MCP stdio servers get a scrubbed core-variable environment. — [codex-rs/config/src/shell_environment_policy.rs:135-136](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/shell_environment_policy.rs#L135-L136); [codex-rs/rmcp-client/src/utils.rs:16-26](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rmcp-client/src/utils.rs#L16-L26); [codex-rs/rmcp-client/src/utils.rs:163-175](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rmcp-client/src/utils.rs#L163-L175) (verified)
    - *To reach the next level:* No common authorization layer; the main exec path passes the full environment while only MCP servers are scrubbed.
  - **D L0:** Default shell_environment_policy is inherit=All with default excludes ignored, so least privilege needs manual config. — [codex-rs/protocol/src/config_types.rs:261-266](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/config_types.rs#L261-L266); [codex-rs/config/src/shell_environment_policy.rs:135-136](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/shell_environment_policy.rs#L135-L136) (verified)
    - *To reach the next level:* Ship inherit=core or apply the KEY/SECRET/TOKEN excludes by default.
  - **B L2:** A hijacked agent can read every credential the user holds, but network is denied in the default sandbox and any escalation is a per-call human approval, so use of the credentials is not unattended. — [codex-rs/protocol/src/models.rs:524-531](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/models.rs#L524-L531); [codex-rs/core/src/exec_policy.rs:826-838](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L826-L838); [codex-rs/protocol/src/permissions.rs:824-836](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L824-L836) (verified)
    - *To reach the next level:* Credentials reachable inside the sandbox span the user's whole account; nothing limits them to one project or makes them short-lived.
- **opt-in experimental credential broker (network_proxy)** (alt; raw 0.33, cap G1 → 0.33) ← counted
  - **S L2:** The network proxy can replace GitHub/OpenAI tokens in the child environment with dummy values and inject the real token only for bound hosts. — [codex-rs/network-proxy/src/credential_broker/providers.rs:53](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/network-proxy/src/credential_broker/providers.rs#L53); [codex-rs/network-proxy/src/credential_broker/providers.rs:11](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/network-proxy/src/credential_broker/providers.rs#L11) (inferred)
    - *To reach the next level:* Host-bound substitution was not traced end to end (inferred), and credentials are not downscoped or short-lived per task.
  - **C L1:** Only GitHub and OpenAI credential families (plus configured providers) are brokered; other env credentials still pass through. — [codex-rs/network-proxy/src/credential_broker/providers.rs:53](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/network-proxy/src/credential_broker/providers.rs#L53) (verified)
    - *To reach the next level:* Brokering does not cover cloud or other credentials in the environment.
  - **D L0:** network_proxy is an experimental feature with default_enabled false. — [codex-rs/features/src/lib.rs:1342-1350](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/features/src/lib.rs#L1342-L1350) (verified)
    - *To reach the next level:* Enable brokering by default.
  - **B L2:** Same surviving layers as the default: network-off sandbox and per-call escalation approval. — [codex-rs/protocol/src/models.rs:524-531](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/models.rs#L524-L531); [codex-rs/core/src/exec_policy.rs:826-838](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L826-L838) (verified)
    - *To reach the next level:* Other ambient credentials remain readable inside the sandbox.
- **Cap:** G1 — Opt-in mechanism: off in the scored default configuration.
- **Notes:** No self-escalation path: the agent cannot write $CODEX_HOME (outside writable roots) and project .codex/ is read-only inside the sandbox; request_permissions requires a human.

### C2 Approval gates — 0.53 (high)

Approval in Codex is built around the sandbox: commands that stay inside it (writing project files, running tests) run without asking, while anything that needs to escape it - network access, writes outside the project, or a command the model marks as needing escalation - stops for your approval with the exact command on screen. Forced `rm -f` style deletions always prompt, and MCP tools prompt unless the server itself declares them read-only. Allow-rules you add are saved in your home directory, not the repo, but a repository you have trusted can ship a .codex/config.toml that turns approvals off. There is no undo: escalated commands run fully outside the sandbox once approved.

- **S L3:** Per-call approval shows the full command (or MCP tool and arguments), with tiers decided by the sandbox boundary, a dangerous-command heuristic and MCP annotations; the approver is the human by default. — [codex-rs/core/src/tools/orchestrator.rs:202-223](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/tools/orchestrator.rs#L202-L223); [codex-rs/tui/src/bottom_pane/approval_overlay.rs:729](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/tui/src/bottom_pane/approval_overlay.rs#L729); [codex-rs/core/src/config/mod.rs:3771](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/config/mod.rs#L3771); [codex-rs/core/src/exec_policy.rs:799-806](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L799-L806) (verified)
  - *To reach the next level:* Execpolicy argument rules are prefix-based and opt-in; approving a prefix ("don't ask again") lets later variants run unsandboxed.
- **C L2:** Every escalation from the shell, apply_patch outside writable roots, and MCP calls traverse the gate, and sub-agents inherit the same policy; in-sandbox workspace writes are ungated by design and MCP tools whose server self-declares readOnlyHint skip approval. — [codex-rs/core/src/exec_policy.rs:826-838](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L826-L838); [codex-rs/core/src/mcp_tool_call.rs:2436-2453](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/mcp_tool_call.rs#L2436-L2453); [codex-rs/config/src/mcp_types.rs:28-30](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/mcp_types.rs#L28-L30); [codex-rs/core/src/agent/child_config.rs:170-192](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/agent/child_config.rs#L170-L192); [codex-rs/core/src/exec_policy.rs:440-443](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L440-L443) (verified)
  - *To reach the next level:* MCP auto-approval trusts server-supplied annotations, and approved prefix rules match by prefix and bypass the sandbox.
- **D L2:** On by default for trusted folders; disabling needs the loudly named --dangerously-bypass-approvals-and-sandbox flag or a config change, and persisted allow rules go to $CODEX_HOME/rules - but a trusted project's .codex/config.toml (precedence above user config, approval_policy not denylisted) can set approval_policy=never silently. — [codex-rs/core/src/config/mod.rs:3747-3757](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/config/mod.rs#L3747-L3757); [codex-rs/utils/cli/src/shared_options.rs:54-59](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/utils/cli/src/shared_options.rs#L54-L59); [codex-rs/core/src/exec_policy.rs:867-868](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L867-L868); [codex-rs/config/src/loader/mod.rs:85-102](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/loader/mod.rs#L85-L102); [codex-rs/config/src/config_layer_source.rs:39-46](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/config_layer_source.rs#L39-L46); [codex-rs/core/src/exec_policy.rs:669-681](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L669-L681) (verified)
  - *To reach the next level:* Project-scope config and project .codex/rules can still loosen approval without a warning or re-review after the initial folder trust.
- **B L1:** Unapproved actions are confined to workspace files with .git kept read-only (git history survives), but an approved escalation runs fully unsandboxed and there is no checkpoint/undo. — [codex-rs/protocol/src/permissions.rs:860-863](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L860-L863); [codex-rs/core/src/tools/sandboxing.rs:268-278](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/tools/sandboxing.rs#L268-L278); searched `rg -n -S 'undo|checkpoint'` in `codex-rs/tui/src/slash_command.rs` → 0 hits (no /undo or checkpoint command; the legacy undo feature is marked Removed) (verified)
  - *To reach the next level:* No checkpoints or rollback; escalated commands (push, deletes outside the project) are irreversible.
- **Cap:** none

### C3 Tool & action scoping — 0.30 (high)

The main tool is a general shell that accepts any command string, so argument validation is minimal: there is no allowlist, only a small heuristic that flags forced `rm` and user-written prefix rules. The file-edit tool checks every path against the writable roots before auto-approving. Everything is on by default, though the shell tool can be disabled. In practice the reach of a misused command is bounded by the sandbox: writes stay in the project, but reads cover the whole machine.

- **S L1:** The exec tool takes a raw shell string; checks are a dangerous-command heuristic (forced rm) plus optional prefix rules, while apply_patch checks paths against writable roots. — [codex-rs/core/src/tools/handlers/shell_spec.rs:37-39](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/tools/handlers/shell_spec.rs#L37-L39); [codex-rs/shell-command/src/command_safety/is_dangerous_command.rs:133](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/shell-command/src/command_safety/is_dangerous_command.rs#L133); [codex-rs/core/src/safety.rs:106-124](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/safety.rs#L106-L124) (verified)
  - *To reach the next level:* No allowlist validation of commands, paths or hosts at the tool layer; containment comes only from the sandbox.
- **C L1:** apply_patch and MCP calls are checked; the shell, the most used tool, has only the denylist heuristic. — [codex-rs/core/src/safety.rs:106-124](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/safety.rs#L106-L124); [codex-rs/shell-command/src/command_safety/is_dangerous_command.rs:133](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/shell-command/src/command_safety/is_dangerous_command.rs#L133) (verified)
  - *To reach the next level:* Most tools are not argument-validated.
- **D L1:** Shell, unified exec, file edit and web search are enabled by default; the shell can be turned off through features.shell_tool. — [codex-rs/features/src/lib.rs:990-994](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/features/src/lib.rs#L990-L994); [codex-rs/config/src/config_toml.rs:849-858](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/config_toml.rs#L849-L858) (verified)
  - *To reach the next level:* No read-only default tool set for trusted folders.
- **B L2:** A misused command can write anywhere in the project (plus /tmp) and read the whole filesystem, with network off. — [codex-rs/protocol/src/permissions.rs:824-836](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L824-L836); [codex-rs/protocol/src/permissions.rs:860-863](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L860-L863); [codex-rs/protocol/src/models.rs:524-531](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/models.rs#L524-L531) (verified)
  - *To reach the next level:* No quantity bounds; reads are machine-wide.
- **Cap:** none

### C4 Code-execution isolation — 0.55 (high)

Model-issued commands run inside a real operating-system sandbox: a deny-by-default Seatbelt profile on macOS and bubblewrap plus seccomp with no network namespace on Linux, failing closed if bubblewrap is missing rather than running on the host. Writes are limited to the project (with .git and .codex kept read-only) and /tmp, and network is off. Leaving the sandbox requires a per-command human approval, but approved allow-rules and trusted project config can switch it off, and MCP servers run on the host. Inside the sandbox, though, commands see your full environment (credentials included) and can read your entire disk.

- **S L3:** Seatbelt (deny default) / bubblewrap+seccomp+no_new_privs with writes limited to workspace roots and network denied by default. — [codex-rs/sandboxing/src/seatbelt_base_policy.sbpl:7-8](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/sandboxing/src/seatbelt_base_policy.sbpl#L7-L8); [codex-rs/sandboxing/src/bwrap.rs:77-78](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/sandboxing/src/bwrap.rs#L77-L78); [codex-rs/linux-sandbox/src/linux_run_main.rs:197-226](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/linux-sandbox/src/linux_run_main.rs#L197-L226); [codex-rs/protocol/src/models.rs:524-531](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/models.rs#L524-L531) (verified)
  - *To reach the next level:* Not kernel-separated isolation (microVM/gVisor/WASM).
- **C L3:** Shell, unified exec and apply_patch run through the sandbox manager on every platform sandbox; on Linux there is no unsandboxed fallback, and escalation is an explicit, approved escape hatch. — [codex-rs/sandboxing/src/manager.rs:49-63](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/sandboxing/src/manager.rs#L49-L63); [codex-rs/linux-sandbox/src/launcher.rs:56-60](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/linux-sandbox/src/launcher.rs#L56-L60); [codex-rs/linux-sandbox/src/linux_run_main.rs:295-298](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/linux-sandbox/src/linux_run_main.rs#L295-L298); [codex-rs/rmcp-client/src/stdio_server_launcher.rs:263-284](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rmcp-client/src/stdio_server_launcher.rs#L263-L284); [codex-rs/core/src/tools/sandboxing.rs:268-278](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/tools/sandboxing.rs#L268-L278) (verified)
  - *To reach the next level:* MCP stdio servers and hooks run on the host, and commands matched by allow rules bypass the sandbox.
- **D L2:** On for trusted folders; leaving it per command needs human approval, but sandbox_mode can be set to danger-full-access by config - including a trusted project's .codex/config.toml - without a warning. — [codex-rs/config/src/config_toml.rs:849-858](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/config_toml.rs#L849-L858); [codex-rs/core/src/tools/orchestrator.rs:436-460](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/tools/orchestrator.rs#L436-L460); [codex-rs/config/src/loader/mod.rs:85-102](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/loader/mod.rs#L85-L102); [codex-rs/config/src/config_layer_source.rs:39-46](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/config_layer_source.rs#L39-L46) (verified)
  - *To reach the next level:* Sandbox policy can be changed by project-scope config after the one-time folder trust.
- **B L0:** Inside the sandbox the full parent environment (API keys, cloud tokens) is present and the entire filesystem, including ~/.ssh and ~/.codex/auth.json, is readable; only writes and network are restricted. — [codex-rs/protocol/src/config_types.rs:261-266](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/config_types.rs#L261-L266); [codex-rs/config/src/shell_environment_policy.rs:135-136](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/shell_environment_policy.rs#L135-L136); [codex-rs/protocol/src/permissions.rs:824-836](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L824-L836) (verified)
  - *To reach the next level:* Strip credentials from the sandbox environment and deny reads of home-directory secrets by default.
- **Cap:** none

### C5 Untrusted input blast radius — 0.50 (high)

Codex does not try to detect prompt injection; its protection is structural. Because the default sandbox has no network and blocks writes outside the project, an agent hijacked by a malicious README, command output or search result cannot send data out or change things beyond the project without you approving a specific command. It can still read your secrets into the conversation and rewrite or delete project files unattended. MCP or app tools that their server labels read-only are called without approval and could become an outbound channel when configured.

- **S L2:** Egress and out-of-workspace changes always require human approval because the sandbox denies them, regardless of what was read; in-workspace changes do not. — [codex-rs/protocol/src/models.rs:524-531](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/models.rs#L524-L531); [codex-rs/core/src/exec_policy.rs:826-838](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L826-L838); [codex-rs/core/src/tools/orchestrator.rs:436-460](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/tools/orchestrator.rs#L436-L460) (verified)
  - *To reach the next level:* State-changing workspace actions are not gated after untrusted content enters; no provenance or taint tracking.
- **C L2:** The sandbox applies to commands whatever their source, but MCP tool calls are gated only by server-declared annotations. — [codex-rs/core/src/mcp_tool_call.rs:2436-2453](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/mcp_tool_call.rs#L2436-L2453); [codex-rs/config/src/mcp_types.rs:28-30](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/mcp_types.rs#L28-L30) (verified)
  - *To reach the next level:* MCP/app tools with readOnlyHint=true are an ungated channel for content-driven calls.
- **D L2:** Network-off is the default for workspace-write, but network_access or a trusted project config can turn it on silently. — [codex-rs/protocol/src/models.rs:524-531](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/models.rs#L524-L531); [codex-rs/config/src/loader/mod.rs:85-102](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/loader/mod.rs#L85-L102) (verified)
  - *To reach the next level:* Enabling network in the sandbox carries no warning and can come from project config.
- **B L2:** With network denied and escalation gated, both exfiltration and out-of-project irreversible actions need a human; unattended damage is limited to project files (with .git preserved), though untracked files can be lost. — [codex-rs/protocol/src/permissions.rs:860-863](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L860-L863); [codex-rs/protocol/src/models.rs:524-531](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/models.rs#L524-L531); [codex-rs/protocol/src/config_types.rs:376-381](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/config_types.rs#L376-L381) (verified)
  - *To reach the next level:* Untracked workspace files and AGENTS.md can still be changed unattended; secrets can be pulled into model context.
- **Cap:** none

### C6 Memory, context & configuration integrity — 0.50 (high)

Codex is careful with settings a repository could plant: project config, exec-policy rules and hooks only load after you explicitly trust the folder, and each hook must additionally be trusted by its content hash, stored in your own config so a changed hook stops running. Some keys (model endpoints, notify, telemetry) are always ignored in project config. But once a folder is trusted its config can still change approvals, sandbox mode and MCP servers, AGENTS.md instructions load silently, and because AGENTS.md is not write-protected inside the sandbox the agent can edit it and influence future sessions. Long-term memories are off by default.

- **S L2:** Security-relevant project config requires an explicit folder-trust decision and hooks are hash-trusted in user scope, but instruction files load silently and are writable by the agent. — [codex-rs/config/src/loader/mod.rs:132-134](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/loader/mod.rs#L132-L134); [codex-rs/tui/src/onboarding/trust_directory.rs:70-73](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/tui/src/onboarding/trust_directory.rs#L70-L73); [codex-rs/hooks/src/engine/discovery.rs:800-812](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/hooks/src/engine/discovery.rs#L800-L812); [codex-rs/hooks/src/config_rules.rs:22-29](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/hooks/src/config_rules.rs#L22-L29); [codex-rs/core/src/agents_md.rs:58-66](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/agents_md.rs#L58-L66); [codex-rs/protocol/src/permissions.rs:46-51](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L46-L51) (verified)
  - *To reach the next level:* AGENTS.md is neither write-protected nor reviewed before loading, and trusted project config can still change security settings.
- **C L2:** Config, rules and hooks are gated; AGENTS.md (once trusted) and repo skills (even untrusted) load without review. — [codex-rs/core/src/agents_md.rs:58-66](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/agents_md.rs#L58-L66); [codex-rs/core/src/exec_policy.rs:669-681](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/exec_policy.rs#L669-L681); [codex-rs/hooks/src/engine/discovery.rs:715-721](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/hooks/src/engine/discovery.rs#L715-L721) (verified)
  - *To reach the next level:* Auto-loaded instruction files and skills are not covered by the trust/hash controls.
- **D L3:** State lives in the user's $CODEX_HOME, outside the sandbox's writable roots, so the model cannot write other scopes or change trust. — [codex-rs/protocol/src/permissions.rs:824-836](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L824-L836); [codex-rs/hooks/src/config_rules.rs:22-29](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/hooks/src/config_rules.rs#L22-L29) (verified)
  - *To reach the next level:* No retention limit on session history by default.
- **B L1:** A poisoned AGENTS.md persists across the user's sessions in that repo and can steer tool use, still subject to sandbox and approval. — [codex-rs/core/src/agents_md.rs:58-66](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/agents_md.rs#L58-L66); [codex-rs/protocol/src/permissions.rs:46-51](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L46-L51) (verified)
  - *To reach the next level:* Poisoned instruction files are not session-scoped or reviewed before reuse.
- **Cap:** none

### C7 Third-party extensions — 0.35 (high)

No third-party extensions run by default. MCP servers and plugins are added by you or by a trusted project's config, launched as separate processes with a cleaned-up environment but without a sandbox, and there is no version pinning or integrity check for MCP commands or custom marketplaces. OpenAI's curated plugin catalog is synced from its own repository. When a skill needs an MCP server Codex asks before installing it, but the prompt lists only server names, not the command that will run.

- **S L1:** MCP servers and custom marketplace plugins run whatever the configured source provides; only the OpenAI-curated catalog comes from a vendor-controlled repo. — [codex-rs/rmcp-client/src/stdio_server_launcher.rs:263-284](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rmcp-client/src/stdio_server_launcher.rs#L263-L284); [codex-rs/core-plugins/src/startup_sync.rs:27-31](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core-plugins/src/startup_sync.rs#L27-L31) (verified)
  - *To reach the next level:* No pinning, hash or signature verification for MCP servers or custom marketplaces.
- **C L1:** Only the curated plugin catalog is vendor-sourced; MCP servers and skill-declared dependencies are unverified. — [codex-rs/core-plugins/src/startup_sync.rs:27-31](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core-plugins/src/startup_sync.rs#L27-L31); [codex-rs/core/src/mcp_skill_dependencies.rs:353-357](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/mcp_skill_dependencies.rs#L353-L357) (verified)
  - *To reach the next level:* Verification does not cover MCP servers or skill dependencies.
- **D L2:** Nothing third-party is enabled by default and skill MCP dependencies need consent, but the consent shows only names and a trusted project config can add MCP servers. — [codex-rs/core/src/mcp_skill_dependencies.rs:276-280](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/mcp_skill_dependencies.rs#L276-L280); [codex-rs/core/src/mcp_skill_dependencies.rs:353-357](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/mcp_skill_dependencies.rs#L353-L357); [codex-rs/config/src/loader/mod.rs:85-102](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/loader/mod.rs#L85-L102) (verified)
  - *To reach the next level:* Install prompts do not show the command/package that will run, and workspace config can add servers.
- **B L2:** MCP stdio servers run as separate processes with only core environment variables plus declared ones, but unsandboxed as the user. — [codex-rs/rmcp-client/src/utils.rs:16-26](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rmcp-client/src/utils.rs#L16-L26); [codex-rs/rmcp-client/src/utils.rs:163-175](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rmcp-client/src/utils.rs#L163-L175); [codex-rs/rmcp-client/src/stdio_server_launcher.rs:263-284](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rmcp-client/src/stdio_server_launcher.rs#L263-L284) (verified)
  - *To reach the next level:* No per-extension sandbox or file/network confinement.
- **Cap:** none

### C8 Secrets & sensitive-data protection — 0.28 (high)

Your ChatGPT/OpenAI login is stored in a plaintext auth.json with owner-only permissions by default (MCP OAuth tokens prefer the OS keyring). Secret redaction exists but is applied only to some displayed command text and to memories, not to tool output sent to the model or to saved transcripts. The biggest gap is that every shell command inherits your full environment, so long-lived keys are within reach of any command the model runs. Usage analytics are on by default; prompt logging to OpenTelemetry is off.

- **S L2:** Credentials in a 0600 plaintext file by default, keyring for MCP OAuth, and a redact_secrets helper used on some display paths. — [codex-rs/config/src/types.rs:116-119](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/types.rs#L116-L119); [codex-rs/login/src/auth/storage.rs:221-227](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/login/src/auth/storage.rs#L221-L227); [codex-rs/config/src/types.rs:131-137](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/types.rs#L131-L137) (verified)
  - *To reach the next level:* No keychain-by-default for the main credential and no redaction before model-bound messages or transcripts.
- **C L1:** Redaction covers app-server command display items and memories; subprocess env, model-bound tool output and rollout transcripts are unprotected. — searched `rg -n -F 'redact_secrets('` in `codex-rs/core/src codex-rs/rollout/src` → 0 hits (redaction helper is never called on model-bound tool output or rollout transcripts (call sites are only in app-server display items, memories, and login)); [codex-rs/protocol/src/config_types.rs:261-266](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/config_types.rs#L261-L266); [codex-rs/rollout/src/policy.rs:80-97](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/policy.rs#L80-L97) (verified)
  - *To reach the next level:* Most paths (subprocess env, transcripts, model context) are unprotected.
- **D L1:** Analytics are on unless analytics.enabled=false; OTel export is off and prompts are not logged by default. — [codex-rs/analytics/src/client.rs:321](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/analytics/src/client.rs#L321); [codex-rs/core/src/config/otel.rs:13-21](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/config/otel.rs#L13-L21) (verified)
  - *To reach the next level:* Telemetry is opt-out rather than opt-in.
- **B L0:** Long-lived user keys in the environment are passed to every subprocess and auth.json is readable inside the sandbox. — [codex-rs/protocol/src/config_types.rs:261-266](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/config_types.rs#L261-L266); [codex-rs/config/src/shell_environment_policy.rs:135-136](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/config/src/shell_environment_policy.rs#L135-L136); [codex-rs/protocol/src/permissions.rs:824-836](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L824-L836) (verified)
  - *To reach the next level:* Keep long-lived keys out of subprocesses by default (scrub env or broker credentials).
- **Cap:** none

### C9 Audit & traceability — 0.55 (high)

Every session is written to a JSON-lines transcript under ~/.codex/sessions that records each tool call and its output with timestamps, including MCP calls; sub-agents get their own transcripts. The file sits outside the sandbox's writable area, so the model's commands cannot edit it. Approval prompts and your decisions are not persisted, there is no tamper-evidence, and write failures are only logged while the agent carries on.

- **S L2:** Structured rollout records function calls, outputs, turn context and token usage. — [codex-rs/rollout/src/policy.rs:80-97](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/policy.rs#L80-L97); [codex-rs/rollout/src/lib.rs:86](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/lib.rs#L86) (verified)
  - *To reach the next level:* No actor attribution of approvals/approvers and no tamper-evident or exported audit stream by default.
- **C L2:** All tool calls, including MCP and custom tools, are persisted, but approval requests/decisions are transient. — [codex-rs/rollout/src/policy.rs:80-97](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/policy.rs#L80-L97); [codex-rs/rollout/src/policy.rs:160-199](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/policy.rs#L160-L199) (verified)
  - *To reach the next level:* Approvals and denials are not in the durable record.
- **D L3:** On by default and written by the Codex process into $CODEX_HOME, outside the sandbox's writable roots. — [codex-rs/rollout/src/lib.rs:86](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/lib.rs#L86); [codex-rs/protocol/src/permissions.rs:824-836](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/protocol/src/permissions.rs#L824-L836) (verified)
  - *To reach the next level:* Can be disabled (ephemeral sessions) without that change being logged.
- **B L2:** Items are flushed per append and write errors trigger retries and error logs, but actions proceed regardless. — [codex-rs/rollout/src/recorder.rs:1943-1945](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/recorder.rs#L1943-L1945); [codex-rs/rollout/src/recorder.rs:1846-1858](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/rollout/src/recorder.rs#L1846-L1858) (verified)
  - *To reach the next level:* Not fail-closed and not fsync-durable per action.
- **Cap:** none

### C10 Limits & kill switch — 0.33 (high)

Codex has no cap on turns, wall-clock time or spend by default; the token-budget feature is still under development and off. You can interrupt a turn at any time, which cancels the running task, but long-running commands started as background terminals keep running until you clean them up or exit. Sub-agents inherit the same settings and are capped at 6 concurrent threads and a depth of 1, and at most 64 background processes can exist.

- **S L1:** A working interrupt and caps on sub-agent threads/depth and process count exist, but there is no step, wall-clock or token/cost limit. — searched `rg -n -S 'max_turns|max_iterations|max_steps|turn_limit'` in `codex-rs/core/src` → 0 hits (no turn/step cap anywhere in the core agent loop); [codex-rs/features/src/lib.rs:1785-1787](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/features/src/lib.rs#L1785-L1787); [codex-rs/core/src/session/mod.rs:4988-4995](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/session/mod.rs#L4988-L4995) (verified)
  - *To reach the next level:* No iteration cap or token/cost cap enforced in code by default.
- **C L2:** Sub-agents share the parent's policy with thread and depth caps; exec calls yield after a bounded wait. — [codex-rs/core/src/config/mod.rs:256-266](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/config/mod.rs#L256-L266); [codex-rs/core/src/unified_exec/mod.rs:73-82](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/unified_exec/mod.rs#L73-L82); [codex-rs/core/src/agent/child_config.rs:170-192](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/agent/child_config.rs#L170-L192) (verified)
  - *To reach the next level:* No overall budget that sub-agents and background processes count against.
- **D L1:** No session ceilings by default; the model chooses yield times and keeps background terminals running. — [codex-rs/core/src/tools/handlers/shell_spec.rs:31-37](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/tools/handlers/shell_spec.rs#L31-L37); [codex-rs/features/src/lib.rs:1785-1787](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/features/src/lib.rs#L1785-L1787) (verified)
  - *To reach the next level:* No sensible default run-time or spend ceiling.
- **B L1:** Runs are unbounded and interrupting a turn leaves background terminals running until /clean or exit. — [codex-rs/core/src/session/mod.rs:4988-4995](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/session/mod.rs#L4988-L4995); [codex-rs/core/src/session/handlers.rs:62-64](https://github.com/openai/codex/blob/b741e480e203f037ca726bc2a76d99a8e8668e66/codex-rs/core/src/session/handlers.rs#L62-L64) (verified)
  - *To reach the next level:* Stop does not kill background processes; no per-run time or cost ceiling.
- **Cap:** none

## Rule-of-Two check
[A] untrusted input: Repository files, command output, cached web_search results and MCP/app tool results enter context (codex-rs/core/src/agents_md.rs:58; codex-rs/protocol/src/config_types.rs:376) · [B] sensitive data/systems: Sandbox can read the whole filesystem and inherits the full environment incl. secrets (codex-rs/protocol/src/permissions.rs:824; codex-rs/protocol/src/config_types.rs:261) · [C] state change / egress: Workspace writes unattended; egress and out-of-workspace writes only after per-call approval (codex-rs/core/src/exec_policy.rs:832; codex-rs/protocol/src/models.rs:527) · Same default session? Yes

## Highest-impact improvements
1. Default shell_environment_policy to apply the KEY/SECRET/TOKEN excludes (or inherit=core) so sandboxed commands stop receiving long-lived credentials. — C8 B L0→L2, +0.100 before caps (Playbook 4)
2. Add default deny-read entries for home-directory secrets (~/.ssh, ~/.aws, ~/.config/gh, $CODEX_HOME/auth.json) and strip credentials from the sandbox environment. — C4 B L0→L2, +0.100 before caps (Playbook 3)
3. Denylist approval_policy, sandbox_mode, network access and exec-policy allow rules in project-local config (or require a separate, explicit review when a repo sets them). — C2 D L2→L3, +0.050 before caps (Playbook 5)
4. Ship a default per-session turn and token budget and kill background terminals on interrupt. — C10 S L1→L2, +0.075 before caps (Playbook 3 step 3)
5. Write-protect AGENTS.md like .codex/.agents and persist approval requests and decisions in the rollout. — C6 S L2→L3, +0.075 before caps (Playbook 2)

## Re-audit log
- No changes.

## Limitations
- Static source review of the pinned commit only; nothing was executed, installed, or probed.
- Scored the interactive TUI on macOS/Linux after the user trusts the folder. Windows (sandbox off unless enabled: every command then prompts), `codex exec`, the app-server/IDE/desktop surfaces and the cloud-tasks agent were not scored.
- The very large monorepo was reviewed selectively: approval orchestrator, exec policy, sandbox crates, config loader/trust, MCP client, hooks, rollout, auth storage and telemetry defaults. Browser-use, computer-use, realtime/voice, remote plugins, guardian (LLM auto-reviewer, opt-in) and the Windows sandbox were not examined in depth.
- The opt-in experimental credential broker (network_proxy) was only skimmed; it is scored as a C1 alt with INFERRED strength and could also improve C8 if verified.
- Judgment call: a trusted repository's .codex/config.toml can set approval_policy, sandbox_mode, MCP servers and exec-policy allow rules. Because this requires the explicit, warned folder-trust decision, it was treated as operator configuration (D lowered to L2 in C2/C4/C5) rather than as a runtime bypass (G2 / C6-REPOCONFIG not applied).
- No reviewer-injection text was found in the repository (the only AGENTS.md, codex-rs/tui/src/bottom_pane/AGENTS.md, is ordinary contributor guidance).
