The Spec Is the New Attack Surface

Written by: 
Pixee
Published on: 
Aug 26, 2026
An abstract specification sheet where one requirement line is missing, the empty slot glowing as the flaw
On This Page
Share:

When AI writes your code from a spec, spec-driven development security becomes the risk your scanners can't see.

By Pixee

Every security team we talk to still reviews code. And if we're being honest, fewer and fewer of us read the code as closely before it merges either.

Given that reality — Menlo Ventures reports AI coding is the largest enterprise AI spending category and 90% of devs report they use it daily — the question shifts to where and how we inject security into the SDLC to maximize both human and agentic leverage.

The spec became the primary artifact

When a person wrote every line, the code was the source of truth for what the software was supposed to do. You could read it and know.

When an agent writes the code from a prompt, a ticket, or a design doc, that intent document becomes the primary source of truth from which the rest of the execution flows. In other words, when humans interact less with code, specifications become the primary development artifact. That's the primary finding in Berkeley's Center for Responsible Decentralized Intelligence paper, and the fundamental shift as we move through their levels of code autonomy.

Berkeley RDI's levels of code autonomy, from AI-assisted work today to Level III demand autonomy, across requirement curation, system design, implementation, testing and auditing, and deployment and integration. Berkeley RDI's levels of code autonomy, from today's AI-assisted work to full demand autonomy. Source: Berkeley RDI.

It's not just researchers at Berkeley coalescing on this idea. OpenAI's Sean Grove makes the same point in this fascinating 20-minute talk, where he argues for focusing on the spec so you can "write once, run everywhere." His fundamental argument is that code is a lossy projection of intent, while the spec is a purer capture of that intent.

You already live this, even if nobody on your team says the word "spec." The Jira ticket, the Linear epic, the Confluence design doc, the Slack thread that kicked off the change. That is the spec. It is now the security artifact that most determines what your software actually does, and almost no one is reviewing it that way.

Whatever the spec leaves out, the agent fills in

AI doesn't so much turn your spec into code as it turns your assumptions into shipped behavior. Every question a careful developer would have stopped to ask, the agent answers on its own, using whatever is statistically normal in the training data and the surrounding codebase.

Take a one-line ticket: "let admins export account activity as CSV." A careful engineer stops to ask who actually counts as an admin, which fields belong in the export, and whether it needs a tenant check or an audit trail. The agent usually doesn't ask. It builds an export that includes session metadata and PII, opens it to the "manager" role because the surrounding code loosely treats managers as admins, and never writes an audit event because the ticket didn't mention one.

No scanner screams. The tests pass. The spec was satisfied. And the export ships with customer data exposed, because nothing said it shouldn't.

So the attack surface is everything the ticket left undecided. The agent settles those questions on its own, dozens per change, and no one reviews its answers as security decisions. It compounds, too. Research on iterative AI code generation found critical vulnerabilities rose about 37.6% after five rounds of "improve this code," because each pass optimizes for the instruction in front of it, not the intent behind it.

Why your current stack can't see it

The deeper problem is that your scanners read code, not intent. A SAST tool will flag a query built through string concatenation. It has no way to flag that opening the CSV export to managers was a decision no one approved. That gap between what you intended and what the agent decided is the part a scanner can never see.

The tempting fix is to let the coding agent check its own work. When one agent writes both the implementation and its tests, they share the same blind spots, so passing tests really only show that the code agrees with itself. That is Berkeley's point: passing tests may demonstrate consistency, not correctness.

The benchmarks bear it out. On BaxBench, even the strongest models produce backends that are both correct and secure only about 37% of the time, and of the programs that do run, roughly half are exploitable.

The thing writing the code can't be the only thing validating it. Create, find, and fix have to be separate by design.

What securing the spec actually looks like

So what do you do about an artifact your tools don't read and your agents can't grade? There are three basic categories to focus on.

Review intent at design time. Read the change while it's still a design decision, before the code exists, and catch the security promise the spec is silent on. This is the architecture review your best people already do in their heads, done at the speed agents now ship.

Keep the threat model alive. A threat model you write by hand is stale the next time someone ships. Generate it from the code instead, and regenerate it on every change, so it always reflects how the app is exposed today, where the trust boundaries are, and which data matters. (We wrote more about why a static threat model is stale the day you save it if you want the longer version.) Take the identical SQL injection in two apps. In one it sits behind three internal services and touches no regulated data. In the other it's one hop from a public endpoint and reads customer PII. Same finding, opposite priority, and the only thing that tells them apart is the threat model.

Watch for drift. Compare what shipped against what the spec promised, and flag where the two have separated, so the gap surfaces as a finding instead of an incident.

This is the problem we built Foresight for

The three moves above are exactly what Pixee's Foresight does.

Foresight reads a change while it's still a design. It works from the PRD, the ticket, the diff description, and figures out the security promises the design is making, the ones it stays silent on, and where the two come apart.

It generates a threat model from the actual code and keeps it current as the code moves, so a reviewer, human or agent, can see how the app is exposed, where the trust boundaries sit, and which data matters. Point it at the CSV-export change from earlier and it raises the questions the ticket never answered: who actually reaches this endpoint, what data it returns, whether the tenant check is there. It surfaces the assumptions before they ship as behavior.

Foresight runs on the same context graph as the reactive side of Pixee, the side that triages which findings from your existing scanners are genuinely exploitable and ships fixes your developers merge. Design-stage and post-detection feed each other. What we learn fixing a vulnerability becomes something the next design review looks for, and what a review flags becomes context the triage side can use.

Foresight is generally available today. It won't make your spec perfect, and it won't replace the engineer who knows the business reason a boundary exists. What it gives that engineer, and the agents working beside them, is the one thing a scanner never could: a read on intent, checked independently of the model that wrote the code.

See it on your own code →

Weekly Intel

AppSec Weekly

The briefing security leaders actually read. CVEs, tooling shifts, and remediation trends — every week in 5 minutes.

Weekly only. No spam. Unsubscribe anytime.