Back to Blog

Every Codex Release Note Is a Security Receipt

Eli Rapoport | Backslash Research Team

-

June 23, 2026

June 23, 2026

Codex CLI is open-source, and every release note links straight to the merged pull request behind each fix. So when OpenAI closes a security-relevant gap, the note ships with the receipt: the maintainer's own account of what was wrong and why it changed. We did not have to guess.

Most security disclosures make you reconstruct what broke. Codex hands you the evidence: update and you move forward; stay pinned below the fix and you keep carrying the old behavior, with no CVE, advisory, or bulletin to tell you. We read seven weeks of stable releases and pulled the PRs behind every security-relevant line - the maintainers already wrote the disclosure for us.

This is not a "Codex is uniquely unsafe" post. Every fix below is OpenAI doing the right thing: finding a trust-boundary weakness and closing it. The point is what happens to the fleet that never updated, and what its own release notes already admit.

An agent's version is part of its control plane

A Codex CLI install is far more than a model client. It wires model reasoning into local files and shells (behind an approval layer), a sandbox with a configurable network egress policy, credentials at rest, MCP servers, plugins, hooks, app connectors, and local WebSocket listeners.

Every one of those is a boundary. When a release note says a boundary changed, the pinned version keeps the old boundary. That is the entire exposure, in one sentence.

The security fixes (the signals)

Seven weeks of stable Codex CLI releases - 0.128.0 (Apr 30, 2026) through 0.141.0 (Jun 18, 2026) - produced this set of trust-boundary fixes:

What changed in each release, and what a fleet pinned below it still carries.

Version (floor) Fix PR Boundary What a pin below this still carries
0.128.0 Stop auto-approving git -C … #20085 Approval scope git -C … was auto-approved instead of going through the approval flow
0.128.0 Network-proxy bypass/target hardening #20002, #19999, #19995 Network isolation Looser proxy bypass defaults and connect-target rechecks
0.129.0 execpolicy unwraps PowerShell -Command wrappers #20336 Command safety On Windows, prefix rules (e.g. git push) don't fire for powershell.exe -Command …-wrapped commands → policy bypass
0.131.0 Preserve managed deny_read during escalation #15977 Admin policy / read-deny Escalation, a prefix-allow, a sandbox-denial retry, or a legacy override can rebuild policy without the admin's deny-read
0.131.0 App-server WebSocket listener gains an auth guard #22404, #21843 Local WebSocket Earlier TCP WebSocket listener without the non-loopback auth guard
0.136.0 /diff no longer runs repository-selected Git helpers/hooks #24954 (PSEC-4395) Code execution A malicious repo can ship diff/clean filters, core.fsmonitor, or post-index-change hooks that execute when the user runs /diff
0.136.0 exec-server rejects WebSocket requests with an Origin header #24947 Browser → local A web page can initiate connections to the local exec-server WebSocket (DNS-rebind / CSRF-style)
0.136.0 Remote-control uses short-lived server tokens, not the ChatGPT access token #24141 Credential/token Remote-control WebSocket authenticated with the user's long-lived ChatGPT access token
0.136.0 Preserve deny_read for known-safe commands #23943 Read-deny "Safe" commands (cat, ls) run outside the filesystem sandbox and can read admin-denied paths
0.139.0 Enforce configured network proxy as the sandbox's only path #27035 Network isolation Config-driven proxy policy not consistently enforced as the sole egress in codex sandbox
0.140.0 Reworked encrypted credential backend; extends it to MCP OAuth #27504, #27535, #27539, #27541 Credential at rest MCP OAuth not on the reworked encrypted store; large keyring payloads can fail (Windows). (The keyring option itself predates 0.140.)
0.141.0 Blocking PostToolUse hooks correctly reject code-mode tool calls #28365 Hook enforcement A PostToolUse hook reports "blocked" but code mode still hands the result to the running script

The five worth dwelling on

1. /diff ran code chosen by the repository (floor 0.136.0)

This is the cleanest issue of the batch, because OpenAI classified it as a security one: PR #24954 cites internal security ticket PSEC-4395, "codex-cli /diff executes repository-selected…". In the maintainer's words:

/diff is intended to display working-tree changes, but its Git invocations honored repository-selected executable helpers. A repository could configure diff/text conversion helpers, clean/process filters, core.fsmonitor, or post-index-change hooks that execute when a user runs /diff.

Read the threat model: clone or open an untrusted repository, run /diff to review it - a perfectly normal, "I'm being careful" action - and the repository's own Git config executes code on your machine. It needs no model cooperation and no approval prompt; it is a property of how /diff shelled out to Git. Anyone pinned below 0.136.0 still has this. (We separately confirmed the underlying Git vector is real and trivially weaponizable - see the appendix.)

2. A "blocking" security hook silently didn't block (floor 0.141.0)

Hooks are a defense mechanism - a PostToolUse hook can veto a tool result. PR #28365:

Previously, a PostToolUse hook could block a completed tool result, but code mode would still return the original typed result to JavaScript. The hook appeared blocked in hook telemetry while the running script continued with the result.

