Sample audit report

This is a real audit performed on a live production app (a university dorm-management system), anonymized. Every finding carries its 30-second verify query — that's the standard format you receive. Owners were notified the same day.

RLS Spot Check — client: [redacted], dorm-management SaaS, Supabase + Next.js

SeverityFindingTable / file
HIGHStaff table fully readable by anonymous userspublic.staff
HIGHCleaning/duty schedule readable by anyonecleaning_schedule
MEDIUMAny authenticated user can rewrite the duty schedulecleaning_schedule
HIGH

The staff table is readable by anyone with the anon key

In supabase/migrations/…_initial_schema.sql:

CREATE POLICY "Anon email existence check"
ON public.staff FOR SELECT
TO anon
USING (true);

The comment says it was added for the login page's email check. But USING (true) on FOR SELECT TO anon exposes every column of every staff row: email, phone_number, full_name, staff_id, role, assigned_floor, assigned_gender.

Verify (30 sec) — Supabase SQL editor:

select policyname, roles, cmd, qual
from pg_policies
where tablename = 'staff';

You'll see roles = {anon} and qual = true.

Fix: don't open the table for a lookup — wrap the lookup:

create or replace function public.staff_email_exists(email text)
returns boolean language sql security definer
set search_path = public as $$
  select exists(select 1 from public.staff s
               where s.email = staff_email_exists.email);
$$;

revoke all on public.staff from anon;
drop policy "Anon email existence check" on public.staff;
MEDIUM

Any authenticated user can rewrite the duty schedule

CREATE POLICY "Allow authenticated insert/update" ON cleaning_schedule
    FOR ALL TO authenticated USING (true) WITH CHECK (true);

Any student account can insert, rewrite, or erase duty-roster rows. Integrity loss among authenticated users.

Fix: scope writes to the staffing role and a row predicate: TO authenticated USING (exists (select 1 from staff s where s.id = auth.uid() and s.role in ('admin','tarbiyachi'))).

What the full tier adds

Grants & role exposure review (the gates around RLS), write-path simulation on a fixture clone of your schema, and remediation SQL per finding with severity-ranked fix-hours.

Run the free check on your repo →