Skip to content

Build1 publisher3 min readPublished

Four coding-agent CVEs trace to approval gates that parse commands differently from the shell

Four September 2026 CVEs in Codex CLI, Roo-Code, OpenClaw and Ollama came from approval gates that read commands differently from the shell. Ollama fixed its version by deleting the prefix parser, and gates that match exact strings and refuse unmodeled syntax close that gap by design.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying Four coding-agent CVEs trace to approval gates that parse commands differently from the shell
Generated illustration

What happened

  • Codex CLI and Desktop marked some PowerShell commands safe because their parser read the stop-parsing token --% differently from PowerShell, so a prepared repository could write files through Git.
  • Roo-Code through 3.54.0 read everything after # as a comment, so an allowlisted word, #, a separator and a denied command passed the gate and bash ran the denied part.
  • OpenClaw before 2026.8.1 trusted an approved command-running wrapper without inspecting its arguments, so a later agent turn could swap any inner command into it.
  • Ollama's experimental agent mode, from 0.14.0 until 0.31.2, did not properly parse shell syntax in bash approvals, so prompt injection could append ; or && to an approved command.
  • Ollama shipped its fix in 0.31.2 on July 6 with no security note, according to ThreatFrontier, and the CVE was published 85 days later.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • exposure An 'always allow' granted in any affected version covered more than the string the user read, so a poisoned repository or an injected prompt could reach the shell through that one click.
  • decision Harness builders have to pick between keeping a per-shell parser in step with bash and PowerShell, or refusing whatever the gate does not model; only the second removes the divergence these four bugs share.
  • cost Exact-match gates push work back to the human: in the author's demo, 5 of 7 commands either stopped for a prompt or were blocked.

Each gate had one job before the shell ran anything: decide whether this string is the one the human approved [16]. The dev.to author bobbyhalljr, who gathered the four advisories into a tutorial, traces the failure to what the gate kept. "Every bug above starts with a gate that stored something smaller than what the human saw: a program name, a prefix, a wrapper," the author wrote [6].

In three of the four, the smaller thing came from a grammar the gate only partly modelled. Codex's parser and PowerShell disagreed about `--%` [1]. In Roo-Code, the gate saw a comment after `#` where bash saw more commands [2]. A trailing `;` or `&&` rode along on an approved Ollama command because its prefix parser did not properly parse shell syntax [4][7]. OpenClaw's failure is one of scope: its policy trusted the wrapper program and did not inspect the command carried in the wrapper's arguments [3]. All four carry September dates, 28 days from first to last [1].

Making the gate parse like the shell means keeping a second copy of each shell's grammar in step with the real one. Codex's gate had a command-safety parser that handled PowerShell, and it still diverged on one token [1]. Ollama went the other way. Its fix "does not patch the prefix parser; it removes it," ThreatFrontier wrote [7]. A parser that no longer exists cannot drift from bash. The tutorial's author reached the same conclusion: "Don't build a smarter allowlist. Build a gate that refuses what it can't parse." [14]

The author's TypeScript gate does three things, in order [8]:

1. Rejects any tool argument the model should not own. `MODEL_ARGS` is `["command"]`, and every other argument belongs to the harness [9]. 2. Refuses any character it does not model [8]. 3. Splits what is left on control operators and matches every segment exactly against the approved list [8].

The approved list is plain strings: `const approved = new Set(["git status", "npm test", "timeout 60 npm test"]);` [10]. Exact matching handles the OpenClaw shape with no wrapper-specific code. `timeout` is approved only with `60 npm test` after it, so a swapped inner command produces a string that is not in the set [10]. "If the human didn't read it, the human didn't approve it," the author wrote [11].

I think step 2 is the right tradeoff for a coding-agent harness. With it, a gap in the gate's model of the shell ends in a refusal; in each of the four CVEs, a gap of that kind let a command run without approval [15].

Against a poisoned README, the tutorial's naive gate runs 7 of 7 commands, while the new gate allows 2, asks about 2 and denies 3 [12]. The naive gate is the author's composite of the four bug shapes, not any product's code, and not how any vendor fixed its bug [13]. The 7-of-7 result shows the new gate beats the bugs it was written against. It transfers to a real harness only if that harness stores prefixes, program names or wrappers as its approvals [6][13].

What to watch

  • How Codex, Roo-Code and OpenClaw fixed their gates, since the tutorial's author says their fixes were not described and Ollama is the only one known to have removed prefix matching.
  • New advisories against other agent harnesses whose approvals store program names, prefixes or wrappers, especially on PowerShell, where the Codex bypass lived.
  • Whether other agent tools turn out to have shipped security fixes silently, as Ollama did 85 days before its CVE.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories