DEV Community

Cover image for Lovable's security scan passed. Here's the 3-query check it doesn't do
Hristijan
Hristijan

Posted on AI-assisted

Lovable's security scan passed. Here's the 3-query check it doesn't do

This month a study of 159,041 Lovable projects made the news: passport numbers, payment details and more than 200,000 private chat messages that anyone could read from a browser, without logging in. Different write-ups count it differently (7,403 sites with readable email data in one, about 24,600 Supabase-backed apps returning records without auth in another), but the cause is the same everywhere: database access rules.

Not a hack of Lovable. Not a hack of Supabase. Tables that said "anyone may read me", and people who read them.

If you build on Lovable, you probably thought: my app runs the security scan on every publish, so I'm fine. Maybe. Here's the gap.

What the scan checks, and what it doesn't

Lovable's scan builds on the Supabase Security Advisor. The Advisor is good at one thing: telling you a table has row level security (RLS) switched off.

It does not judge what a policy allows. This passes:

alter table public.messages enable row level security;
create policy "read messages" on public.messages for select using (true);
Enter fullscreen mode Exit fullscreen mode

RLS is on. The policy says using (true): every caller, including anyone holding your public anon key, can read every row. Your public key is in your website's JavaScript, so "anyone" really means anyone.

Another write-up here on dev.to reviewed 38 public Lovable codebases in October: only 2 of 826 tables had RLS off, but 10 of the 38 apps (26%) had a policy that let anyone, or any signed-in user, update or delete every row of at least one table. The switch was on. The rules were open.

The 3-query check (15 minutes, read-only, your own project)

1. The logged-out read test. Use only your project URL and your public key:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*&limit=1" \
  -H "apikey: YOUR_PUBLIC_KEY"
Enter fullscreen mode Exit fullscreen mode

Rows coming back means anyone can read that table. Repeat for every table with private data.

2. Tables with RLS off (Supabase SQL editor):

select c.relname
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'r' and not c.relrowsecurity;
Enter fullscreen mode Exit fullscreen mode

Every name it returns is open.

3. What every policy actually allows:

select tablename, policyname, cmd, roles, qual, with_check
from pg_policies where schemaname = 'public' order by tablename;
Enter fullscreen mode Exit fullscreen mode

Look for qual or with_check equal to true, and for policies on the anon or public role, on anything private.

If something was open

Fix first: replace open policies with owner rules (using (auth.uid() = user_id) for select, update and delete; with check (auth.uid() = user_id) for insert). If a service role or sb_secret_ key was ever in your front end, rotate it. Then look at the API logs in the Supabase dashboard for reads you didn't make, and if EU or UK users' personal data may have been read, check whether you have to notify a regulator (often within 72 hours).

A fix prompt you can paste into Lovable:

Enable row level security on every table in the public schema. For each table that holds user data, add policies so users can only select, update and delete rows where user_id = auth.uid(), and only insert rows with their own user_id. Remove any policy that uses true for update or delete. Then list every table and its policies so I can check them.

One more thing: the key in your website

The anon key, or the newer sb_publishable_ key, in your JavaScript is normal. It's designed to be public. The one that must never be there is the service role key (or sb_secret_), because it skips RLS entirely. If you're not sure which one you have, decode it: the JWT payload says "role": "anon" or "role": "service_role".


I wrote the longer version, with what to do after a leak, here: https://redproof.dev/guides/lovable-data-exposure-check. I build Redproof, a security checker for apps made with AI tools. There's a free check by URL (no GitHub needed) and a key checker that runs only in your browser on the same site.

Top comments (0)