AI CODE VULNERABILITIES: THE TAXONOMY, BY FAMILY
AI coding tools do not produce random bugs. They produce the same vulnerability classes over and over, because the same generation habits cause them. This page catalogs those classes by family — what the flaw looks like in generated code, why the model produces it, and where the deep-dive pattern write-up lives.
How to read this taxonomy
This page catalogs what ships: the recurring vulnerability classes in AI-generated code, grouped into six families. Each family gets the same treatment — why generators produce it, one concrete code shape, the fix direction, and links to the detailed pattern write-ups in our patterns series. For the analysis of why these failure modes happen at all — training-data bias, plausibility over correctness, review economics — see the companion page, AI-Generated Code Risk Analysis. The two pages are deliberately split: this one is the catalog, that one is the causal model.
Injection
Injection survives in AI-generated code despite every model “knowing” about parameterized queries. The failure is contextual: when a developer describes a novel or complex query in chat, the model reaches for a template literal because interpolated SQL reads more clearly than placeholder syntax, and clarity is what the training data rewarded. The same reflex produces shell commands built with string concatenation (exec("convert " + filename)), MongoDB filters assembled from raw request bodies (open to $ne / $gt operator injection), and LDAP or XPath lookups spliced from form input.
The canonical shape:
db.query(`SELECT * FROM users WHERE email = '${req.body.email}'`);
The fix direction is mechanical and prompt-able: parameterize every query, allowlist shell arguments (or avoid the shell entirely), and cast/validate NoSQL filter values so user input can never contribute operators. Deserialization of untrusted input belongs in this family too — generated code that feeds pickle, ObjectInputStream, or permissive YAML loaders from request data hands the attacker code execution. Deep dives: insecure deserialization, LDAP, XPath and MIME confusion and prototype pollution and DOM attacks.
Broken authentication and authorization
This family produces the highest-impact findings in AI-built apps, and it has a distinctive signature: authentication is present, authorization is missing. Training data shows login flows constantly and ownership checks rarely, so models internalize “user is logged in” as “user is allowed.” The result is BOLA/IDOR on nearly every generated CRUD route:
app.get("/api/invoices/:id", requireAuth, async (req, res) => {
res.json(await db.invoices.findById(req.params.id)); // no owner check
});
Adjacent shapes in the same family: jwt.decode() where jwt.verify() was needed (or accepting the alg: none header), mass assignment where the whole request body is spread into an update (User.update(req.body) — letting a client set role: "admin"), and generated password-reset or magic-link flows with guessable or non-expiring tokens. The fix direction is a per-route ownership check tied to the authenticated principal, strict JWT verification with pinned algorithms, and explicit field allowlists on every write. Deep dives: BOLA in AI-generated CRUD, JWT alg:none and kid traversal, mass assignment, and auth flows: magic links, OTP, reset.
Data exposure
Generators over-fetch and over-return by default, because SELECT * and res.json(user) are the shortest correct-looking answers. The API returns the whole row — password hash, email, internal flags — and the frontend simply renders the fields it wants. On Supabase and Firebase backends the exposure is more direct: generated schemas frequently ship with Row Level Security disabled or rules wide open, which means the public client key grants read/write on every table. Two more shapes round out the family: secrets that belong on the server landing in the client bundle (a process.env reference in a component, inlined by the bundler), and production builds shipping source maps or a reachable .git/ directory that hand attackers the entire codebase.
Fix direction: explicit field selection on every response, RLS or rules enabled and tested on every table, server-only code paths for anything holding a secret, and source maps stripped from production builds. Deep dives: naked databases, Supabase service-role key leaks, source maps and exposed .git, and GraphQL, Swagger and gRPC exposure. Point tools: Supabase RLS checker, Firebase scanner, token leak checker.
File uploads and parsing
Upload handlers are a place where AI tools generate the happy path and stop. The generated handler accepts the file, trusts the client-supplied MIME type and filename, and writes it to disk or a public bucket. That yields path traversal via filenames like ../../config.js, stored XSS via HTML/SVG uploads served from the app origin, zip-slip when generated code extracts archives without normalizing entry paths, and XXE when an XML parser is instantiated with external entities left on — the library default in several ecosystems, and models reproduce library defaults.
Fix direction: generate your own filenames, validate content (not the Content-Type header), serve user uploads from a separate origin or with forced-download headers, normalize archive entry paths before extraction, and disable external entity resolution explicitly. Deep dive: file upload, zip slip and XXE.
Configuration and deployment
Config flaws ship because they are the model’s standard answer to a development-time error. CORS errors get “fixed” with Access-Control-Allow-Origin: * — fatal once combined with credentials. Cookies ship without Secure/HttpOnly/SameSite because the tutorial code the model learned from didn’t set them. OAuth callbacks accept loose redirect URIs and skip PKCE. Debug and admin routes scaffolded during development (/admin, /_debug, seed endpoints) go to production ungated. And generated CI pipelines echo secrets into logs or pull unpinned third-party actions, extending the blast radius from the app to the delivery pipeline. Dangling DNS and takeover-able buckets belong here too: infrastructure the generator referenced but nobody inventoried.
Fix direction: an origin allowlist from configuration, never * with credentials; hardened cookie and TLS defaults; exact-match OAuth redirect URIs with PKCE; an auth gate or removal for every non-production route; pinned CI dependencies and masked secrets. Deep dives: CORS with credentials misconfiguration, cookie, TLS and OAuth/PKCE gaps, poisoned CI and DevOps leaks, hosting panels and internal surface, and S3 and subdomain takeover.
Business logic and money paths
Logic flaws are the family scanners historically miss and generators reliably create, because the model implements the described workflow — not the abuse of it. Checkout flows compute the price on the client and trust it on the server. Discount codes have no use-count enforcement outside the UI. Webhook handlers mark orders paid without verifying the provider’s signature, so anyone who can POST to the endpoint can “pay.” And money paths run check-then-act without locks or idempotency keys, so two concurrent requests both pass the balance check:
const bal = await getBalance(user); // check
if (bal >= amount) await withdraw(user, amount); // act — raceable
Fix direction: recompute every price and entitlement server-side, verify webhook signatures with constant-time comparison, and make money operations atomic and idempotent (row locks, unique idempotency keys, single-statement conditional updates). Deep dives: race conditions in money paths and Stripe webhooks and paid-trust bypass.
AI-specific failure modes
Some classes exist because the code came from a model, with no pre-LLM equivalent:
- Hallucinated packages. Models suggest dependencies that do not exist. Attackers register those names on npm/PyPI (slopsquatting), and the next generated
npm installpulls attacker code. Check every unfamiliar dependency — our package hallucination scanner automates it. - Prompt-injectable rendering. Apps that render LLM output as HTML or Markdown without sanitization let anyone who can influence the model’s input inject markup, scripts, or data-exfiltrating image URLs into other users’ sessions. Deep dive: LLM-rendered HTML/Markdown.
- Indirect prompt injection. Generated agent and RAG features feed untrusted content (web pages, documents, emails) into the model as if it were instructions. Deep dives: indirect prompt injection and RAG poisoning.
- MCP and tool-spec injection. Generated agent scaffolds trust tool descriptions and MCP servers implicitly; a malicious tool spec becomes an instruction channel. Deep dive: open MCP and tool-spec injection.
- Weak randomness and hallucinated security helpers.
Math.random()for tokens, or calls to plausible-sounding security functions that don’t exist in the imported library — the code compiles only after someone “fixes” it by removing the check. Deep dive: ReDoS and weak randomness.
Using this taxonomy
Reading a taxonomy is cheap; finding which families are present in your app is the work. Scan first, then fix by family — the Vibe Code Scanner tests for these classes across your deployed app, and the free security self-audit covers the manual checks. For tool-specific manifestations of the same families, see Cursor security risks and Copilot security risks.
Related resources
- AI-Generated Code Risk Analysis — why these failure modes happen, not just what ships
- OWASP Top 10 for AI Code — the same territory mapped to OWASP categories
- Vibe Coding Vulnerabilities — the vibe-coding-specific view
- Secure AI Coding Practices — prompting and workflow discipline that prevents these classes
- SAST Tools for AI Code — which static tools catch which families
- Between SAST and Pentest — where the logic and config families fall through
SCAN YOUR DEPLOYED APP
Paste your live URL. We probe exposed keys, missing auth, open databases, and broken access control — results in under 60 seconds. 14-day trial, no card.
14-day free trial · No credit card · Cancel anytime