Every AI Code Review Blind Spot Is the Same One

Everything a code scanner misses in AI-generated code reduces to one root cause: it sees structure, never intent, and never the running system.

Copper Sun Brass Team · · 5 min read
ai-code-reviewsecurityengineering

Everything a code scanner can’t catch in AI-generated code reduces to one root cause: the scanner sees structure, and every miss it makes lacks a structure to see. Those misses fall into exactly two piles — intent failures, where correct and incorrect code share a shape, and runtime failures, where the problem exists only in the running system. BrassCoders drew that boundary one failure at a time across seven posts; this is the whole map on a single page.

Once you see the rule, the entire frontier of what AI-code tools can and can’t do stops looking like a scattered list of gaps and starts looking like one line drawn through all of them.

The One Rule

BrassCoders is a deterministic pattern reporter: it matches shapes known to be dangerous and reports them the same way on every run. The rule that defines its edge is simple — a scanner can see the form of code but not its meaning, so any bug whose danger lives in meaning rather than form is invisible to it, by construction and not by tuning.

That single rule sorts every blind spot into two families. A bug can escape a scanner because correct and incorrect versions look identical in the source — that’s an intent failure. Or it can escape because the danger doesn’t exist in the source at all, only in the running system — a runtime failure. Nothing a scanner misses sits outside those two boxes.

The Intent Failures

BrassCoders can’t verify intent because correct and incorrect code are structurally identical, so there’s no bad shape to match — and three of the seven boundaries are exactly this failure. A logic bug is well-formed code that computes the wrong thing; a broken authorization check is a plausible check that enforces the wrong rule; a deliberate backdoor is code engineered to look like known-good code.

All three defeat pattern matching for one reason: the danger is a fact about what the code was supposed to do, and that intent isn’t written anywhere a rule can read. Whether sum(readings) / len(readings) is safe depends on whether the list can be empty; the right authorization check depends on your ownership model; a backdoor depends on the author’s purpose.

Each failure gets its own post — the logic bug, the broken authorization check, and the deliberate backdoor that mirrors the real xz-utils compromise. OWASP ranks broken access control as its #1 web risk because automated detection struggles most where application intent defines the bug.

The Runtime Failures

BrassCoders can’t catch a runtime failure because the danger never appears in the committed source — it exists only when the system runs, and four of the seven boundaries are this shape. Prompt injection arrives in untrusted content the agent reads at runtime; data leakage happens when a model emits sensitive data at inference; misconfiguration is a property of the deploy; the real bottleneck is a measured fact about a live workload.

None of these has a literal in the repository. The injected instruction rides in on a README at runtime; the leaked record flows through the model’s context, not the source; the debug flag is flipped by an environment variable at deploy; the hotspot is whichever line the profiler measures under load.

Each has its own post — prompt injection, data leakage, misconfiguration, and the performance bottleneck. OWASP tracks the runtime AI risks in its Top 10 for LLM Applications. Scan the whole codebase and there’s nothing to find, because the failure isn’t in the code.

Why This Forces the Two-Layer Model

BrassCoders treats the deterministic-plus-reasoning split as a structural fact, not a product preference — the boundary between shape and meaning is where one tool has to hand off to another. A scanner owns the shape layer completely and can’t reach past it; intent needs a reasoner and runtime needs a monitor, and no amount of rule-writing turns a pattern matcher into either.

This is the division of labor the whole product rests on. BrassCoders reports the structural findings deterministically and reproducibly; the AI assistant reading the output reasons about intent; runtime controls govern the running system. Each layer does the thing the others structurally can’t, and the failure mode of every “AI code review” tool that overpromises is claiming one layer covers all three.

What Falls on Each Side

BrassCoders catches the shape; the reasoning layer catches the intent; the runtime layer catches the behavior. Laid out as a reference, the seven boundaries and their handoffs line up in one column each.

LayerWhat it catchesWho catches it
Shape (at rest)SQL injection, weak crypto, secrets, command injection, perf anti-patternsBrassCoders (deterministic scan)
Intent (meaning)logic correctness, authorization, deliberate backdoorstests, property-based testing, LLM or human review
Runtime (behavior)prompt injection, data leakage, misconfiguration, real bottlenecksDAST, guardrails, config scanning, profilers

The map is the argument. A tool that tells you which column a given bug lives in has already told you which tool will catch it, and a scanner that claimed all three columns would be selling confidence it can’t structurally deliver.

pip install brasscoders
brasscoders --offline scan /path/to/your/project

Run the scan for the shape layer. Send the intent to a reasoner and the runtime to a monitor. One rule tells you which is which.

Frequently Asked Questions

Why does a code scanner miss so many bugs in AI-generated code?

Because a scanner matches structural patterns, and its misses all share one trait: they have no distinctive structure to match. They fall into two groups — intent failures, where correct and incorrect code look identical (a wrong logic branch, a plausible-but-wrong authorization check, a deliberate backdoor), and runtime failures, where the problem exists only in the running system (prompt injection, data leakage at inference, a misconfigured deploy, the actual performance bottleneck). One rule explains the whole frontier.

What can a static scanner actually catch, then?

Bugs that leave a structural marker: SQL injection built by string concatenation, weak cryptography like MD5, hardcoded secrets, command injection, known-bad performance shapes. Those have a form a rule can match, so a scanner catches them deterministically on every run. That's a real and valuable layer — it's just not the whole problem.

If a scanner can't catch these, what does?

Intent failures need a reviewer that reasons about what the code was supposed to do — tests, property-based testing, and an LLM or human reading for intent. Runtime failures need tools that watch the running system — DAST, output filtering, configuration scanning, and profilers. Neither is a pattern match, which is exactly why a pattern scanner can't stand in for them.

Does this mean static analysis isn't worth running on AI code?

The opposite. Running the deterministic scan first clears every bug that has a shape, so the reasoning layer — the LLM, the tests, the human — spends its attention on the intent and runtime failures that only reasoning and measurement can find. The two layers are complementary; the scan makes the expensive layer efficient.