<?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>Forem: Elise Vance</title>
    <description>The latest articles on Forem by Elise Vance (@shecantcode).</description>
    <link>https://forem.com/shecantcode</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3878897%2F6ebcddbc-3637-4adf-a77d-741d02c2cd41.jpeg</url>
      <title>Forem: Elise Vance</title>
      <link>https://forem.com/shecantcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shecantcode"/>
    <language>en</language>
    <item>
      <title>I scanned every major vibe coding tool for security. None scored above 90.</title>
      <dc:creator>Elise Vance</dc:creator>
      <pubDate>Wed, 15 Apr 2026 21:51:29 +0000</pubDate>
      <link>https://forem.com/shecantcode/i-scanned-every-major-vibe-coding-tool-for-security-none-scored-above-90-3opg</link>
      <guid>https://forem.com/shecantcode/i-scanned-every-major-vibe-coding-tool-for-security-none-scored-above-90-3opg</guid>
      <description>&lt;p&gt;I'm a non-technical founder. I can't write code. I built two production apps entirely with AI.&lt;/p&gt;

&lt;p&gt;Last week I scanned my own app for security. It scored 20/100. Found 8 vulnerabilities including a critical auth bypass where missing config silently allows all requests.&lt;/p&gt;

&lt;p&gt;So I built Vibe Check — an AI-powered security scanner for vibe-coded apps. Then I pointed it at the tools themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Scorecard
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Critical Finding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;open-lovable (Firecrawl)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0/100&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unauthenticated command execution across 3 API routes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Devika&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;40/100&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLM responses executed via subprocess.run() without validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloudflare VibeSDK&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;78/100&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API tokens logged in plain text, missing OAuth CSRF protection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bolt.new&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;90/100&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Command injection via user-controlled shell action content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cline&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;96/100&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minor CI script command injection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;100/100&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The tools millions of people use to build apps have their own security issues. If their code has vulnerabilities, what about yours?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Static Scanners Miss These
&lt;/h2&gt;

&lt;p&gt;Three days ago, VibeDoctor &lt;a href="https://dev.to/vibedoctor_io/i-scanned-the-most-famous-ai-coding-repos-on-github-heres-what-i-found-469l"&gt;launched a scanner&lt;/a&gt; and scanned some of these same repos. They use SonarQube, Gitleaks, Trivy, and Lighthouse — six tools running in parallel. They scored Devika 66/100.&lt;/p&gt;

&lt;p&gt;I scored it 40/100 and found a CRITICAL command injection they missed entirely.&lt;/p&gt;

