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.
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:
- 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.