How to Audit AI-Generated Code: 5 Security Checks Your LLM Won't Do for You
Veracode's 2026 report found AI-generated code passes security checks only 56% of the time. Here's a practical audit checklist that catches what Copilot and Cursor miss.
If you’ve been using Copilot, Cursor, or any AI coding assistant for more than a few weeks, you’ve probably developed a sixth sense for its mistakes. A variable name that’s slightly off. A function that looks right but does the wrong thing. You catch those because they break your tests or look wrong in context.
Security vulnerabilities are different. They compile. They pass your linter. They look like perfectly reasonable code. In Veracode’s 2026 GenAI Code Security Report, about 44% of the coding tasks it tested produced code containing a known vulnerability when no security-specific prompting was used.
The report found that AI-generated code meets security benchmarks at a pass rate of just 56%. That number hasn’t improved from previous years, even as models have gotten demonstrably better at writing functionally correct code. LLMs are getting smarter, but they’re not getting safer.
Here’s a practical checklist for auditing AI-generated code, organized by the vulnerability categories that AI assistants miss most often.
1. Authentication and Session Management
AI models are often weak at authentication code, because secure patterns depend on details that rarely appear in a short prompt. Common gaps include missing brute-force protection, incorrect session timeout logic and weak password storage. Use the NIST SP 800-63B digital identity guidelines and the OWASP Authentication Cheat Sheet as your reference, rather than the model’s defaults.
When reviewing AI-generated authentication code, check for these three things:
Brute-force protection: Does the login endpoint have rate limiting? AI assistants frequently generate login handlers that accept unlimited attempts. Add a counter that tracks failed logins per IP or account and locks after a threshold, usually 5-10 attempts within a 15-minute window. The lockout should be temporary (15-30 minutes) rather than permanent, to avoid enabling denial-of-service attacks against legitimate users.
Session invalidation: When a user logs out, does the session actually end? AI code often calls a logout function that clears the client cookie but leaves the server-side session token valid. Verify that your logout path invalidates the session on the server, not just the client. This is especially dangerous in JWT-based systems where AI assistants commonly generate “logout” endpoints that simply delete the cookie without maintaining a token blacklist or revocation list.
Password hashing: Check that the code is using bcrypt, argon2, or scrypt, not MD5 or SHA-256 with no salt. AI models sometimes default to simpler hashing functions because those patterns appear more frequently in their training data. A quick grep for hash( or md5( in auth-related files catches most of these. While you’re there, verify that password reset tokens have expiration times and that they’re single-use — AI-generated reset flows often generate a token with no expiry and no one-time-use flag.
2. Input Validation and Injection Prevention
SQL injection sounds like a solved problem. It isn’t. AI assistants generate raw query strings with string interpolation more often than you’d expect, especially when the prompt describes a simple “get user by ID” task. The model picks the shortest path to a working function, and that path often skips parameterized queries.
For every AI-generated database interaction, verify that:
- SQL queries use parameterized statements or an ORM’s safe query builder. If you see string concatenation with user input inside a query, reject it immediately.
- Shell commands use argument arrays (
spawnwith['ls', userPath]notexec(`ls ${userPath}`)). - API endpoints validate input types, ranges, and formats before processing. AI code is notorious for generating Express routes that destructure
req.bodyand pass it directly to database operations without checking whetheruserIdis actually a number.
The fix is mechanical, not creative. You’re not judging whether the code is elegant. You’re checking whether user input touches any interpreter — SQL, shell, HTML renderer, file system — without sanitization in between. A useful habit: every time you see AI-generated code that accepts user input, trace that input through the entire function before approving. If it reaches a query, a shell command, a file path, or a response body without being validated or escaped, flag it. This single habit catches roughly a third of the security issues Veracode found in their 2026 audit.
3. Error Handling and Information Leakage
AI-generated error handlers tend to be either nonexistent or too verbose. Both are problems.
The nonexistent case is straightforward: the code calls an API, gets a response, and assumes success without checking status codes or catching exceptions. When the external service returns a 500, the application crashes or propagates undefined values downstream.
The overly verbose case is more subtle. AI assistants frequently generate error messages that include stack traces, internal file paths, database schema details, or API keys. These are gold for an attacker doing reconnaissance. In one pattern I’ve seen across multiple AI-generated codebases, the error handler logs the full exception object to the client response:
res.status(500).json({ error: err })
That err object often contains err.stack with absolute file paths, err.sql with the raw query that failed, and occasionally environment variables that leaked into the error context.
Your checklist for error handling: every catch block should log the full error server-side and return a generic message to the client. No stack traces in responses. No SQL in responses. No internal paths in responses. For Node.js backends, a one-line middleware is enough: app.use((err, req, res, next) => { console.error(err); res.status(500).json({ error: 'Internal server error' }); }). The AI will never generate this for you — it defaults to echoing the error object directly — but adding it takes thirty seconds and closes off an entire class of information leakage vulnerabilities.
4. Dependency and Supply Chain Awareness
AI models trained on code from 2023 and earlier don’t know about vulnerabilities discovered in 2026. When they suggest pip install django==4.2.0 or npm install express@4.18.2, they’re recommending versions that were current at training time but may have critical CVEs today.
This isn’t the AI’s fault. It’s a structural problem with how training data works. The model can’t know that a specific version of a library has a known exploit unless that information was in its training corpus. And even when it was, the model doesn’t weigh security updates higher than functional correctness when generating code.
The fix: every time AI generates a dependency declaration, run npm audit or pip-audit before merging. This takes seconds and catches most known vulnerabilities automatically. For Docker-based projects, add a docker scan step to catch base image vulnerabilities too. AI assistants love to generate Dockerfiles that pull node:latest without pinning a specific digest hash, which means your build is pulling whatever version happens to be current when the image builds — including any newly discovered vulnerabilities in the base OS layer.
Also watch for AI-generated import statements that pull in entire libraries for a single utility function. import lodash to use _.cloneDeep() adds hundreds of functions to your bundle, any one of which could harbor a future vulnerability. Be specific about what you import. Tree-shaking helps at build time, but it doesn’t protect you from supply chain attacks that compromise the package itself before it reaches your bundler.
5. Business Logic and Authorization Boundaries
This is the hardest category because it requires understanding your application’s rules, which the AI doesn’t have. AI assistants can’t know that only admins should be able to delete user accounts, or that a discount code should only apply to orders above a minimum amount, or that a user shouldn’t be able to access another user’s private data by changing an ID in the URL.
AI code is especially bad at authorization checks on multi-tenant systems. A common pattern: the AI generates a route like /api/projects/:id that fetches the project by ID and returns it, without checking whether the requesting user belongs to that project’s organization. The code works. The tests pass if you only test with authorized users. And it’s a data breach waiting to happen.
For every AI-generated route or function that accesses data:
- Verify there’s an authorization check between the request and the data access.
- Test with a different user’s credentials and confirm they can’t access data they shouldn’t.
- Pay special attention to batch operations. AI assistants love to generate
deleteMany({})calls that look efficient but have no scope limiting.
Business logic flaws are especially dangerous because they are largely invisible to static analysis tools, including the SAST scanners used in reports like Veracode’s. Your linter won’t catch them. Your type checker won’t catch them. Only a human reviewer who understands what the code is supposed to do — and not just whether it compiles — catches these. Broken access control tops the OWASP Top 10 for the same reason.
A useful rule of thumb for any AI coding assistant: if a route touches data that isn’t explicitly attached to the requesting user’s session, assume the AI forgot to check permissions until you have verified otherwise.
Build This Into Your Workflow
The goal isn’t to stop using AI coding assistants. The productivity gains are real, and rejecting them entirely because of security concerns is cutting off your nose to spite your face. The goal is to treat AI-generated code the way you’d treat code from a junior developer who’s brilliant but has never worked on a production system: trust the syntax, verify the security.
A practical approach: add these five checks to your PR review checklist. Not a separate security review process that nobody follows. Just five bullets in the same checklist you already use:
- Auth: rate limiting, session invalidation, password hashing correct
- Input: no user input touching SQL/shell/HTML without sanitization
- Errors: stack traces logged server-side, generic messages to client
- Dependencies:
npm audit/pip-auditclean, no wildcard imports - Authorization: every data access route checks user permissions, batch operations are scoped
Run through these on every PR with AI-generated code. Once you’ve internalized the patterns, the checks become a quick routine — far cheaper than shipping one of the vulnerabilities that Veracode’s tests found in nearly half of AI-generated outputs.
The gap between what AI can write and what it can secure isn’t closing yet. Until it does, that checklist is the difference between code that works and code that’s safe to deploy.
Reader Dispatches
No reader responses yet. Share your technical perspective below.
Related Posts
AI Coding Assistants Are Making You Worse at Your Job — Here's How to Fix That
AI coding tools save time on boilerplate but quietly erode the skills that make you valuable. A practical workflow guide for using them without losing yourself.
Build a RAG Pipeline from Scratch: Python + ChromaDB Tutorial
A complete, hands-on tutorial for building a Retrieval-Augmented Generation pipeline from scratch — from document loading and chunking to vector storage, retrieval, and answer generation with working code at every step.
Beyond the Chat: Automating Claude Code with Hooks, Skills, and MCP
Claude Code is more than a coding chatbot. Learn how to set up event-driven hooks, custom skills, and MCP integrations to turn it into an automated development workstation — with real configuration examples.