// guide

RLS enabled but still leaking? 5 policy mistakes to check

Turning on Row Level Security is step one. What actually protects your data is the policies. A common report from vibe coders: RLS is "enabled", yet a normal signed-in user can still load other people's profiles. These are the usual causes.

1. A policy that just says true

USING (true) means every role the policy applies to can see every row. RLS is on, but it filters nothing.

✗ leaks

create policy "read" on profiles
  for select using (true);

✓ fixed

create policy "read own" on profiles
  for select to authenticated
  using (auth.uid() = user_id);

2. Checking 'signed in' instead of 'owner'

auth.role() = 'authenticated' only proves someone has an account. Anyone can sign up, so anyone can read.

✗ leaks

using (auth.role() = 'authenticated')

✓ fixed

using (auth.uid() = user_id)

3. Locking reads but not writes

A select policy doesn't cover insert or update. Without WITH CHECK, users can write rows owned by someone else, or change user_id on their own row.

✗ leaks

create policy "update" on orders
  for update using (auth.uid() = user_id);

✓ fixed

create policy "update own" on orders
  for update to authenticated
  using (auth.uid() = user_id)
  with check (auth.uid() = user_id);

4. Trusting user-editable metadata for roles

user_metadata can be changed by the user. A policy that checks it for 'admin' lets anyone make themselves admin.

✗ leaks

using ((auth.jwt()->'user_metadata'->>'role') = 'admin')

✓ fixed

-- keep roles in a separate table users can't write
using (public.has_role(auth.uid(), 'admin'))

5. A table nobody turned RLS on for

New tables, join tables and views are easy to miss. One unprotected table is enough to leak data.

✗ leaks

create table invoices (...);  -- no RLS

✓ fixed

alter table invoices enable row level security;

How to test it yourself

  1. Create two test accounts, A and B.
  2. As A, create some data.
  3. Sign in as B and try to read, edit and delete A's rows, from the app and straight through the API with B's token.
  4. Repeat signed out. Anything that comes back is a leak.

Check your schema

Paste your SQL schema into the Deep Audit. It looks for these patterns and runs in your browser, so your code isn't uploaded. ShipAudit AI checks common launch risks; it is not a full security review.