Build2 publishers3 min readPublished
Agent token scope decided how far the Codex branch-name injection could reach
OpenAI has fixed a Critical Codex flaw in which a semicolon in a branch name leaked the agent's GitHub OAuth token on all four Codex surfaces. How far one leaked token could reach was set by its scope, which is chosen by whoever provisions the agent.
The Engineer · Build desk

What happened
- When Codex built a task container, it passed the target branch name into a shell command unsanitized, so Bash treated ;, &&, |, $() and backticks as syntax.
- The proof of concept used main plus a semicolon, then a command saving git remote get-url origin to a file; that remote URL held the token in cleartext.
- Researchers confirmed the attack could be automated to compromise multiple users who share a repository.
- OpenAI spent roughly six weeks on iterative hardening before the issue was cleared for public disclosure.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- exposure An agent holding broad organizational GitHub access turns one bad branch name into an org-wide compromise, while a token held to one branch of one repo makes the same bug an inconvenience.
- decision Teams wiring agents into delivery pipelines have to tie the credential to the task instead of the developer who configured the agent, and favour single-use tokens so a theft ends with the task.
- precedent File paths, commit messages and ticket titles carry the same risk as branch names, so any of them reaching a subprocess unsanitized is the next instance of this injection.
The exfiltration step took no exploit at all. According to the DevOps.com write-up of the Phantom Labs disclosure [1], the researchers asked Codex, in the prompt, to read the file back, and the token came out in the agent's own task output [5]. The write-up calls each wired-up agent a new privileged identity. It clones a real repository with a real GitHub credential and gets less scrutiny than a human holding the same access [9].
Git cannot do the input check for you. Ref names may contain ;, $, parentheses, &, | and backticks, so git check-ref-format accepts this payload and the harness has to reject it [14]. The dev.to piece says the narrowest fix is to stop building shell strings. If a shell can't be avoided, its advice is to permit only an allowlisted set of characters, refuse any value that resembles an option, and put quotes around what's left [16]. Its example is four lines [17]:
``` case "$BRANCH" in -* | *[!A-Za-z0-9._/-]*) echo "rejecting branch name" >&2; exit 1 ;; esac git clone --branch "$BRANCH" --single-branch "$REPO_URL" workspace ```
The first pattern refuses a name that starts with a dash and could be read as an option [16]. The second refuses any character outside letters, digits, dot, underscore, slash and hyphen, so a semicolon never reaches Bash [17]. I like this shape because it fails closed. A metacharacter nobody thought to list is refused by default [17].
Hiding the token helps less than it seems to. Moving it out of the remote URL into a credential helper or a git config header changes the file that holds it, and any command running as the agent's user can still read it [18]. The write-up says what limits the damage is the token's scope and lifetime [18].
Part of the case for tight scope comes from surveys. Teleport interviewed 205 CISOs and security architects for its 2026 State of AI in Enterprise Infrastructure Security report, and found that organizations that over-provision AI systems experience 4.5 times more security incidents than organizations that enforce least privilege [11]. Seventy percent of respondents said their AI agents get more access than a human would for the same task, and 67% still rely on static credentials for AI systems [12]. Teleport and Gravitee both sell access and security products in this space, and the dev.to author advises treating the figures as directional [13]. For the 4.5x to transfer to a given pipeline, the two groups would have to differ mainly in how they provision access. An interview sample of 205 does not establish that [11].
Pipeline operators have handled this bug class before. GitHub's guidance on hardening Actions tells workflow authors not to drop attacker-controlled values, github.head_ref or a pull request title for example, straight into run: scripts, and to route them through an environment variable instead [20]. Installation tokens for a GitHub App can be limited to chosen repositories and permissions, and they lapse after one hour [21]. On GitLab, CI_JOB_TOKEN works only for as long as its job is running, and OIDC federation replaces stored cloud keys with per-job tokens [22]. For a team already on GitHub, I'd build the agent's credential on an installation token restricted to the one repository the task names, since the scoping and the expiry already exist there [21]. Agent harnesses do not inherit any of these protections by default [23].
What to watch
- Whether OpenAI or BeyondTrust publish what the six weeks of hardening changed, in particular whether Codex task credentials are now scoped to a repo or short-lived.
- Whether other coding-agent harnesses ship task-scoped, expiring credentials by default, matching what GitHub App tokens and GitLab job tokens already do in CI.
- Disclosures of the same injection through commit messages, file paths or ticket titles in any coding agent.