The login works. The API underneath doesn't have the same lock.
Lovable builds clean UIs and wires authentication onto the pages. What
it almost never wires up is the same authentication on the API routes
the pages call. The dashboard redirects logged-out users; the
/api/* endpoints behind it stay open.
What we read for in Lovable apps.
The patterns below are characteristic, not universal. Not every Lovable app has all six. Most have several. The free audit narrows down which apply to yours.
- 01
Page-vs-API split
CRITLovable wires auth into middleware that protects /dashboard. The API routes that /dashboard calls have none of it. A logged-out curl returns the data the dashboard would have rendered.
- 02
Supabase RLS off by default
CRITLovable creates tables and leaves Row-Level Security disabled. The database itself doesn't refuse cross-tenant reads, so a single missing API check leaks every customer's rows.
- 03
Unsigned-webhook default
CRITStripe webhook handlers read request.json() and trust the body. No signature verification, no replay protection. Anyone with the URL can mark invoices paid.
- 04
Service-role keys in the client bundle
CRITSupabase admin keys imported into client components. They ship to every browser, granting anyone full database access via the JS console.
- 05
Body-spread inserts
CRITMutation routes spread req.body into .insert(). Users write arbitrary columns: user_id, admin flags, stripe_customer_id. Self-promotion to admin is a single POST away.
- 06
Wildcard CORS
HIGHDefault Next.js CORS opens to *. Any logged-in user, browsing any other site, becomes an unwitting client of your API.
Why Lovable ships these patterns.
This isn't a Lovable bug. It's a consequence of how AI code generators handle prompt boundaries. When the prompt says "build a SaaS with auth and a dashboard," Lovable generates each piece in isolation: a login flow, a middleware, an API route, a database schema. Each piece is correct on its own. Nothing wires the auth check to the API route, because nothing in the prompt asked for it.
Every fix we ship in the Sprint includes the negative tests for the gap we just closed, so the next time you prompt Lovable to extend the feature, the test catches the regression.
The user gets a working dashboard with multi-tenant-looking code, and a database that has no idea tenants exist.
From the Lovable RLS teardown
What this approach won't catch.
We read for the patterns Lovable ships by default. We don't catch bespoke business-logic bugs you wrote on top of Lovable's scaffolding. We don't audit complex permission systems that go beyond what the generator produced. If your app has heavy custom code, the audit still finds the AI-generated layer's gaps, but the custom layer needs a different kind of review.
- Bolt.new code review: webhooks, missing migrations, hard-coded dev URLs.
- Cursor code review: refactor drift across sessions, optimistic-update races.