How to Catch AI Coding Agent Security Vulnerabilities
AI coding agents write insecure code more often than most teams expect. Here is how to catch the vulnerabilities before they merge, with a practical checklist and a pasteable review prompt.
How to Catch AI Coding Agent Security Vulnerabilities
AI coding agents write vulnerable code more often than most teams expect, and the gap is not small. Independent testing puts the failure rate at nearly half of generated samples on basic security checks. Catching an AI coding agent introducing a security vulnerability comes down to three habits: knowing which bugs it tends to write, reviewing the diff the way you would a new hire's first pull request, and running automated scanners as a backstop rather than a replacement for reading the code. This guide covers all three, with a pasteable review instruction you can drop straight into a repo.
Why AI coding agents keep writing the same security bugs
Large language models learn from public code, and public code is full of security mistakes. Veracode's 2025 GenAI Code Security Report tested more than 100 large language models across 80 coding tasks and found that 45 percent of AI-generated code samples failed basic security checks tied to the OWASP Top 10. The failure rate was not spread evenly across vulnerability types. Models passed SQL injection tests 80 percent of the time and cryptographic-handling tests 86 percent of the time, but passed cross-site scripting tests only 14 percent of the time and log injection tests just 12 percent of the time.
Language mattered too, based on the same report:
Language | Security pass rate |
|---|---|
Python | 62% |
JavaScript | 57% |
C# | 55% |
Java | 29% |
Java came out worst, largely because so much of its training data predates modern security conventions. The exact numbers matter less than what they tell you: an agent is not reasoning about your threat model. It is pattern-matching to the most common version of a solution it has seen in training data, and the most common version is often the least secure one. Agents also work without full context. They cannot see your .env file, do not know your existing auth setup unless you point them at it, and often generate a route or a query in isolation from the rest of the system that is supposed to protect it.
This is a tradeoff worth knowing before you lean on AI coding tools for anything that touches user data, not a reason to avoid them. The fix is a review habit, not a policy against using the tools at all.
The vulnerabilities that show up again and again
A handful of patterns account for most of what slips through. Knowing them in advance turns a vague "review the code" instruction into something you can actually check for.
Vulnerability | Why it happens | What to look for in the diff |
|---|---|---|
Hardcoded secrets or API keys | The agent writes a working value inline to make the code run, instead of wiring up a config or secrets manager | New string literals that look like keys, tokens, or connection strings, sitting outside of environment variable references |
Missing auth checks on new routes | The agent optimizes for making the feature work, not for who is allowed to call it | A new endpoint with no auth middleware, session check, or role check compared to sibling routes |
SQL injection via string concatenation | String concatenation or an f-string is the simplest way to build a query, and simplest is what gets reproduced | Queries built by joining strings or template literals with raw request input instead of using parameters |
Cross-site scripting from unescaped output | The agent renders a value directly into HTML or the DOM to satisfy the feature as asked | User-supplied data inserted into markup without escaping, sanitization, or a templating engine's auto-escape |
Overly permissive CORS | A wildcard origin is the fastest way to stop a request from failing in testing | Access-Control-Allow-Origin set to a wildcard on routes that handle account or user data |
Debug output left in production paths | Verbose errors and stack traces are common in tutorial code the model trained on | Stack traces, raw exception messages, or debug flags reachable from a production code path |
Review the diff like it is a new hire's first pull request
Treat every agent-authored change as unverified until you have looked at it, the same way you would a junior engineer's first week of commits. Read the whole diff, not just the part tied to the feature you asked for. Agents frequently touch adjacent files, add a helper function, or adjust a config value as a side effect, and those side effects are where auth checks quietly go missing.
Smaller diffs make this realistic. A 400-line pull request gets skimmed. A 40-line one gets read. If your agent tends to produce large changes, breaking the task down first makes the security review something you can actually do, not something you sign off on because reading it properly is not practical. Combining this with a proper AI-assisted code review pass on each change catches most of what a first glance misses, since a second look with security specifically in mind is different from just reading for whether the feature works.
Automated scanners are a baseline, not a substitute
GitHub's own documentation on reviewing AI-generated code recommends starting with tooling built for exactly this problem: run CodeQL and Dependabot to catch vulnerabilities and dependency issues before a human opens the diff, alongside standard tests. GitHub also calls out a failure mode specific to generated code: hallucinated APIs and hallucinated packages, meaning a call to something that does not exist or a dependency name that looks real but was never published. Verify that every new package in a diff is actively maintained and comes from a source you recognize before it lands in a lockfile.
GitHub's guidance also suggests turning the model on its own output by asking it directly: "What possible vulnerabilities or security issues could this code introduce?" That question does not replace a scanner or a human review, but it is a fast, free check that surfaces issues the agent already has some awareness of and simply was not asked about.
A pasteable review instruction for catching agent-introduced vulnerabilities
Most of this comes down to what you ask the agent to check before it tells you a task is done. A generic "make sure the code is secure" instruction gets a generic answer. A specific checklist, dropped into your repo's AGENTS.md or CLAUDE.md, gets specific answers you can verify. Use something like this:
Before marking any task complete, review your own diff against this list and report what you found, even if the answer is none:
1. Hardcoded secrets, API keys, tokens, or credentials. These must come from environment variables or a secrets manager, never a literal string in code.
2. New or changed API routes without an explicit authentication or authorization check.
3. Database queries built with string concatenation or interpolation of request input. Use parameterized queries or the project's query builder instead.
4. User-supplied data written into HTML, templates, or the DOM without escaping.
5. Wildcard CORS origins or disabled CORS checks on any route touching user or account data.
6. Debug output, verbose error messages, or stack traces reachable from a production code path.
7. New dependencies added. Name each one and confirm it is a real, maintained package, not an assumption.
State explicitly which items you checked and what you found for each.The before-and-after matters more than the checklist itself. Here is the kind of fix that checklist item three is meant to catch, using a lookup an agent might write when asked to "get a user by ID":
Before, vulnerable to SQL injection:
def get_user(user_id):
query = "SELECT * FROM users WHERE id = " + user_id
return db.execute(query)After, parameterized:
def get_user(user_id):
query = "SELECT * FROM users WHERE id = %s"
return db.execute(query, (user_id,))The first version works in every manual test you would think to run. It also lets anyone pass a value like "1 OR 1=1" through user_id and get every row in the table back, because the input becomes part of the SQL itself. The second version keeps user_id as data the database driver escapes, never as code the database parses. Nothing about the feature changed. The only difference is whether input can rewrite the query, which is exactly the kind of change a checklist catches and a quick manual test of the happy path does not.
A checklist before you merge
Read the full diff, not only the section tied to the feature you asked for.
Search the diff for hardcoded secrets, keys, and connection strings before anything else.
Confirm every new or modified endpoint has an explicit auth check, not an inherited one you are assuming exists.
Confirm database queries use parameters rather than concatenated or interpolated input.
Run your SAST and dependency scanner, and treat new findings as blocking rather than something to triage later.
Ask the agent what security issues its own change could introduce, and compare its answer against what you found manually.
Questions
Do AI coding agents really introduce more vulnerabilities than human developers do?
On the numbers available, yes, by a meaningful margin. Veracode's 2025 GenAI Code Security Report found 45 percent of AI-generated code samples across more than 100 models failed basic OWASP Top 10 security checks, with cross-site scripting and log injection failing the vast majority of the time. That does not mean every agent-written line is unsafe. It means the failure rate is high enough that skipping a security-focused review of agent output is a real risk, not a formality.
Can static analysis and scanners catch everything an AI agent gets wrong?
No. Tools like CodeQL and dependency scanners are a strong baseline for known vulnerability patterns, and GitHub recommends running them before a human even opens the diff. But they will not reliably catch a hallucinated package name, a business-logic auth check that is subtly wrong, or a query that is syntactically fine but logically exposes data it should not. Those need a person reading the diff with the feature's actual requirements in mind.
Should an AI coding agent have direct access to authentication or payment code?
Treat that code as higher-risk by default rather than banning agents from it outright. Give agents the narrowest access that lets them do the task, review changes to auth and payment paths more slowly than routine changes, and keep the kind of read-only or staging access that limits blast radius if a change turns out to be wrong.
What is the fastest way to catch a hardcoded secret an agent added?
A pre-commit hook or CI step that scans diffs for key-shaped strings catches most of them automatically, and it is worth setting up once rather than relying on memory every time. Short of that, searching a diff for new string literals near words like "key," "token," "secret," or "password" before merge catches the same thing manually in under a minute. It is the same failure mode as when an AI agent commits a secret to your repo outright, just caught earlier, before the commit lands.
Does asking the agent to review its own diff for security issues actually work?
It helps, but treat it as one more data point rather than a guarantee. The question GitHub suggests, asking the model directly what vulnerabilities its own change could introduce, tends to surface issues the model already had latent awareness of. It will not reliably catch a mistake the model did not recognize as a mistake in the first place, which is exactly why it pairs with a checklist and a human pass rather than replacing either.
How did this land?
About the author

Developer Advocate
Steve builds something with Swarmz every week and writes up what worked, what broke, and what he'd do differently. Tutorials and hands-on guides are his lane.


