7 Vulnerability Patterns I Found in AI-Generated Code (and How to Catch Them) A developer built ai-vuln-scan, an open-source static analysis tool tuned to catch security flaws that appear disproportionately in AI-generated code, and published a versioned seven-pattern taxonomy called the AI Vulnerability Pattern Catalog. The patterns include hardcoded placeholder secrets, unparameterized SQL queries, shell commands built by string interpolation, permissive default configurations, weak cryptographic primitives, inconsistent authorization across near-identical routes, and verbose error responses that leak internals. The developer attributes the flaws not to model capability but to AI assistants optimizing for plausible, runnable code samples rather than production-ready code. If you've used GitHub Copilot, Claude Code, or any AI coding assistant for more than a few weeks, you've probably shipped at least one of the bugs in this post without realizing it. Not because the AI is bad at coding — these tools are remarkably good — but because certain classes of mistake show up disproportionately in AI-generated code, for reasons that have nothing to do with capability and everything to do with what a code sample optimizes for. I wanted to find out whether that pattern was real or just a feeling, so I built ai-vuln-scan https://github.com/jitendrarout/ai-vuln-scan , a static analysis tool tuned specifically to these patterns, and used it to look closely at what actually goes wrong. Here's what I found, and the open-source tool that came out of it. Traditional static analysis tools look for bugs in general. What I was after was narrower: which specific mistakes are more likely to show up in AI-assisted code than in code a human wrote from scratch? The pattern I kept noticing was this: AI assistants are optimizing, in a sense, for "a plausible, runnable example" — and a plausible runnable example doesn't need a real secrets manager, doesn't need parameterized queries to demonstrate the concept, and doesn't need the hardened production config. So it generates the version that works , not necessarily the version that's safe , unless the prompt specifically asks for the safe version. That's not a knock on the models. It's a predictable consequence of what "helpful code sample" optimizes for versus what "production-ready code" requires. Which means the fix isn't "better prompting" though that helps — it's catching the gap systematically, the same way we catch any other predictable class of bug. I documented these as a public, versioned taxonomy — the AI Vulnerability Pattern Catalog https://github.com/jitendrarout/ai-vuln-scan/blob/main/docs/PATTERN CATALOG.md — specifically so the reasoning behind each one is checkable and extendable by anyone else who's noticed the same thing. Here are the seven, briefly: 1. Hardcoded secrets in placeholder form. A plausible-looking example API key or connection string gets generated to make the sample runnable, and gets copy-pasted into real code without ever being swapped for an environment variable. 2. Unparameterized query construction. SELECT FROM users WHERE id = ${userId} reads naturally as "the way you'd explain a query with a variable in it." Parameterization is the correct approach, but it's an extra, less narratively obvious step. 3. Shell commands built by string interpolation. Same root cause as 2 — exec "cmd " + arg is the intuitive-looking version; execFile with an argument array is correct but less often what gets generated by default. 4. Permissive default configuration. Wildcard CORS, disabled TLS verification, debug mode left on — these "just work" in a demo and remove setup friction the assistant doesn't have the context to resolve it doesn't know your real allowed origins or have your real cert . 5. Weak cryptographic primitives in a security context. MD5 and Math.random are often the first hashing/randomness functions that come to mind for a generic "hash this" or "generate a random string" request — the security-context distinction isn't always surfaced unless specifically prompted. 6. Inconsistent authorization across near-identical routes. This one is, I think, the most distinctly AI-flavored bug on the list. When you ask an assistant to "add another route like the others," it regenerates the pattern rather than copy-pasting the existing block — and regeneration is where a step like auth middleware can quietly drop out, especially across separate prompts or edits. A human copy-pasting an existing route is more likely to preserve the whole block by construction; an AI regenerating it from a description is not. 7. Verbose error responses leaking internals. Returning err.stack directly in an HTTP response is the fastest way to make error handling "work" and visible during development — the split between server-side logging and a generic client message is a production concern that's easy to omit from a first-pass generation. Here's a realistic example — an Express route handler, the kind you'd get by asking an assistant for "an endpoint that looks up a user's orders": js app.get '/api/users/:id/orders', req, res = { const userId = req.params.id; res.json { userId } ; } ; Nothing looks wrong at a glance. But if this route sits in a file where every other route includes an authMiddleware call and this one doesn't, that's exactly pattern 6 — and it's the kind of thing that's easy to miss in review because each individual route reads fine in isolation. Running ai-vuln-scan against a small sample file with a handful of these patterns deliberately included caught all of them — 12 findings across hardcoded secrets, unparameterized queries, a missing-auth route, and a weak-randomness token generator — with zero false positives on the same routes rewritten safely. Generic security linters ESLint security plugins, Bandit, Semgrep catch some of this - but generically, not with the AI-specific framing. Two things make a dedicated tool worth having: The tool currently covers JavaScript/TypeScript and Python with regex/structural detection — solid for the patterns above, but a real AST-based taint-tracking engine would catch more, especially multi-line or aliased variants. That's the next milestone, along with expanding language coverage and publishing a labeled dataset of AI-generated vulnerable code samples for anyone else working on this problem. If you've noticed other patterns that seem to show up disproportionately in AI-assisted code, I'd genuinely like to hear about them — the pattern catalog https://github.com/jitendrarout/ai-vuln-scan/blob/main/docs/PATTERN CATALOG.md is open to contributions, and documenting a new pattern doesn't require having a detection rule for it yet. Try it: git clone https://github.com/jitendrarout/ai-vuln-scan and run npm run scan:examples to see it catch all seven patterns against the bundled sample files. Jitendra Rout is a Principal Software Engineer working on AI systems. This post is part of ongoing work on transparent, auditable AI tooling.