We scanned 155 launched vibe-coded apps. Here's what we found.
State of vibe-coded app security · October 2026 · 5 min read
Vibe coding made shipping easy. Lovable, Bolt, Replit and Cursor take you from prompt to live URL in an afternoon. We wanted to know what that URL looks like from the outside, so we collected 155 apps that their makers had publicly launched on Show HN (mostly *.lovable.app, *.replit.app, Vercel, Netlify and Bolt, plus apps on custom domains) and ran ShipSniffer's free passive scan on each one.
99 were still live and scannable. Here is what they had in common.
The good news: almost nobody leaked a secret key
We looked through each app's HTML and up to 25 same-site JavaScript bundles for secret-shaped strings: OpenAI, Anthropic, Stripe live, AWS, GitHub, Slack and SendGrid keys, private key blocks, and Supabase service_role JWTs. Only 1 of 99 apps (1%) shipped a secret key to the browser, in its case an OpenAI key. That's much better than the horror stories suggest. The platforms' "put secrets in edge functions" defaults seem to be working.
But one is still enough: anyone who opens DevTools on that app can run up its OpenAI bill. We aren't naming it, and we never publish key values.
No app exposed /.env or /.git, and only 2% served public JavaScript source maps.
The real gap: Supabase without proof of RLS
37% of the live apps ship a Supabase anon key in their frontend. That's normal and by design. But it means one thing protects every row of user data: Row Level Security policies. One table with RLS switched off, or a policy of using (true), and anyone with that public key can read the whole table.
A passive scan can't tell whether RLS is on, and we deliberately did not test anyone's database. (ShipSniffer only runs those checks on sites whose owners verify they own them.) If you're one of the 37%, open the Supabase dashboard and check every table in public before you launch. Firebase showed up in 4% of apps, and the same rule applies to its security rules.
Security headers: the step everyone skips
| Header | Missing |
|---|---|
| Content-Security-Policy | 91% |
| X-Frame-Options / frame-ancestors | 88% |
| Referrer-Policy | 38% |
| X-Content-Type-Options | 37% |
| Strict-Transport-Security | 8% |
| All of the above | 5% |
Hosting platforms add HSTS for you, so it's mostly fine. A CSP is the header that limits the damage when an AI-written component renders untrusted HTML, and almost no one has one. 13% also announce their stack with an X-Powered-By header.
Launch polish
30% were missing Open Graph tags (so shared links show up bare on X and Discord), 11% had no meta description, and 77% had no sitemap.xml. Overall grades: 13% A, 69% B, 16% C, 2% D, with a median score of 84/100.
The 5-minute pre-launch checklist
- Search your built bundle for
sk-,sk_live_,service_role. Rotate anything you find and move it into a server or edge function. - Supabase: turn on RLS for every table and remove any
using (true)policy. Firebase: no test-mode rules. - Add a CSP and
frame-ancestors 'none'. Start withContent-Security-Policy-Report-Onlyif you're nervous. - Turn off production source maps, or don't upload them publicly.
- Add OG tags, a meta description and a sitemap so your launch post actually gets clicks.
Paste a URL to get a score, leaked-key detection, and copy-paste fix prompts for Cursor or Lovable. Apps that score A or B get a free "Scanned by ShipSniffer" badge.
Scan my app free →
Method
Sample: 155 URLs from Show HN posts that mention Lovable, Bolt, Replit, v0, Cursor or "vibe coded", collected in October 2026. 99 were reachable. 56 were dead, blocked or erroring, including 41 platform-hosted apps returning 404. Each app got a single passive pass: GET requests for the homepage, up to 25 same-site JS bundles, robots/sitemap/favicon, up to 30 internal links, plus /.env, /.git/HEAD and .map files for the first three bundles, at low concurrency. No logins, no database queries, no exploitation. Results are anonymized and reported only in aggregate. Automated scans produce false positives and negatives; treat these numbers as indicative.