FR
live
AI

An AI code review bot rejects a security fix by inventing a CVE that does not exist

On October 29, 2026, Docker recounted how a code review bot running locally through Ollama refused a routine PostgreSQL version bump by citing a CVE that was never issued. The lesson applies to any team wiring an AI agent into its pipeline: an agent’s output is untrusted input, to be labeled and verified as such.

A magnifying glass resting on a blank sheet of paper in a dark office, an amber glint at the edge of the lens.

October 29, 2026. Docker publishes the sixth installment of its AI Coding Agent Horror Stories series. 1,500 — the number of words in the report by which a review bot rejected a routine security fix. Zero — the number of real flaws that report described. The point that matters: a read-only agent, unable to execute anything, still nearly triggered a mass access revocation — not through its permissions, but through the authority of its prose.

The review that made everything up

A platform engineer built a code review bot for his team. The idea was simple: on every push, the bot reads the changes and writes a review into the pipeline, the same first pass a human teammate would give before a person looks at the change. He named it BuaJi — in India, the bua, your father’s sister, is the relative who inspects everything you do and always finds something wrong. The name fits.

The setup is rigorous. The bot runs on local models through Ollama: the weights stay on the engineer’s own hardware, no request ever goes out to an AI vendor, and the company’s source code never leaves the building. It runs as a CI job, triggered on every push. Above all, it gets one firm rule: read and comment, nothing more. It cannot run terraform apply, cannot run kubectl apply, and cannot push a commit. On paper, this is exactly how you would want an AI reviewer built.

A version bump that should have passed in three seconds

The change in question is as boring as it is responsible. The product ships with a bundled copy of PostgreSQL, and older published releases carry vulnerabilities with CVE numbers attached. So the engineer bumps the version:

diff
- POSTGRES_VERSION=16.11
+ POSTGRES_VERSION=16.13

A few lines, a version number, one purpose: close already-public holes. Any reviewer, human or machine, should approve it in a few seconds. There is nothing suspicious in the diff — and that is precisely what makes what happened next matter.

A convincing report, entirely false

BuaJi responded with a report of roughly 1,500 words rejecting the change. For reference, a normal review comment is a sentence or two. The report described the bump as an “architectural takeover,” a structural change smuggled into the codebase under the cover of routine maintenance. It claimed the new PostgreSQL build was a vehicle for exfiltrating internal data to a third-party company — which it named. That company does not exist.

The bot went further. It referenced specific Kubernetes resources by name, as though it had inspected the cluster — those resources had never existed, and BuaJi had no access to the cluster to look. It wrote Go scripts to “isolate the infected PostgreSQL nodes,” though nothing was infected. Finally, it told the engineer to revoke all IAM access immediately, an action that would lock out the entire organization, including the people needed to fix a real problem.

Holding the whole argument together was a CVE number that has never been issued by anyone. This is the detail to pause on: a CVE identifier is one of the easiest things in security to verify, since you can look it up in a public registry in seconds. The bot invented a fact in the one place where invented facts are easiest to catch — yet the report looked flawless: clean Markdown, syntactically valid YAML, the urgent, confident tone of a senior security engineer who has found something serious. The engineer spent two minutes reading the CI logs, realized none of it was real, and went back to work.

The trap is not permission, it is authority

It is important to say clearly: the read-only rule worked. BuaJi could not carry out any of its own recommendations, and never touched anything. The episode cost two minutes of an evening and nothing more. The problem is that the rule only controlled what the bot could do — nobody had thought about what it could say. A read-only agent is still allowed to give orders, and the person reading its output usually holds exactly the access the agent was denied.

This case is not dangerous because a permission was violated — none was. The agent stayed inside its boundary, the engineer had every right to run those commands on his own systems, and the only false thing in the entire chain was the content of the report. Now imagine a newer engineer, on call at two in the morning, half awake, reading a long, alarming security report in the pipeline. Revoking all IAM access in production in the middle of the night is an outage by itself; running an unreviewed Go script against your database nodes could be considerably worse. In both scenarios, BuaJi would have done nothing. A person would have done all of it, in good faith, because the report told them to.

An agent’s output is untrusted input

Docker’s lesson fits in one sentence: a sandbox is not a fact-checker. No microVM boundary will ever make a review comment accurate. What a boundary changes is what that fabricated output can do once it exists. Docker Sandboxes run agents inside isolated microVMs, each with its own kernel, filesystem, and a network that denies everything by default: an invention has nowhere to go, no credentials to use, no cluster to reach, until someone grants that access ahead of time. The read-only rule stops being a remembered convention and becomes a property of the environment.

But that protects you from the agent, not from the human who reads its output — and no sandbox ever will. For that half, treat an agent’s output the way you already treat its input: untrusted until proven otherwise. Concretely, label it so it does not carry a colleague’s weight, verify any CVE number it cites, and never run agent-written remediation until a human has read and understood it.

Verdict

If you wire an AI reviewer into your pipeline, treat its output the way you treat its inputs: label it untrusted and verify every CVE it cites before acting — the BuaJi case shows that an urgent, well-formatted recommendation can be entirely fabricated, and that an IAM revocation triggered by a hallucinated report is an outage all its own. If your agents must act, run them in an isolated, sandboxed microVM, where the boundary becomes a property of the system rather than a convention someone eventually forgets. And if you are the person reading an alarming AI report at three in the morning, touch nothing until you have checked the identifier against a public registry: that is the one action in this story that costs two minutes instead of a night.

References

The cyber brief, every Tuesday

The flaws that matter and the patches to apply, in a ten-minute read.

No spam. One-click unsubscribe.
read next

On the same topic

vLLM lets an EPS image disguised as PNG reach Ghostscript and run code

On October 20, 2026, vLLM published CVE-2026-115915: its ImageMediaIO loader handed incoming bytes to Pillow’s format autodetection with no allowlist, so an EPS file labeled image/png reached the host’s Ghostscript binary and could execute commands. Anyone hosting a multimodal inference server should upgrade to 0.32.0 and restrict inputs to raster formats.

Anthropic’s unreleased model chains five working exploits across Chrome, Safari, Android, Windows and Linux

On October 27, 2026, Anthropic published the details of five exploit chains built by an unreleased internal research model, capable of remote code execution on Chrome, Safari, Android, Windows and Linux with little or no user interaction. The flaws are patched: the real story is what this does to the cost of offensive research, and why defenders must use the same models to scan their own code first.

← Back to the feed

Type at least two characters.

↑ ↓ navigate ↵ open esc dismiss