That is the worst kind of control failure: it reports success while doing nothing. Anyone enforcing policy through PostToolUse hooks in code mode, pinned below 0.141.0, has a hook that lies.

3. deny_read could be dropped during escalation and by "safe" commands (floors 0.131.0 and 0.136.0)

Two PRs close two ways an administrator's read-deny could be bypassed:

  • #15977: "an explicit escalation, a prefix-rule allow, a sandbox-denial retry, or an app-server legacy sandbox override could rebuild the runtime pol[icy]" without the managed deny_read. Managed deny-read is an admin control on specific paths (think ~/.ssh, credential files). Below 0.131.0, escalation can silently re-open them.
  • #23943: treating a command as "known-safe" (or an execpolicy allow) was enough to run it outside the filesystem sandbox - so cat/ls on a deny-listed path would read it anyway. Fixed at 0.136.0.

For any team relying on Codex's managed permission profiles to keep secrets unreadable, the effective floor is 0.136.0.

4. Codex reworked encrypted credential storage in 0.140.0 - but the options predate it

Release 0.140.0 landed a four-PR credential-storage stack (#27504#27535#27539#27541). Read precisely, it does not add encryption for the first time: the credential-store settings cli_auth_credentials_store (file/keyring/auto/ephemeral) and mcp_oauth_credentials_store (auto/file/keyring) are present unchanged at least as far back as 0.137.0 - and keyring already means OS-encrypted storage. What 0.140 changes is the backend: keyring-mode CLI auth now keeps only the encryption key in the OS keyring and stores the payload in an encrypted local-secrets file (a workaround for the Windows Credential Manager 2,560-byte limit), adds auth-specific encrypted namespaces, and extends the encrypted backend to MCP OAuth credentials (#27541).

The fleet implication is therefore narrower than "no encryption below 0.140": below 0.140.0 you don't get the encrypted-local-secrets backend (large keyring payloads can fail, notably on Windows) and MCP OAuth credentials aren't on the reworked encrypted store. The plaintext-vs-encrypted choice itself is a pre-existing config setting; we did not establish the shipped default, so we make no claim that credentials are plaintext "by default" on either version.

5. The local WebSocket surfaces got real boundaries (floors 0.131.0 and 0.136.0)

Codex runs local WebSocket listeners. Two fixes harden them:

  • App-server: the TCP WebSocket listener was removed and restored only behind an auth guard for non-loopback binds (#22404, floor 0.131.0).
  • Exec-server: it "did not apply the same browser-origin request handling as the app-server" - so a browser page could reach it. #24947 adds middleware that refuses any upgrade carrying an Origin header (floor 0.136.0).

Browser-origin requests to a localhost developer service are the classic DNS-rebinding / CSRF path to local code execution; our repro demonstrates the missing guard, not a full exploit chain. Below these floors, that boundary is simply absent. We reproduced this one end-to-end: a raw WebSocket upgrade carrying Origin: <http://evil.example> is accepted (101 Switching Protocols) on 0.135.0 but refused (403 Forbidden) on 0.136.0, while a no-Origin client still upgrades on both versions (details in the appendix).

Same binary, same request, one version apart.

The Codex version-floor table

The minimum version each security guarantee requires.

If you depend on… Minimum Codex floor Because
Approval scope holding for git 0.128.0 #20085
Command policy on Windows/PowerShell 0.129.0 #20336
Managed deny_read surviving escalation 0.136.0 #15977#23943
Not executing untrusted-repo code on /diff 0.136.0 #24954 / PSEC-4395
Local WebSocket listeners refusing browsers 0.136.0 #24947
Remote-control not riding your ChatGPT token 0.136.0 #24141
Sandbox network egress actually constrained 0.139.0 #27035
MCP OAuth on the reworked encrypted credential backend 0.140.0 #27541 stack
PostToolUse hooks blocking in code mode 0.141.0 #28365

The operating model for Codex fleets

The Claude Code article asked three questions. None of them have easy answers for Codex, and that is the point - the gap lives in the space between them:

  1. What Codex versions are actually running across developer laptops, CI images, containers, and long-lived exec / app-server sessions?
  2. Which release boundaries changed security-relevant behavior after those versions? (This post is that map for the last seven weeks.)
  3. Which floor do you enforce, and how - pinning policy, image scanning, or a startup check?

Most teams can answer (1) only by hand today. (2) is changelog-plus-PR archaeology. (3) is a policy decision most teams have not made yet, because nothing forced the question.

The bottom line

Codex's release notes are not product news. They are security signals, and because the project is open-source, they come with the receipts attached. In seven weeks, Codex closed an untrusted-repo code-execution path on /diff, two ways to bypass an administrator's read-deny, a browser-reachable local WebSocket, a long-lived-token remote-control design, a reworked at-rest credential backend, and a hook that reported blocking while letting code through.

We did not just read these fixes. We ran one. On the exec-server WebSocket, a connection carrying Origin: <http://evil.example> is accepted (101 Switching Protocols) on 0.135.0 and refused (403 Forbidden) on 0.136.0: same binary, same request, one version apart: present on one, gone on the other. The whole argument, in a single test.

None of this shipped with a CVE. Every fix is a boundary the vendor already decided was wrong, still live on any fleet that has not caught up. The receipts are attached to every release. The only question left is whether anyone on your side is reading them.

Appendix: Methodology & Evidence

How this was built

  • Source of truth: the openai/codex releases. Codex has no CHANGELOG.md; the release page is the changelog, and the alpha tags are stubs - the real notes live in the stable releases.
  • Window: the 14 stable releases from 0.128.0 (Apr 30, 2026) through 0.141.0 (Jun 18, 2026) - roughly seven weeks.
  • Scan: every stable release body, filtered for security-relevant language (sandbox, network, approval, permission, credential, token, deny-read, execpolicy, MCP, websocket, escalation, proxy, traversal, redact).
  • Confirmation: for each high-value candidate we opened the merged PR and read the maintainer's own "Why / What changed." One fix links an internal OpenAI security ticket (PSEC-4395).
  • Lab: a deterministic version matrix (0.135.0-0.140.0 installed side by side) for the boundaries we can probe without credentials or network.

Evidence tiers

We separate three things on purpose, because conflating them turns a changelog reading into an overclaim:

Tier Meaning
PR-confirmed The fix and its security rationale are in the merged, public PR (sometimes a security ticket). Strong, citable evidence of what changed.
Lab-checked / reproduced We ran a deterministic, model-free probe across installed versions.
Needs credentialed/targeted repro Observing the actual before/after behavior needs real auth, a configured proxy, a crafted repo, or a browser/WebSocket client.

Every fix in the table is at least PR-confirmed. The version floors are not new exploits we discovered; they are public vendor fixes, read for their fleet implications - the same posture as the Claude Code post.

What the lab reproduced

  • Reproduced (end-to-end version delta): exec-server's Origin rejection (#24947). A raw WebSocket upgrade carrying Origin: <http://evil.example> returns 101 Switching Protocols on 0.135.0 but 403 Forbidden on 0.136.0; a no-Origin upgrade still returns 101 on both (built-in negative control). This is the actual Codex binary's behavior across the version boundary - a true credential-less before/after, not an analogue (scripts/exec-server-origin-repro.py).
  • Reproduced (mechanism level): the /diff attack vector. A throwaway repo with a diff.<driver>.textconv helper (plus a one-line .gitattributes) executes that helper the instant git diff renders the file - our sentinel fired; the same diff under a hardened --no-textconv invocation did not. This confirms the class of issue #24954 closes. It does not drive Codex's interactive /diff end-to-end (that path needs a TUI session and auth), so the Codex-specific version delta stays PR-confirmed.
  • Confirmed harness: the deterministic command-policy checker cleanly separates benign / network / command-execution / destructive probes; the sandbox reads marker files; with no proxy configured, in-sandbox curl fails closed (Could not resolve host), and a host-side negative control (example.com → HTTP 200 outside the sandbox) isolates that failure to codex sandbox, not a broken resolver.

Everything in this report is already public in OpenAI's merged PRs, so the lab was never about establishing the facts. It was about confirming the version deltas are real on the actual binary. A few boundaries (credential-at-rest, sandbox proxy enforcement, and interactive /diff) sit outside what a credential-less, network-off probe can exercise, so we left them PR-confirmed rather than chase a reproduction the published commit already settles.

Note on version floors

Floors follow the first stable release whose notes ship the fix - not the PR merge date, which can mislead. Example: #27035 merged the same day as the 0.138.0 tag, but that tag does not contain the commit (git compare shows it behind_by: 1); it first ships in 0.139.0.

Codex Security FAQ

How do we tell if we're behind on these fixes?

Map your installed version against the floor table above: each row is a security boundary and the first release that moved it. Anything below 0.136.0 is still carrying the /diff code-execution path, the read-deny bypasses, the browser-reachable WebSocket, and the old token handling; the sandbox-egress boundary moved at 0.139.0, the credential backend at 0.140.0, and PostToolUse hook enforcement at 0.141.0. The point is not a single "safe" number. It is that the gap grows quietly with every release you skip.

Does Codex CLI auto-update?

No. Codex CLI does not auto-update. It may notify you when a newer version exists, but you update manually through the channel you installed with: npm install -g @openai/codex@latest, brew upgrade codex, or by re-running the install script. A running session keeps the version it launched with, so restart to pick up an update. That is exactly how a pinned or long-lived install drifts below the floor with no signal.

How do I tell which Codex version is deployed?

codex --version on one machine. Across a fleet (laptops, CI images, containers, long-lived exec/app-server sessions) you need a version inventory; a single check misses background sessions still running their launch version.

Are any of these being actively exploited?

We have no evidence of in-the-wild exploitation, and this is not an exploit report. Every item is a public vendor fix, read for what it means for a fleet still pinned below it.

Is Codex less secure than other AI coding agents?

No. Open-source is why these fixes are this legible: the release note links the PR that explains what changed. Tools that disclose less are not safer, just quieter. The same trust-boundary classes show up across coding agents.