← All notes

AnalysisAct

An allowlist cannot out-parse the shell

Five ways past one coding agent's command approval gate, published on the same day. They are not five mistakes. They are one design: a gate that reads a shell command with regular expressions and hopes it reads it the way bash does.

Published
Topics
AI agent security · prompt injection · coding agents · CVE · CWE
5separate ways past one command allowlist, all from the same two finders

A coding agent proposes a shell command. Before it runs, a small piece of code decides whether to ask the human or wave it through. In Roo Code that piece of code has just collected five CVEs in a single day, and every one of them works the same way: the gate reads the command one way, the shell reads it another, and the shell wins.

Two parsers, one string

An approval gate for shell commands is, whether its authors meant it or not, a second shell parser. It has to find every command inside a string before it can check them against a list. The real parser is bash, which has decades of grammar behind it: quoting rules, substitutions, redirections, globbing, and new syntax in every release. The gate is a handful of regular expressions.

Whenever the two disagree about where a command begins or ends, an attacker gets to choose which reading the gate sees. The weakness label on four of the five records is exactly that, an interpretation conflict. It is the most honest CWE assignment we have seen this year.

Illustrative shapes, taken from the record descriptions. echo is allowlisted, X is denied
>&echo X            redirect stripped, the rest reads as echo
echo \";X;\"         an escaped quote masks the separators
echo ${ X; }         a funsub read as a parameter expansion
echo *$(X)          the substitution vanishes with the glob
$(echo X)           echo's output becomes the command

None of these needs a clever payload. Each is ordinary shell syntax that the gate simply does not model. The last one is the purest case: the gate checks the inner command, sees a harmless echo, and approves. The shell runs echo, then executes whatever echo printed.

Why the next patch would not have closed it

The code already had a dedicated check for dangerous substitutions, a hand-written list of forms to send to the human. The glob case got its own label, an incomplete list of disallowed inputs, because that is what a list of known-bad shapes always becomes. Every one of these bypasses could be patched with one more regular expression, and the patched gate would still be a partial copy of bash chasing an original that keeps moving: one of the five uses syntax that older bash releases do not even accept.

The trigger is content, not a user

The records name indirect prompt injection as the way in. Nobody types these commands. The agent reads a README, an issue or a web page carrying instructions, proposes a command shaped like the ones above, and the gate approves it because the visible prefix is on the list. Trust is the attack surface: the allowlist was trusted to describe what would run, and it only ever described what the gate managed to parse.

What to actually do

  • Turn off command auto-approval in Roo Code now. No record names a fixed version, so the setting is the only control you have. Every command then goes to a human.
  • Check every coding agent you run for the same design. If auto-approval works by matching prefixes of a shell string, assume the same class of bypass exists until shown otherwise. Forks of one codebase share one parser.
  • If you build agents, stop handing strings to a shell. Run tools as an argument vector with no shell in between, or refuse to auto-approve any command containing shell metacharacters at all. Both are cruder than a parser and both are correct.
  • Put the boundary below the agent. A container or sandbox without your credentials limits what an approved command can reach, whichever parser was fooled.

E/01What is confirmed
  • Five CVE records against Roo Code, all assigned by VulnCheck and published 13 October 2026: CVE-2026-110202 (redirect operator stripping), CVE-2026-110203 (quote masking desynchronisation), CVE-2026-110204 (funsub command substitution), CVE-2026-110205 (glob token discarding a command substitution) and CVE-2026-110206 (command substitution at command position).
  • Affected range in every record: Roo Code from 0 up to and including 3.54.0, default status unaffected. No record names a fixed version.
  • Weakness: CWE-436, interpretation conflict, on CVE-2026-110202, CVE-2026-110203, CVE-2026-110204 and CVE-2026-110206. CWE-184, incomplete list of disallowed inputs, on CVE-2026-110205.
  • Scores, identical on all five and all from VulnCheck: CVSS 3.1 base 8.8, AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, and CVSS 4.0 base 7.7, AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H.
  • Mechanism per the records: the approval code in parseCommandLine() and getCommandDecision() decides whether an agent-proposed shell command is auto-approved, and in each case it sees an allowlisted command such as echo while the shell runs a denied one. The records name indirect prompt injection as the trigger, and CVE-2026-110205 states the condition as auto-approval being enabled. CVE-2026-110204 applies on bash 5.3 or later and on ksh93.
  • CISA's ADP entry on CVE-2026-110204 (13 October 2026) records exploitation as proof of concept, automatable as no, technical impact as total. The other four records carry no ADP assessment.
  • Credited finders on all five: Minqiang Gui and Siyu Lou. NVD status on 14 October 2026: Awaiting Analysis, so NVD has published no score of its own.
  • GitHub lists the RooCodeInc/Roo-Code repository as archived, which makes it read-only. Its last release, v3.54.0 of 15 May 2026, is the top of the affected range.
E/02What we do not yet know
  • Whether a fix exists anywhere. The records list no fixed version, and an archived repository takes no changes. The finders date the archiving to 16 May 2026; GitHub's API does not expose that date, so it is their statement, not ours.
  • The write-ups name a v3.53.0 commit as the version they tested, while the records say through 3.54.0. We cite the records.
  • We reproduced none of the five. Everything here rests on the CVE records and the finders' proof-of-concept write-ups.
  • How many installations run with command auto-approval switched on, which is the configuration that matters. Nobody has published that, and we hold no telemetry: our public sensor is offline.
  • Whether forks and derivatives of Roo Code inherited the same parser. None appears in these records, and we have not audited them.