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, 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. 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.