&lt;p&gt;The difference is approach. Static scanners pattern-match for known vulnerabilities. They look for &lt;code&gt;eval(&lt;/code&gt;, hardcoded API keys, outdated dependencies. They're good at this.&lt;/p&gt;

&lt;p&gt;What they can't catch is &lt;strong&gt;intent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Consider this real code from Devika:&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;# LLM generates a list of commands
&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;cmd&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;commands&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="n"&gt;cmd&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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;eval()&lt;/code&gt;. No obviously dangerous function call. SonarQube sees &lt;code&gt;subprocess.run()&lt;/code&gt; with a list argument — that's the &lt;em&gt;safe&lt;/em&gt; way to call subprocess. Clean.&lt;/p&gt;

&lt;p&gt;But the commands come from an LLM response. Whatever the AI decides to generate gets executed on the server. That's not a syntax bug. That's an intent bug. The code does exactly what it was written to do — it just shouldn't have been written to do that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bug That Started Everything
&lt;/h2&gt;

&lt;p&gt;Here's the function from my own production app that Vibe Check caught:&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;_auth_ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&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="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="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WEBHOOK_SECRET&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;secret&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;# no secret set -&amp;gt; allow all
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every static tool I ran against this said it was clean. No hardcoded secrets. No SQL injection. No XSS. The code is syntactically perfect.&lt;/p&gt;

&lt;p&gt;The bug: when &lt;code&gt;WEBHOOK_SECRET&lt;/code&gt; isn't set in the environment, the function returns &lt;code&gt;True&lt;/code&gt; and every webhook request is authorized. In development, that variable is often unset. In production, one env var typo means your backend is wide open.&lt;/p&gt;

&lt;p&gt;This is a semantic bug. You only catch it by reading the code and asking "what happens when the inputs are missing?"&lt;/p&gt;

&lt;h2&gt;
  
  
  How Vibe Check Works
&lt;/h2&gt;

&lt;p&gt;Vibe Check sends your code to Claude (Anthropic's AI) with a security-focused prompt covering six categories: secrets, auth, injection, data exposure, dependencies, and config.&lt;/p&gt;

&lt;p&gt;The prompt has specific instructions for the bugs AI coding tools create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default-allow patterns.&lt;/strong&gt; Flag &lt;code&gt;if not secret: return True&lt;/code&gt; as critical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM output as execution input.&lt;/strong&gt; Flag any path where model output reaches subprocess, eval, or file write without validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing auth on action endpoints.&lt;/strong&gt; Flag routes that perform destructive actions without authentication checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output is structured via Anthropic's tool-use API. Every finding has a category, severity, file, line number, description, and suggested fix — all in plain English.&lt;/p&gt;

&lt;h2&gt;
  
  
  False Positives Are the Real Product
&lt;/h2&gt;

&lt;p&gt;LLMs are noisy security reviewers. During a day of self-scanning, Claude flagged:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"render.yaml contains &lt;code&gt;sync: false&lt;/code&gt; for secrets — could be misconfigured"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(&lt;code&gt;sync: false&lt;/code&gt; is the correct Render.com setting for secrets.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"compare_digest is correctly implemented"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Flagged as a finding even though it's literally the fix.)&lt;/p&gt;

&lt;p&gt;The solution isn't more prompting — Claude ignores guardrails about a third of the time. The real fix is a &lt;strong&gt;hard post-parse filter&lt;/strong&gt; that drops findings matching known false-positive patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infrastructure config files at low/medium severity&lt;/li&gt;
&lt;li&gt;Template files for secrets-category findings&lt;/li&gt;
&lt;li&gt;Test files entirely&lt;/li&gt;
&lt;li&gt;Self-contradictory descriptions ("X is correct, but...")&lt;/li&gt;
&lt;li&gt;Speculative hedging ("could be a", "potentially allowing")&lt;/li&gt;
&lt;li&gt;Dev-environment-only warnings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Critical severity never gets filtered. We want false positives at critical to surface for human review.&lt;/p&gt;

&lt;p&gt;After this filter, Vibe Check's own repo scans as &lt;strong&gt;100/100 with zero findings&lt;/strong&gt;. Without the filter, it bounced between 79 and 94 with different findings each run.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers Behind the Problem
&lt;/h2&gt;

&lt;p&gt;This isn't theoretical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;65%&lt;/strong&gt; of vibe-coded production apps have security issues (Escape.tech, 1,400 apps scanned)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;45%&lt;/strong&gt; of AI-generated code contains OWASP Top 10 vulnerabilities (Veracode, 100+ LLMs tested)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;35 CVEs&lt;/strong&gt; traced to AI-generated code in March 2026 alone (Georgia Tech Vibe Security Radar)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1.5 million&lt;/strong&gt; API keys leaked from one vibe-coded app within 3 days of launch (Moltbook)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;63%&lt;/strong&gt; of vibe coding users are not developers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last number is the one that matters most. Most vibe coders can't read the code they're shipping. They can't audit it. They can't tell if the login page can be bypassed.&lt;/p&gt;

&lt;p&gt;I'm one of them. I built two production apps without writing a line of code. I had no idea my code was vulnerable until I scanned it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Loop
&lt;/h2&gt;

&lt;p&gt;Here's what Vibe Check enables:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scan&lt;/strong&gt; — paste a GitHub URL, get a score in 60 seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download&lt;/strong&gt; — get the findings as a markdown file&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix&lt;/strong&gt; — paste the findings into your AI coding tool (Cursor, Claude Code, Lovable, whatever you used to build it)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-scan&lt;/strong&gt; — verify the fixes worked and your score went up&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My own app went from 20/100 to working on fixes now. The scanner caught what I couldn't see. The AI coding tool fixed what the scanner found. The re-scan verified the fixes worked.&lt;/p&gt;

&lt;p&gt;That's the product. Scan. Fix. Verify.&lt;/p&gt;

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

&lt;p&gt;Vibe Check is free. No signup needed for your first scan. Your code is never stored — we scan it, report findings, and forget it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://chat-api-19ij.onrender.com" rel="noopener noreferrer"&gt;https://chat-api-19ij.onrender.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Code is open source at &lt;a href="https://github.com/evance1227/chat" rel="noopener noreferrer"&gt;evance1227/chat&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Scan your repo. You might be surprised what's hiding in code that works perfectly.&lt;/p&gt;

&lt;p&gt;— Elise Vance (&lt;a href="https://twitter.com/shecantcode" rel="noopener noreferrer"&gt;@shecantcode&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Static vs Semantic: how a security scanner reads AI-generated code</title>
      <dc:creator>Elise Vance</dc:creator>
      <pubDate>Wed, 15 Apr 2026 01:52:21 +0000</pubDate>
      <link>https://forem.com/shecantcode/static-vs-semantic-how-a-security-scanner-reads-ai-generated-code-cj6</link>
      <guid>https://forem.com/shecantcode/static-vs-semantic-how-a-security-scanner-reads-ai-generated-code-cj6</guid>
      <description>&lt;p&gt;Three days ago &lt;a href="https://dev.to/vibedoctor_io/i-scanned-the-most-famous-ai-coding-repos-on-github-heres-what-i-found-469l"&gt;VibeDoctor launched a scanner&lt;/a&gt; for AI-generated apps. They scan by running six tools in parallel: SonarQube, Gitleaks, Trivy, Lighthouse, plus custom checks. They scanned open-lovable, devika, and bolt.new and found hundreds of issues. It's good work.&lt;/p&gt;

&lt;p&gt;But there's a whole class of bug their approach can't see. I built &lt;a href="https://chat-api-19ij.onrender.com" rel="noopener noreferrer"&gt;Vibe Check&lt;/a&gt; to catch that class. Here's what's different.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap
&lt;/h2&gt;

&lt;p&gt;Static scanners pattern-match. They look for &lt;code&gt;eval(&lt;/code&gt;, &lt;code&gt;os.system(&lt;/code&gt;, hardcoded API keys, outdated dependencies. They're good at this, and they catch a lot.&lt;/p&gt;

&lt;p&gt;What they can't catch is &lt;strong&gt;intent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Consider this real function from a repo I scanned this week:&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;_auth_ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&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="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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Accept multiple common secret formats so GHL/curl both work.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WEBHOOK_SECRET&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;secret&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;# no secret set -&amp;gt; allow all
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grep for known vuln patterns: nothing. SonarQube: clean. Gitleaks: no secrets here (that's the point). Trivy: no CVEs. Every static tool I threw at it said OK.&lt;/p&gt;

&lt;p&gt;The bug is that when &lt;code&gt;WEBHOOK_SECRET&lt;/code&gt; is unset in the environment, the function returns &lt;code&gt;True&lt;/code&gt; and the webhook is &lt;strong&gt;fully open&lt;/strong&gt;. In development &lt;code&gt;WEBHOOK_SECRET&lt;/code&gt; is often unset. In production, a simple env-var typo becomes an unauthenticated remote action vector.&lt;/p&gt;

&lt;p&gt;This is a semantic bug. You only catch it by reading the code and asking "what happens when the inputs are missing?" That's a human pen-tester mindset, not a regex.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Vibe Check reads code
&lt;/h2&gt;

&lt;p&gt;Vibe Check sends files to Claude via the Anthropic API with a custom prompt focused on six categories: secrets, auth, injection, data exposure, dependencies, config. The prompt has specific guardrails for LLM failure modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Default-allow patterns.&lt;/strong&gt; Explicit instructions to flag &lt;code&gt;if not secret: return True&lt;/code&gt; as critical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic SQL in column names.&lt;/strong&gt; Parameterized queries are safe until the column name or &lt;code&gt;ORDER BY&lt;/code&gt; clause comes from an f-string. The prompt flags this explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy invariant.&lt;/strong&gt; Claude is told to never echo actual secret values in findings. The scanner itself never persists source code, raw responses, or the secrets it finds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Output is structured via Anthropic's tool-use API. Every finding has category, severity, file, line, title, description, suggested_fix, and a verbatim &lt;code&gt;code_snippet&lt;/code&gt; field that Vibe Check uses to auto-correct line numbers post-hoc (Claude hallucinates line numbers; the snippet search fixes that).&lt;/p&gt;

&lt;h2&gt;
  
  
  False positives are the hard part
&lt;/h2&gt;

&lt;p&gt;LLMs are noisy. Over a day of self-scanning my own repo, I watched Claude emit gems like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"render.yaml contains &lt;code&gt;sync: false&lt;/code&gt; for secrets — could be misconfigured"&lt;br&gt;
(&lt;code&gt;sync: false&lt;/code&gt; is the &lt;em&gt;correct&lt;/em&gt; Render.com setting)&lt;/p&gt;

&lt;p&gt;"compare_digest is correctly implemented"&lt;br&gt;
(flagged as a finding even though it's literally the fix)&lt;/p&gt;

&lt;p&gt;"SQL query logging during development could expose sensitive data"&lt;br&gt;
(it's development; that's the whole point of &lt;code&gt;echo=True&lt;/code&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The solution isn't more prompting. Claude ignores prompt guardrails about a third of the time. The real fix is a &lt;strong&gt;hard post-parse filter&lt;/strong&gt; that drops findings matching known false-positive patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infrastructure config files (&lt;code&gt;render.yaml&lt;/code&gt;, &lt;code&gt;.github/workflows/*&lt;/code&gt;, &lt;code&gt;Dockerfile&lt;/code&gt;, &lt;code&gt;*.tf&lt;/code&gt;) at low/medium severity&lt;/li&gt;
&lt;li&gt;Template files (&lt;code&gt;.env.example&lt;/code&gt;) for secrets-category findings&lt;/li&gt;
&lt;li&gt;Test files (&lt;code&gt;tests/&lt;/code&gt;, &lt;code&gt;conftest.py&lt;/code&gt;) entirely&lt;/li&gt;
&lt;li&gt;Self-contradictory descriptions ("X is correct, but...")&lt;/li&gt;
&lt;li&gt;Speculative hedging at low/medium ("could be a", "if this were", "potentially allowing")&lt;/li&gt;
&lt;li&gt;Dev-environment-only warnings ("during development", "in non-production")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Critical severity &lt;strong&gt;never&lt;/strong&gt; gets filtered. We want false positives at critical to surface for human review, not be silently dropped.&lt;/p&gt;

&lt;p&gt;After this filter, my own repo scans as &lt;strong&gt;100/100 with zero findings&lt;/strong&gt;. Without it, the score bounced between 79 and 94 across seven runs with completely different findings each time. &lt;em&gt;Filtering is the product.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I found in the wild
&lt;/h2&gt;

&lt;p&gt;I scanned a 24K-star AI-coding repo (responsible disclosure in flight; this post will be updated with the repo name after Thursday 2026-04-17 EOD UTC). Top findings:&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;File&lt;/th&gt;
&lt;th&gt;Why static scanners miss it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Unauthenticated command execution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/api/run-command-v2/route.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No auth gate before shelling out. No &lt;code&gt;eval()&lt;/code&gt; or &lt;code&gt;child_process.exec()&lt;/code&gt; with obviously-tainted input. Just a route handler that trusts any caller.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Arbitrary file write via AI-generated content&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/api/apply-ai-code-stream/route.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Writes to disk. Static tools see &lt;code&gt;fs.writeFile&lt;/code&gt; and don't flag it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Missing auth on package installation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/api/install-packages-v2/route.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The action is "install arbitrary npm package". No auth.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API key leaked in error responses&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app/api/search/route.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Regex scanners look for hardcoded keys in source. This one is &lt;em&gt;echoed&lt;/em&gt; back to the client on failure paths.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Combined, these form a complete "submit code → write it → execute it" chain. A separate scanner ran against this same repo three days ago and flagged hundreds of issues. None of these four. They require reading the route handlers end-to-end and asking what's actually authenticated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;I'm a non-technical founder. I can't write code. I built two production apps using AI coding tools and realized I had no way to know if they were safe. 65% of vibe-coded apps have security vulnerabilities. 35 CVEs were traced to AI-generated code in March 2026 alone.&lt;/p&gt;

&lt;p&gt;So I built the tool I needed. Vibe Check uses Claude to understand what code is &lt;em&gt;trying&lt;/em&gt; to do, and catches when it silently fails. Built by someone who can't code, for everyone who can't code.&lt;/p&gt;

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

&lt;p&gt;Vibe Check is free, no signup required for basic scans. Sign in with GitHub for scan history. Your code is never stored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://chat-api-19ij.onrender.com" rel="noopener noreferrer"&gt;https://chat-api-19ij.onrender.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Code is open at &lt;a href="https://github.com/evance1227/chat" rel="noopener noreferrer"&gt;evance1227/chat&lt;/a&gt;. Feedback welcome, especially on the prompt and the false-positive filter.&lt;/p&gt;

&lt;p&gt;— Elise Vance (&lt;a href="https://twitter.com/shecantcode" rel="noopener noreferrer"&gt;@shecantcode&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codequality</category>
      <category>security</category>
      <category>vibecoding</category>
    </item>
  </channel>
</rss>
