A Lovable app is safe to launch when six things hold: every table has row-level security with policies you have read, no secret key ships in the browser bundle, Supabase Auth has confirmation and leaked-password checks on, storage buckets are private with per-user policies, third-party keys live in Edge Functions, and a backup exists that you have restored once. Lovable’s built-in scan covers part of this. The rest takes about an hour with the Supabase dashboard open.

TL;DR

Six checks, in the order a senior architect runs them on a Lovable + Supabase app:

  1. RLS on every table, with policy bodies you have read, not just enabled
  2. No service_role or sb_secret_ key anywhere the browser can see
  3. Auth settings: email confirmation on, leaked-password protection on, real SMTP
  4. Storage buckets private, with upload and read policies scoped to auth.uid()
  5. Third-party keys in Lovable Secrets and Edge Functions, never in the client
  6. A backup you have restored, which means a paid Supabase plan before real users

Why the question is still open in 2026

On March 21, 2025, Matt Palmer reported to Lovable that generated apps were shipping without row-level security. The CVE, CVE-2025-48757, was published on May 29, 2025 after a 45-day window. The follow-up scan, summarized by Superblocks on September 3, 2025, found 170 of 1,645 Lovable apps (10.3%) with exposed databases across 303 endpoints: user names, emails, third-party API keys, transactions.

Lovable shipped a security scan on April 24, 2025 and, per its founder’s guide of March 24, 2026, now enables RLS by default and runs four scanners. Its own docs still say the tools “cannot guarantee complete security”. Red Access scanned 380,000 public assets on vibe-coding platforms in May 2026 and found 2,000+ holding sensitive corporate or personal data.

The mechanism has not changed. Your app’s browser code talks to Supabase directly with a public key, and RLS is the only thing between that key and every row.

flowchart LR B["Browser with publishable key"] --> API["Supabase REST API"] API --> RLS{"RLS enabled with a real policy?"} RLS -->|"Yes"| OK["Only the caller's own rows"] RLS -->|"Enabled, USING (true)"| ALL["Every row, to anyone"] RLS -->|"Disabled"| ALL S["service_role key in the bundle"] -.->|"bypasses RLS entirely"| ALL

The six checks

1

RLS with real policies

Supabase’s Security Advisor flags every public table without RLS as an error (lint 0013). Then read each policy. USING (true) on a SELECT is “enabled” and still public.
2

No secret key in the client

Search the built JavaScript for service_role and sb_secret_. Secret keys bypass RLS entirely. Found one: rotate first, then move the code to an Edge Function.
3

Auth settings

Email confirmation on. Leaked-password protection on (Pro plan and above). Custom SMTP: the default sender is capped at 2 emails per hour.
4

Bucket policies

A public bucket serves any file to anyone with the URL. Keep user uploads private, and scope storage.objects policies to a folder named by the user’s id.
5

Keys in Edge Functions

Stripe, OpenAI, Resend keys belong in Lovable Secrets, called from Edge Functions that verify the caller’s JWT. Anything in VITE_ or NEXT_PUBLIC_ variables is in the bundle.
6

A restored backup

The Free plan takes no automatic backups. Pro keeps 7 daily backups; Storage files are not included. Restore once to staging and write down the date.

Check 1: RLS you have read, not RLS that is enabled

Lovable’s scanner and Supabase’s advisor both answer one question: is RLS switched on? The CVE was about tables where it was off. The 2026 failure is subtler: RLS is on, and the policy the AI wrote lets everyone through because that made the demo work.

Open Authentication, then Policies, and read every policy on every table that holds user data. A policy for a profiles table should mention auth.uid(); a policy that says true for SELECT on orders is the CVE with a green checkmark.

Policy the AI wroteWhat it meansWhat you want
USING (true) on SELECTanyone, signed in or not, reads all rowsUSING (auth.uid() = user_id)
TO authenticated USING (true)any account reads every account’s datasame, plus the role restriction
no INSERT/UPDATE policywrites fail, so the AI may have disabled RLS to “fix” itexplicit write policies, RLS stays on

Check 2: the key that bypasses everything

Supabase has two families of keys. The publishable key (anon, or sb_publishable_ since 2025) is designed to sit in the browser and is constrained by RLS. The secret key (service_role, or sb_secret_) authenticates as a Postgres role with BYPASSRLS and “skips any and all Row Level Security policies”. Legacy anon/service_role JWT keys are deprecated by the end of 2026.

Found in the bundle
A service_role string inside assets/index-*.js. Anyone who opens DevTools has full read and write on every table, and no policy applies.
After the fix
Key rotated in the dashboard (irreversible for the old key). The admin query moved into an Edge Function that checks the caller’s JWT. The bundle only contains sb_publishable_.

New sb_secret_ keys refuse browser requests with a 401, and Supabase auto-revokes secret keys found in public GitHub repos. Neither covers a legacy key or a private repo.

Checks 3 to 5: auth, buckets, and third-party keys

Auth. Two settings decide whether sign-up is a door or a hole. Confirm-email off means anyone registers with any address, including your CEO’s. Leaked-password protection uses the HaveIBeenPwned k-anonymity API and is available on the Pro plan and above; on the Free plan the check does not run. And the built-in mail service is limited to 2 emails per hour, so password resets stop the moment you have users.

Buckets. When a bucket is public, “anyone who possesses the asset URL can readily access the file”; uploads, deletes and moves still need a policy on storage.objects. The pattern that holds is a private bucket with one folder per user, guarded by (storage.foldername(name))[1] = auth.uid(), per the Storage access control docs. Uploaded IDs, invoices and avatars belong there, not in a public bucket with guessable names.

Third-party keys. Lovable now detects API keys pasted into the chat and steers them into Secrets. The earlier version of your app may predate that. Search the repo for sk_live_, sk-, re_ and any VITE_ variable holding a key, then move each call behind an Edge Function that verifies the user’s JWT. The Gmail-connected app checklist walks through the OAuth token case in detail.

Check 6: the backup nobody restored

The Supabase Free plan takes no automatic backups; the docs tell you to run supabase db dump yourself. Pro adds daily backups kept 7 days, Team 14, Enterprise 30. Point-in-time recovery, at $0.137 per hour (about $100 per month) for 7 days, archives the write-ahead log every two minutes for a worst-case loss of about two minutes. Storage objects are not included in any of them.

A backup you have never restored is a hope. Restore last night’s backup into a staging project, log in, and note the date. Reviewers in technical due diligence ask for exactly that date.

The one thing to do today
Open Supabase, go to Advisors, then Security Advisor, and clear every error-level finding before you do anything else. Then view-source your live site and search for service_role. Those two actions close the CVE-2025-48757 class and the key-leak class, which is most of what Red Access found across 380,000 apps.

When this stops being a checklist

Run the six checks yourself before the first paying customer. They become someone else’s job at the trigger moments: an enterprise pilot sends a security questionnaire, an investor starts due diligence, your first engineering hire asks how auth works, an incident happens, or the app slows down under real load. Each of those asks for evidence, not a green scan: the policies, the restore date, the map of which key does what. The five patterns common to most AI-built codebases still apply underneath.

If you would rather have senior eyes on the actual project first, that is what the free teardown is: three findings and a verdict, recorded, within 72 hours.

Sources