Overnight we ran a scan across 226 deployed apps: 126 vibe-coded (Lovable, Bolt, Replit, Tempo, Emergent) + 100 YC companies from recent batches (W24–F25). The goal was a clean head-to-head: same scanner, same modules, same week — what's the actual per-platform risk profile?
The headline
| Cohort | Scanned | With CRIT | Rate |
|---|---|---|---|
| YC companies (W24 → F25) | 100 | 0 | 0% |
| Lovable | 58 | 3 | 5.2% |
| Bolt.host | 30 | 6 | 20.0% |
| Bolt.new | 8 | 1 | 12.5% |
| Replit + Tempo + Emergent | 30 | 0 | 0% |
| Vibe-coded total | 126 | 10 | 7.9% |
Every single CRIT in this batch was the same class of issue: Supabase Row Level Security disabled on tables backing real user data. Not a mix of vulnerabilities — one pattern, showing up again and again.
Why Bolt.host apps fail 4× more often than Lovable
Both products target the same developer with the same backend (Supabase). So why the gap?
Our read, from looking at the exposed apps' JS bundles: Bolt deployments are more often quick prototypes that never got productionized. The giveaway is in the hostnames — trippy-duplicated-6mxq.bolt.host, mobile-liquid-glass-w7cb.bolt.host, ffo-paywallmobile-sa-65qs.bolt.host. Those auto-generated slugs mean the dev clicked "deploy" once to share with a friend, then forgot about it. RLS was never in scope because the app was never serious.
Lovable apps are more often named — ruth-prissman-coach.lovable.app, crmcoach.lovable.app, engagementsurvey.lovable.app. They belong to someone. The rate is lower, but the consequences per leak are higher, because it's a live business with real paying customers on the other side.
The table names tell you exactly how this happens
Looking across the 51 world-readable tables in these 10 apps, the names are almost all tutorial-style. Generic primitives that appear on every Supabase "build-your-first-app" guide: users, profiles, sessions, categories, subscriptions, comments, coaches, players, teams.
Only two tables appeared in more than one app in our sample (players and categories, each in 2 apps) — everything else is unique to its app. But the shape of the names is the same everywhere. These are the names you get when you start from the Supabase docs, get RLS working on your first tutorial table (usually profiles or todos), then build 10 more tables without touching the RLS toggle again. The Supabase dashboard shows each new table in green whether RLS is on or off, which makes the error silent.
The fix on Supabase's side is a single default-flip: new tables with RLS on by default instead of off. The dashboard has had an opt-in for this for years, but the default remains off — which is what produces findings like these at scale.
The worst apps we found
Three stood out for sheer volume of exposed data:
- ruth-prissman-coach.lovable.app — 15 tables world-readable. The app is a personal coaching site for a therapist in Israel. The exposed tables include
payment_methods,future_sessions,content_subscribers, andemail_delivery_attempts. Real paying clients, real PII. - videozenithuygulamasi.lovable.app — 9 tables. Turkish live-streaming platform with
live_chat_messages,profiles,subscriptions. Every chat message every user has ever sent is readable with one curl. - crmcoach.lovable.app — 8 tables. Hebrew coaching CRM with
user_roles,coaches,sessions,session_summaries. Plususer_rolesis writable, so an attacker can grant themselves admin on any account by sending one INSERT.
All three were emailed disclosures the morning after the scan. At the time of writing, none have responded yet; we'll update this post if/when they do.
What YC got right
Zero CRITs across 100 YC companies — W24, S24, F24, W25, S25, F25. That's a striking result and worth unpacking.
It's not that YC companies run fancier security programs. A few of the 100 we scanned are 3-person teams that started 8 months ago. But they've all been through YC's Bookface / office-hour culture where one of the first things you hear from other founders is "don't ship the anon key with RLS off." That kind of informal transmission — the thing a YC cohort gives you that a vibe-coder downloading the Lovable starter template doesn't — is what's actually protecting these apps.
The YC apps did have findings: missing security headers, exposed /docs endpoints, CORS misconfigurations. But nothing where an attacker could drop a curl and walk away with customer data. There's a real difference between "not perfectly hardened" and "catastrophically exposed," and this batch makes the gap quantitative.
One embarrassment for StackBlitz
In a small bit of irony: buildwith.bolt.new — the StackBlitz-owned admin console for their "Build with Bolt" workshop program — has the same RLS misconfiguration. Its coupons table is anon-readable AND anon-writable. That table appears to hold Bolt Pro redemption codes. Anyone can harvest the codes or insert new ones and redeem them.
We didn't disclose to StackBlitz security because this post is about pattern, not piling on. But it's a good illustration that the bug doesn't respect maturity — if it can hit the platform's own internal tool, it can hit yours too.
What this means if you're shipping
If you're building on Lovable, Bolt, or Replit with Supabase, the one thing to do today is audit every table in your project — not just the one from the tutorial:
SELECT schemaname, tablename,
CASE WHEN rowsecurity THEN 'ON' ELSE 'OFF' END AS rls
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY rowsecurity, tablename;
Anything showing OFF needs:
ALTER TABLE <table> ENABLE ROW LEVEL SECURITY;
CREATE POLICY "authenticated_only" ON <table>
FOR SELECT USING (auth.uid() IS NOT NULL);
Tighter policies are obviously better — this is the minimum. If you want to check the full attack surface of your app in one shot, run a scan; one free, no card.
Methodology
Targets sourced from: certificate transparency logs (*.lovable.app, *.bolt.host, *.replit.app, *.bolt.new, *.tempo.new, *.emergent.sh) for the vibe-coded cohort; YC's public directory for the YC cohort. All 226 unique — no overlap with our previous 150-target batch.
Scanner: our standard full-scan module set (50+ checks including supabase-audit, baas-detect, secret-scan, nuclei, subdomain-takeover, github-dork, AI-triage).
Every CRIT was verified reproducible before disclosure — we re-ran the exact curl command the scanner used, confirmed a real row came back, and used that specific command in the disclosure email to the owner.
Runtime: ~4 hours wall time at 10 concurrent scans on a t3.2xlarge. Zero scan failures out of 226.