CriticalSecurityAuto-fixable

Missing Row Level Security on Supabase Table

A public Supabase table has RLS disabled or has an overly permissive policy, meaning any authenticated user can read or modify every row regardless of ownership.

Typical error

Row level security disabled

What this is

Supabase's Postgres layer supports row level security (RLS). When enabled, the database itself enforces "can this user see this row?" policies on every query. When disabled, the browser-side Supabase client can read any row by default.

AI-built apps running on Supabase commonly ship with RLS either disabled or with a permissive policy such as:

create policy "allow_all" on public.orders
for all using ( auth.role() = 'authenticated' );

That policy lets any logged-in user read every row in orders, regardless of who owns it.

Why AI tools ship this

Prompt-to-app builders like Lovable and Bolt scaffold Supabase apps that "just work" in the dev preview. They confirm the happy path, ship, and skip the policy tuning step.

How to detect

Run in the Supabase SQL editor:

select tablename, rowsecurity
from pg_tables
where schemaname = 'public';

Any table with rowsecurity = false is publicly readable by any authenticated user. Cross-check each one against sensitive data models.

How to fix

Enable RLS on the table, then add an ownership policy:

alter table public.orders enable row level security;
 
create policy "users_read_own_orders"
on public.orders
for select
using ( auth.uid() = user_id );
 
create policy "users_insert_own_orders"
on public.orders
for insert
with check ( auth.uid() = user_id );

Every policy should pin rows to auth.uid(), a foreign key the database can verify. Never trust a userId passed from the client.

Commonly affected tools

Glossary

Is your app affected?

FinishKit checks for this finding and 50+ more across 8 dimensions of production readiness. Free during beta.

Scan your app