Guide

Why Lovable apps ship with the database open

It isn’t carelessness, and it isn’t a bug in Lovable. The database is open because it has to be while you’re building, and nothing in the process ever closes it again.

The mechanism

When your builder wires up Supabase, it gives your app two things: the project URL and the anon key. Both are public by design. They ship inside the JavaScript that every visitor’s browser downloads, and they’re supposed to — that’s how a browser app talks to a database at all.

What’s meant to stand between that public key and your users’ data is Row Level Security: rules on each table saying who may read which rows. With RLS on and no policy written, a table returns nothing. Which means that on day one of building, with RLS on, your app appears completely broken — you add a table, you fetch from it, you get an empty array.

So the fastest way past that wall is to turn RLS off. Every tutorial does it. The AI does it. It works instantly, and you get on with building the thing you actually wanted to build.

Then you ship. Nothing in the deploy asks whether you ever went back. The app works perfectly — for you, for your users, and for anyone else who opens it.

What that actually means

Not “a hacker could eventually get in.” There is nothing to get into. The key is already in the page, and with RLS off the database answers whoever asks. Anyone who opens your app, opens DevTools, and copies two strings can read every row of every table — emails, names, phone numbers, addresses, Stripe customer IDs, whatever you store. No login. No account. No exploit.

It takes about fifteen seconds, and it is the single most common serious problem we find.

Check your own app in 30 seconds

  1. Open your live app and press F12 to open DevTools.
  2. Go to the Network tab and reload. Look for a request to a URL ending in .supabase.co.
  3. If you see one, your app talks to Supabase from the browser — which is fine and normal.
  4. Now go to your Supabase dashboard → Table Editor. Any table showing Unrestricted next to its name is readable by the public, right now.

That badge is the whole answer. If it says Unrestricted on a table holding anything about a person, that data is public.

The fix

Paste this into your builder. It’s the same prompt Assay hands you when it finds this:

Turn on Row Level Security for all my tables in Supabase. For each table (users, orders), enable RLS and add a policy so that a user can only read and edit their own rows (where the row’s user_id equals auth.uid()). Do not allow public/anonymous read access to any table containing personal data.

Then check the Table Editor again. Every table holding personal data should have lost the Unrestricted badge. If your app breaks after this, that is the point — it was reading rows it had no business reading, and now it has to prove who it is first.

What we’ve actually seen

Being straight about the sample: Assay is new. Across 26 completed scans so far, 8.3% came back with at least one issue rated risky or worse, and two of those were critical. That is a small number of scans, most of them on custom domains where we can’t tell which builder made the app, and it is nowhere near enough to tell you what share of Lovable apps in general have this problem.

What it is enough for: when this problem shows up, it is almost never alone and it is almost always the worst thing on the report. We’ll publish a real rate when we have the scans to back one.