Supabase RLS Audit: is your data actually isolated?

Row-level security is the single most important security feature in a Supabase app, and the single most common thing AI builders get wrong. If your app was built with Lovable, Bolt, Replit, Cursor, or Claude Code on Supabase, your RLS policies are your security. Everything else is decoration.

Here's why it matters in one sentence: Supabase lets your frontend talk to the database directly, so there is no backend standing between a curious user and your tables. Only the policies. I'm Terry Threatt, an engineer who runs Supabase in production, and auditing RLS is the core of every audit I do.

  • Fixed price
  • No sales call
  • Report in days
  • Written for founders, not engineers

The seven RLS mistakes that keep showing up

These are the patterns AI builders keep producing. If you check nothing else, check these:

1. RLS is simply off.

A table without row-level security enabled is readable and writable by any request with your anon key, which ships in your frontend, so that means everyone. One unprotected table is a full data exposure.

2. Policies exist for SELECT and nothing else.

AI tools love writing a read policy and stopping. Without INSERT, UPDATE, and DELETE policies, users can create, modify, or destroy rows they should never touch.

3. USING (true).

A policy that evaluates to true for everyone is RLS theater: the checkbox is on, the protection is absent. Policies need to actually scope to auth.uid().

4. Views that bypass RLS.

Views run with their creator's privileges unless security_invoker is set. A perfectly locked-down table can leak completely through an innocent-looking view.

5. SECURITY DEFINER functions.

Database functions marked SECURITY DEFINER skip RLS entirely. Sometimes that's intentional; in generated code, it's often an accident with admin-level consequences.

6. Storage buckets with their own rules, or none.

Bucket policies are separate from table policies. User uploads frequently sit in buckets where any user can fetch any file by guessing the URL.

7. Forgetting what the anon key can see.

Your anon key is public by design. The question is what a logged-out request can query with it, and the answer is often more than you think.

The two-account test: the check that finds most critical bugs

If you do one thing after reading this page, do this. Create two accounts in your own app, Account A and Account B. Log in as A, open your browser's dev tools, find a request to Supabase, and replay it asking for B's data: change an ID, widen a filter, request the whole table. If anything belonging to B comes back, you have a critical data-isolation bug.

This test works because it goes around your UI. Your interface only shows users their own data, which makes everything look fine. But attackers don't use your interface. They use the API underneath it, and so do I when I audit.

The catch: doing this thoroughly means replaying requests against every table, every operation, every view, every RPC, and every storage bucket, then reading each policy line by line. That's the part founders understandably don't want to spend a week learning, and it's exactly what the audit does for you.

What a professional RLS audit adds

In every audit tier, I list all your tables and check RLS status on each, read every policy for real scoping (not USING (true)), verify policies exist per operation, check every view for security_invoker, review SECURITY DEFINER functions, test storage buckets by trying to fetch another account's files, confirm realtime subscriptions respect your policies, and probe what the anon key exposes to logged-out visitors.

Then everything gets the two-account treatment against the live API. Findings arrive ranked by severity, in plain English, each with a fix prompt you paste into the AI tool that built your app, plus a verify step so you can prove the fix landed.

What it costs

RLS review is included in every tier; it's the foundation. Prices are fixed and published:

PackagePriceTurnaroundBest for
Launch Scan$29948-hour turnaround“I just want to know if anything is on fire.”
Launch Audit$9995 business days“Real users are about to trust me with their data.”
Launch + Revenue AuditMost popular$1,9005-7 business days“This app is supposed to make money. Prove the pipes are connected.”
Ship Monitor$149/monthOngoingeveryone who just passed an audit and plans to keep building.
See everything included in each tier

Frequently asked questions

If I enable RLS on every table, am I done?

Enabling RLS is step one of two. A table with RLS on but no policy denies everything (your app breaks visibly, which is at least honest); a table with a bad policy allows too much (silent, dangerous). The policies themselves need reading. That's where the real bugs live.

Is my Supabase anon key being public a problem?

No, it's designed to be public. The anon key plus correct RLS is a sound model. The problem is the anon key plus missing or permissive RLS, which turns 'public by design' into 'database open to the internet.'

My AI tool says it enabled RLS. Should I trust it?

Trust it the way you'd trust 'I locked the door' from someone who has never been robbed: verify. The tools do usually enable RLS, so mistake #1 is becoming rarer. Mistakes #2 through #7 are alive and well, and they don't show up unless someone reads the policies and runs the two-account test.

Can you just audit my RLS and nothing else?

The Launch Scan ($299) is the closest fit; it covers the highest-risk checks including a data-isolation pass. But RLS problems rarely travel alone; the full Launch Audit exists because exposed keys and unenforced auth routes tend to show up in the same apps.