I scanned 13 popular AI-app starter repos. 4 were clean, and the same bug was in 6 of them. A developer built vibecheck, a stdlib-only deterministic static analyser for production-failure patterns in AI-built apps, and scanned 13 popular open-source AI-app starter repos. Six of the 13 contained an API route with no authentication check, five exposed secrets to the browser bundle, and five rendered untrusted content as raw HTML, with one repo carrying 67 findings including 36 rated critical. The author notes the bugs are mundane 'I'll fix that later' defaults rather than exotic flaws, and anonymised the repos to avoid unfairly naming maintainers on unverified counts. I built a small deterministic scanner for the failure patterns that show up in AI-built apps — the ones that don't stop the demo, but break you in production. Then I pointed it at a corpus of popular open-source starter templates and AI-app repos, because those are what a lot of "vibe-coded" projects are actually built on. If the failure is in the default the framework hands you, it should show up even in well-maintained, heavily-starred repos. It did. Measured 2026-09-16 with vibecheck https://vibechecksai.github.io/vibecheck/ , a deterministic static analyser for the production-failure patterns AI-built apps hit. Anil-matcha/awesome-generative-ai-apps is a vibecheck.py stdlib-only, deterministic, no network, test/fixture paths skipped by default . Run on the tree as published, unmodified. | rule | repos affected | what it means | |---|---|---| | route-without-auth | 6 | An API route with no authentication check — the classic 'I'll add auth later'. | | client-exposed-secret | 5 | A secret-ish variable shipped to the browser bundle via a public prefix. | | dangerous-html | 5 | Untrusted content rendered as raw HTML XSS surface . | | auth-todo | 1 | A TODO /stub standing where an auth check should be. | | cors-wildcard | 1 | Access-Control-Allow-Origin: on an authenticated surface. | | hardcoded-secret | 1 | A real credential literal committed in source. | Projects are anonymised: the counts are static-analysis output and a pattern is a prompt to look, not proof of exploitability. Naming a specific project on unverified counts would be unfair to its maintainers, so the aggregate and rule tables above carry the finding and the raw data stays available for verification. | project | stars | findings | critical | high | medium | |---|---|---|---|---|---| | repo A | 26746 | 67 | 36 | 6 | 25 | | repo B | 817 | 13 | 10 | 2 | 1 | | repo C | 1324 | 7 | 1 | 3 | 3 | | repo D | 1560 | 4 | 0 | 4 | 0 | | repo E | 3052 | 3 | 0 | 2 | 1 | | repo F | 1690 | 3 | 0 | 0 | 3 | | repo G | 844 | 2 | 2 | 0 | 0 | | repo H | 5110 | 1 | 1 | 0 | 0 | | repo I | 3417 | 1 | 0 | 1 | 0 | | repo J | 2138 | 0 | 0 | 0 | 0 | | repo K | 1679 | 0 | 0 | 0 | 0 | | repo L | 838 | 0 | 0 | 0 | 0 | | repo M | 29404 | 0 | 0 | 0 | 0 | Raw per-repo JSON: results.jsonl . Reproduce with python3 scan corpus.py . If you want to run it on your own project before reading further, it's free and open source: vibechecksai/vibecheck https://github.com/vibechecksai/vibecheck . Zero dependencies, stdlib only, runs in about a second. The thing I keep coming back to: none of these are exotic bugs. They're the boring "I'll fix that later" defaults — an unauthenticated route, a secret with a public prefix, unescaped HTML. The AI writes the 80% that works. The last 20% is the part nobody prompts for.