The first time a generated feature broke production for a team I worked with, nobody there had written a line of the offending function. A model produced an invoice download handler in about four seconds, the tests went green, the reviewer skimmed it on a Friday, and three weeks later a customer found they could pull anyone else's invoice by editing one number in the URL.
That story has stopped being unusual. Assistants now write a large share of what lands in release branches, and they are strong at structure, naming, and boilerplate. What they do not know is your threat model, your tenancy rules, or which storage bucket is quietly public, and those gaps look like finished work rather than defects.
So the audit has to change shape. Rather than reading for correctness the way you would review a colleague's pull request, you read for the patterns generated code keeps producing: logic that trusts whatever it is handed, secrets pasted inline, permissions granted far too wide, and authorization checks that guard one route and skip the next.
What Generated Code Gets Wrong, and Why
A model writes the most statistically ordinary solution to the prompt it was given, and ordinary is fine in a tutorial while rarely being fine in a multi-tenant application. The prompt almost never carries the context that decides security: who the user is, which records belong to them, what environment the code will run in.
The weaknesses that turn up are not exotic, which is the encouraging part. MITRE's CWE Top 25, built from the 39,080 CVE records in its most recent dataset, is dominated by the same classes you find in generated output: missing authorization, injection, improper input validation, hard-coded credentials. Most published guidance on vibe-coded app security converges on that same short list. A finite set of failure modes can be checked on purpose instead of hoped about.
It also helps to keep the downside in view. Incident response, customer notification, and the slow erosion of trust add up fast, and the real cost of digital threats usually dwarfs the review time that would have caught the problem in the first place. An hour of skeptical reading is cheap by comparison, and it gets cheaper once the team knows what to look for.
Reading the Logic Like a Skeptic
Begin by ignoring how the code reads and asking what it assumes. Generated functions are confident about their inputs, so trace every parameter from the edge of the system inward and note where validation should exist and does not. Pagination values, sort fields, file names, and anything interpolated into a query deserve individual attention.
Then look for logic that is subtly inverted. I have seen generated code treat an authenticated user as an authorized one, return a 200 on a failed lookup because the error branch was never written, and cache a response keyed only by resource id so one tenant warmed the cache for everyone. None of those produce a stack trace.
Hunting Secrets Before They Reach the Repository
Generated code loves a literal. Connection strings, API keys, signing secrets, and test tokens get written straight into the file because that is the shortest path to working software, and once committed they live in the history forever. Scan the diff, then the whole branch, then make the scan automatic so it stops depending on memory.
The UK National Cyber Security Centre's guidance on protecting a code repository puts the principle plainly: keep secret credentials logically separate from source code, define a deliberate injection process, rotate keys, and run automated scanning for keys in the repository. That advice predates the current wave of assistants and applies to it perfectly, because the failure mode has not changed, only the speed at which it arrives.
Trimming Permissions to What the Feature Actually Needs
Excessive permission is the failure that survives review most often, because broad access makes everything work and nothing complain. A generated deployment script reaches for an administrative role, and a generated service account gets write access to an entire bucket when it needed one prefix. Everything runs beautifully while the blast radius of any future mistake quietly grows.
Token scope deserves the same scrutiny. The IETF's best current practice for OAuth 2.0 security is explicit that token privileges should be held to the minimum a use case requires, audience-restricted to specific resource servers, and limited to particular resources and actions. Generated integration code tends to request the broadest scope that satisfies the compiler, so read the scope list against what the feature genuinely does and cut everything else.
Giving Authentication and Authorization Their Own Pass
Authentication usually looks correct in generated code, since login flows are well represented in training data and hard to get subtly wrong without the tests failing. Authorization is the opposite. It lives in dozens of small places, it depends on business rules the model never saw, and a missing check looks exactly like a route that did not need one.
Audit it as a matrix rather than a file. List every endpoint the change touches, list the roles your product has, and confirm for each pair whether access should be allowed and whether the code enforces it. Then test the ugly cases by hand: a valid token for the wrong tenant, an expired session, an object id belonging to someone else.
Making the Audit Part of Shipping
None of this requires a security team or a new budget line. It requires treating generated code as a draft from a fast, tireless contributor who has never read your requirements and will never mention what they were unsure about. The review you would give a new hire's first pull request is roughly the right level of suspicion.
Make the checks concrete so they survive a busy week. A short pre-release list, secret scanning in continuous integration, a standing rule that any new endpoint ships with an authorization test, and a habit of reading permission grants out loud will catch the overwhelming majority of what generated code gets wrong. Automation handles the boring half, and the skeptical read handles the half automation cannot see.
The teams I watch moving fastest with these tools are not the ones trusting them most. They are the ones who decided early what they would always verify, wrote it down, and kept verifying it while shipping twice as often as before. Speed and scrutiny turn out to be compatible, as long as the scrutiny is a process rather than a mood.

