LLM Bug Classes: What AI Assistants Get Wrong About Security
LLMs like Copilot and Cursor reliably reproduce 6 dangerous bug classes. Learn to spot SQL injection, path traversal, and insecure deserialization in AI output.
Why AI-Generated Code Has Predictable Blind Spots
When HackerOne recently paused bug bounties citing an "AI-led remediation crisis," the security community took notice. AI tools are now discovering vulnerabilities faster than human teams can fix them. But the same models generating that flood of patches are also writing production code full of the same vulnerabilities, just in new repositories.
This is not a coincidence. Large language models are trained on the entire public GitHub corpus, which includes decades of insecure patterns. When you ask an LLM to write a file upload handler, it will produce something that looks correct because it mirrors the most common patterns it has seen, and the most common patterns pre-date modern security expectations.
After scanning thousands of vibe-coded and AI-assisted repositories, the Vouch Security team has identified six bug classes that appear disproportionately often in AI-generated code.
1. SQL Injection via String Formatting
LLMs default to string interpolation for database queries even in 2024. The training data contains far more examples of the unsafe pattern than the safe one.
# What the LLM generates (unsafe)
query = f"SELECT * FROM users WHERE username = '{username}'"
cursor.execute(query)
# What you need (parameterized query)
query = "SELECT * FROM users WHERE username = %s"
cursor.execute(query, (username,))
2. Path Traversal in File Handlers
AI assistants routinely join user-supplied filenames directly onto base paths without sanitization.
// What the LLM generates (unsafe)
const filePath = path.join(uploadDir, req.params.filename);
fs.readFile(filePath, callback);
// What you need
const resolved = path.resolve(uploadDir, req.params.filename);
if (!resolved.startsWith(path.resolve(uploadDir))) {
return res.status(403).send('Forbidden');
}
fs.readFile(resolved, callback);
3. Insecure Deserialization
When LLMs write API endpoints that accept serialized objects, they almost never include type validation before deserialization.
4. Hardcoded Credentials in Configuration
LLMs frequently include placeholder credentials that end up committed as real values. They mirror the tutorial code pattern of password = "secret" because that is what was in the training data.
5. Missing Rate Limiting on Authentication Endpoints
LLMs write login and password-reset endpoints without rate limiting. This is a consistent omission across all major models tested.
6. SSRF in Webhook and Proxy Handlers
When asked to implement webhooks or URL-fetching features, LLMs rarely validate that the supplied URL points to an external host and not an internal service.
Why Static Analysis Alone Is Not Enough
Traditional SAST tools catch many of these patterns, but AI-generated code introduces a new challenge: the vulnerabilities are often one abstraction layer away from the obvious pattern. An LLM might use a wrapper function that obscures the underlying string interpolation, making signature-based detection miss it.
Effective scanning of AI-generated code needs semantic analysis that follows the data flow through wrapper functions, not just pattern matching on surface syntax.
Key Takeaways
- LLMs reproduce six predictable vulnerability classes because their training data reflects the most common (often insecure) patterns in public repositories.
- Parameterized queries, path canonicalization, and URL validation must be explicit engineering requirements, not afterthoughts, when reviewing AI-generated code.
- Standard SAST tools miss abstracted versions of these patterns; data-flow analysis is required to catch vulnerabilities in AI-generated wrapper code.