<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jitendrarout</title>
    <description>The latest articles on DEV Community by jitendrarout (@jitendrarout).</description>
    <link>https://dev.to/jitendrarout</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4120085%2Fcfaf2141-d269-42f4-a50e-5162a5477a65.png</url>
      <title>DEV Community: jitendrarout</title>
      <link>https://dev.to/jitendrarout</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jitendrarout"/>
    <language>en</language>
    <item>
      <title>7 Vulnerability Patterns I Found in AI-Generated Code (and How to Catch Them)</title>
      <dc:creator>jitendrarout</dc:creator>
      <pubDate>Fri, 11 Sep 2026 03:03:00 +0000</pubDate>
      <link>https://dev.to/jitendrarout/7-vulnerability-patterns-i-found-in-ai-generated-code-and-how-to-catch-them-2gji</link>
      <guid>https://dev.to/jitendrarout/7-vulnerability-patterns-i-found-in-ai-generated-code-and-how-to-catch-them-2gji</guid>
      <description>&lt;h1&gt;
  
  
  7 Vulnerability Patterns I Found in AI-Generated Code (and How to Catch Them)
&lt;/h1&gt;

&lt;p&gt;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 &lt;em&gt;disproportionately&lt;/em&gt; in AI-generated code, for reasons that have nothing to do with capability and everything to do with what a code sample optimizes for.&lt;/p&gt;

&lt;p&gt;I wanted to find out whether that pattern was real or just a feeling, so I built &lt;a href="https://github.com/jitendrarout/ai-vuln-scan" rel="noopener noreferrer"&gt;&lt;code&gt;ai-vuln-scan&lt;/code&gt;&lt;/a&gt;, 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core idea: AI-generated bugs aren't random
&lt;/h2&gt;

&lt;p&gt;Traditional static analysis tools look for bugs in general. What I was after was narrower: which &lt;em&gt;specific&lt;/em&gt; mistakes are more likely to show up in AI-assisted code than in code a human wrote from scratch?&lt;/p&gt;

&lt;p&gt;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 &lt;em&gt;works&lt;/em&gt;, not necessarily the version that's &lt;em&gt;safe&lt;/em&gt;, unless the prompt specifically asks for the safe version.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seven patterns worth knowing
&lt;/h2&gt;

&lt;p&gt;I documented these as a public, versioned taxonomy — &lt;a href="https://github.com/jitendrarout/ai-vuln-scan/blob/main/docs/PATTERN_CATALOG.md" rel="noopener noreferrer"&gt;the AI Vulnerability Pattern Catalog&lt;/a&gt; — 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:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Hardcoded secrets in placeholder form.&lt;/strong&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Unparameterized query construction.&lt;/strong&gt; &lt;code&gt;`SELECT * FROM users WHERE id = ${userId}`&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Shell commands built by string interpolation.&lt;/strong&gt; Same root cause as #2 — &lt;code&gt;exec("cmd " + arg)&lt;/code&gt; is the intuitive-looking version;&lt;br&gt;
&lt;code&gt;execFile()&lt;/code&gt; with an argument array is correct but less often what gets generated by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Permissive default configuration.&lt;/strong&gt; Wildcard CORS, disabled TLS&lt;br&gt;
verification, debug mode left on — these "just work" in a demo and&lt;br&gt;
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).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Weak cryptographic primitives in a security context.&lt;/strong&gt; MD5 and&lt;br&gt;
&lt;code&gt;Math.random()&lt;/code&gt; are often the first hashing/randomness functions that&lt;br&gt;
come to mind for a generic "hash this" or "generate a random string"&lt;br&gt;
request — the security-context distinction isn't always surfaced unless specifically prompted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Inconsistent authorization across near-identical routes.&lt;/strong&gt; This one is, I think, the most distinctly &lt;em&gt;AI-flavored&lt;/em&gt; 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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Verbose error responses leaking internals.&lt;/strong&gt; Returning &lt;code&gt;err.stack&lt;/code&gt; 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.&lt;/p&gt;
&lt;h2&gt;
  
  
  Seeing it in practice
&lt;/h2&gt;

&lt;p&gt;Here's a realistic example — an Express route handler, the kind you'd&lt;br&gt;
get by asking an assistant for "an endpoint that looks up a user's&lt;br&gt;
orders":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/users/:id/orders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing looks wrong at a glance. But if this route sits in a file where every &lt;em&gt;other&lt;/em&gt; route includes an &lt;code&gt;authMiddleware&lt;/code&gt; 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.&lt;/p&gt;

&lt;p&gt;Running &lt;code&gt;ai-vuln-scan&lt;/code&gt; against a small sample file with a handful of&lt;br&gt;
these patterns deliberately included caught all of them — 12 findings&lt;br&gt;
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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build a dedicated tool instead of using existing linters
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The taxonomy itself is useful independent of the tool.&lt;/strong&gt; Naming and
documenting &lt;em&gt;why&lt;/em&gt; each pattern shows up disproportionately in
AI-generated code is a different, more specific claim than "this is a
bug that can happen," and it's citable/extendable on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detection can be tuned to the actual distribution.&lt;/strong&gt; Rule #6
(inconsistent auth across near-identical routes) isn't something a
general-purpose linter is likely to check for at all — it requires
knowing that &lt;em&gt;this specific&lt;/em&gt; class of drift is common in
AI-regenerated code specifically.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The tool currently covers JavaScript/TypeScript and Python with regex/structural detection — solid for the patterns above, but a real&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;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 &lt;a href="https://github.com/jitendrarout/ai-vuln-scan/blob/main/docs/PATTERN_CATALOG.md" rel="noopener noreferrer"&gt;pattern catalog&lt;/a&gt; is open to contributions, and documenting a new pattern doesn't require having a detection rule for it yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;code&gt;git clone https://github.com/jitendrarout/ai-vuln-scan&lt;/code&gt; and run &lt;code&gt;npm run scan:examples&lt;/code&gt; to see it catch all seven patterns against the bundled sample files.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Jitendra Rout is a Principal Software Engineer working on AI systems. This post is part of ongoing work on transparent, auditable AI tooling.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>javascript</category>
      <category>vulnerabilities</category>
    </item>
  </channel>
</rss>
