If you built your app on Lovable, there’s a good chance you never touched a single line of backend security code — and that’s exactly the problem. Knowing how to check if your Lovable app has RLS enabled is one of the fastest ways to find out whether your user data, payment records, or private messages are sitting open to anyone on the internet. This guide walks you through the exact steps, directly in Supabase, to confirm your tables are actually locked down.
Prerequisites
Before you start, gather a few things so you’re not hunting for logins mid-check.
Access to your Lovable project and Supabase dashboard
Lovable apps run on Supabase under the hood, so you’ll need direct access to that project — the Lovable chat interface is not a substitute for checking the database’s security settings yourself.
Admin or owner-level login credentials
Viewer-level access won’t let you see or change policies. Make sure you’re logged in as an owner or admin on the Supabase project.
Basic understanding of what Row Level Security (RLS) does
Row Level Security is a Postgres feature that restricts which rows a user can read, insert, update, or delete based on policies you define. Without it, anyone with your public API key can query your entire table.
A list of every table in your app’s database
Write down every table name — users, payments, messages, orders, whatever your app stores. You’ll check each one individually.
Step 1: Log Into Your Supabase Project Dashboard
Find the database linked to your Lovable app
Open your Lovable project’s integration or backend settings, find the connected Supabase project, then log into that project directly at supabase.com.
Navigate to the Table Editor view
Click “Table Editor” in the left sidebar. This lists the tables in your database, and tables without RLS are typically flagged.
Confirm you’re looking at the production project, not a staging copy
Check the project name and URL at the top of the dashboard. Founders sometimes audit a dev branch by mistake and miss the live table that’s actually exposed.
Step 2: Open the Authentication and Policies Panel
Locate the ‘RLS’ toggle per table
In Table Editor, RLS settings are available per table, and tables without RLS are typically flagged with a label or badge. Open the table’s RLS or policies view to see its status.
Identify tables marked ‘Unrestricted’
An “Unrestricted” label (or an “RLS disabled” warning, depending on your dashboard version) is a direct red flag. It means RLS is off, and a table in an exposed schema without RLS is readable and writable through the API by any role with a grant on it, which by default includes the public anon key — no login required.
Understand what ‘Unrestricted’ actually exposes to the public internet
This isn’t a theoretical risk. In a June 2026 scan of 1,072 vibe-coded apps built on Supabase, Symbiotic Security found that 98% had at least one security flaw, and 172 of them (about 16%) allowed unauthenticated data deletion. An “Unrestricted” table on your dashboard is the kind of gap behind numbers like these.
Step 3: Check Each Table for Enabled RLS Status
Click into tables holding user data, payments, or messages first
Prioritize the tables that would cause the most damage if leaked — profiles, subscriptions, orders, direct messages. Work through the lower-risk tables afterward.
Note any table where RLS shows as disabled
Keep a running list. You’ll need it later when you write or request policies, and when you decide whether a DIY fix or a hardening pass makes more sense.
Flag tables with RLS enabled but zero policies defined (equally dangerous)
This is the trap most founders miss. Per Supabase’s documentation, once RLS is enabled, no data is accessible through the API with the anon or authenticated roles until you create policies, so a table with RLS on and no policies is locked rather than open. The catch is the opposite one: your app will appear broken because it can’t read or write anything, and a rushed fix is often a policy like using (true), which reopens everything. Treat a table with no policies as unfinished work, and check any policy you add.
Step 4: Review the Policies Attached to Each Table
Check if policies restrict rows by authenticated user ID
A good policy looks something like this, tying row access to the logged-in user:
create policy "Users can view own data" on profiles for select using (auth.uid() = user_id);
Look for overly permissive policies using ‘true’ as the condition
A policy like using (true) means every row is visible to every request, authenticated or not. This pattern is easy to end up with in AI-generated schemas because it’s a fast way to make a demo “work” without errors — it just happens to work for attackers too.
Test whether anonymous (anon) key access is unintentionally allowed
Check whether policies reference authenticated role only, or whether they also grant access to anon. If anon is included on a sensitive table, that’s a problem worth fixing immediately.
Step 5: Run a Live Test From Outside the App
Use the Supabase API endpoint with the public anon key in a tool like Postman
Dashboard settings tell you what should happen. A live request tells you what actually happens. Grab your project URL and anon key from your project’s API settings, then send a GET request for one of your own tables (replace your_table with a real table name in the public schema):
curl "https://yourproject.supabase.co/rest/v1/your_table?select=*&limit=5" -H "apikey: YOUR_ANON_KEY"
Attempt to read or delete a row without logging in
Insert a throwaway test row yourself, then try to delete only that row with a DELETE request that carries just the anon key, adding the header Prefer: return=representation so the response shows what was deleted. Never test against a real customer record. If the row comes back in the response, your table is exposed regardless of what the dashboard badge says.
Confirm whether the request is rejected or silently succeeds
With RLS working, a read usually returns an empty list [] or a permission error, and a blocked delete removes nothing. A 204 status alone doesn’t prove anything, because a delete that matched zero rows can also return 204, so check whether the row is actually gone. If real rows come back in a read, or your test row is deleted, you’ve found a genuine exposure, not just a dashboard label.
Step 6: Cross-Check Against Known Vibe-Coding Failure Patterns
Compare your findings to published scan results
Security firm Wiz has separately reported risks in roughly 20% of the vibe-coded apps it examined, a much lower figure than the Symbiotic scan, since the two studies used different methods and samples. Either way, if your audit turned up even one unrestricted table, you’re far from alone.
Check for unauthenticated delete access specifically
Delete access is the most damaging gap because there’s no undo. Confirm specifically whether your anon key can remove rows, not just read them.
Watch for temporary or auto-generated rules with hidden expiration dates
This isn’t unique to Supabase-backed builders. Firebase’s “test mode” security rules leave a Firestore or Realtime Database open to anyone until an expiration date (30 days by default), after which access is cut off. Apps built with Firebase, including through tools like FlutterFlow, can end up on those rules if nobody replaces them with proper ones. If your product uses Firebase, check for this pattern too.
Troubleshooting Common RLS Issues
What to do if RLS is disabled on a live production table
Enable it immediately, then add restrictive policies for the access your app genuinely needs. With RLS on and no policies the API returns nothing, so your app may stop working until policies are in place; test the app after every change.
What to do if policies exist but reference the wrong user field
A common mistake is writing auth.uid() = id when the actual foreign key column is user_id. Double-check column names against your schema rather than assuming the generated policy matches.
What to do if you’re not sure how to write a secure policy yourself
Don’t guess on a production database holding real customer data. A misconfigured policy can look fine in testing and still leak data in production, and security gaps in AI-built apps have cost founders real money, as in the case of EnrichLead.
Next Steps: When to Call In Help
Signs your app needs a professional hardening pass
- You found one or more “Unrestricted” tables holding user or payment data
- Your live anon-key test returned real data or a successful delete
- You’re not confident writing or auditing Postgres policies yourself
Why DIY fixes can introduce new gaps
A rushed policy fix can lock out legitimate users just as easily as it blocks attackers, and founders who patch under pressure often skip the live re-test step entirely. For more on how these failures play out in real launches, see our breakdown of the Lovable data breach and the full breach timeline case study.
How a fixed-price audit sits between DIY scanners and a full agency rebuild
Many founders sit between two options: a low-cost monitoring tool that flags problems without fixing them, or a full agency rebuild. SuperApps Vibe Coding Fixes was built for the gap in between — a fixed-price pass that audits your RLS policies, payment webhooks, and auth flow, and fixes what’s broken without a ground-up rebuild. If you’ve already spotted exposed tables using this checklist, that’s the fastest next step before a real attacker finds them first.
Ready to put this into practice? Get your app checked.
Want help closing the gaps this checklist finds? See what SuperApps Vibe Coding Fixes covers.
Get new posts by email
Pick the topics you care about — we only email you when something new is published in them.
