A fifteen-minute manual Supabase audit — and where it stops
Auditing your own Supabase project by hand is worth doing and entirely feasible: every check below runs in psql or curl against information you already have. Fifteen focused minutes surfaces the failure classes that matter most — unprotected tables, decorative policies, open buckets, exposed keys.
The page gives the ordered checklist first, then the honest part: what a manual audit structurally cannot deliver. A snapshot is true once; databases change weekly; and the audit's ceiling is the discipline of whoever remembers to re-run it. Knowing the ceiling is what makes the automation decision easy.
RowShield does not detect this yet. This guide gives you the catalog queries to check it yourself. The nine rules that do ship are listed on the rules index.
The checklist
Start structural, because structure decides everything else. Connect with any read-capable role and ask the catalog directly which tables in the public schema lack row-level security entirely, and which have it enabled but do not force it against the table owner:
The query needs no privileged credential — it reads pg_catalog, the same metadata view RowShield's own scanner uses. Run it before touching anything else on the list, because a single false in the rls_enabled column outweighs every remaining step combined.
SELECT n.nspname AS schema,
c.relname AS table_name,
c.relrowsecurity AS rls_enabled,
c.relforcerowsecurity AS forced
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'p')
AND n.nspname = 'public'
ORDER BY c.relname;Policies, buckets, keys, behaviour
Any row with rls_enabled false is finding number one. Then read the policies themselves — pg_policies shows qual and with_check per policy — looking for conditions that reduce to constant true, INSERT commands without a WITH CHECK, and policies scoped to roles that bypass row-level security regardless. Next, storage: list buckets and their policies; a bucket readable under the public key is the table problem wearing different clothes.
Finish behavioural. Request your own API unauthenticated — the anon key is public by design — and note which endpoints return rows. Grep your deployed frontend bundle for credential shapes: a service_role key or sb_secret_ string in browser-reachable code outranks everything else on this list and means rotation comes before analysis.
Write the outputs down with dates as you go, in one place a colleague could find unaided. Undocumented results evaporate within a week; dated ones accumulate into something better than evidence for others — a record you can diff against next quarter without relying on memory.
Reading the results honestly
Two misreadings account for most false comfort. First, rls_enabled true proves less than it appears to: the flag says policies exist to consult, not that they grant sensibly — a tautology passes this checklist unless you read the policy text. Second, an empty response from the API proves nothing: filtered-out and genuinely empty are indistinguishable from outside, so treat empties as ambiguous rather than clean.
Conversely, resist panic in the other direction. RLS enabled with zero policies denies everything to everyone — that breaks your application quietly instead of leaking loudly, and it wants a different fix than an exposure does. Distinguishing the three core failures — disabled, no-policies, tautological — is most of the diagnostic work.
Where the ceiling sits
Everything above is a photograph. It documents one afternoon; migrations land the next morning, and the audit says nothing about Thursday. Manual repetition decays predictably — thorough the first month, skipped the month the roadmap got loud — which is not a character flaw but the predictable behaviour of unpaid recurring work.
The ceiling, stated plainly: a manual audit cannot watch the intervals, cannot diff against last month, and cannot alert anyone when a fix regresses. Those are properties of a process, not of diligence, and no checklist edits its own re-run schedule.
Automating the repeat
Every check above maps onto RowShield's shipped automation: nine catalog rules covering the structural findings, a probe covering the behavioural ones, and scheduled scans that record created, regressed and resolved transitions between runs. The CLI runs the same engine locally with CI-safe exit codes — 0 clean, 1 findings at or above threshold, 2 broken run:
Keep the manual habit for onboarding — walking the checklist once teaches more than any tour — but let the machine hold the schedule afterwards. Findings and remediation SQL are never withheld on any tier; the Free plan watches one project with daily scans and email alerts. RowShield is an independent product, unaffiliated with and not endorsed by Supabase. Start with the free audit at rowshield.dev/audit: paste a URL, read the findings.
rowshield scan --db-url "$DATABASE_URL" --fail-on high --sql # exit 0 = clean, 1 = findings at/above threshold, 2 = run failed rowshield probe https://your-app.example.com # outside-in check using only the public anon key
Frequently asked
- How long should a manual audit of a small Supabase project take?
- About fifteen minutes for the checklist above on a modest schema: the catalog query, a read of every policy, a bucket listing, one unauthenticated request per important endpoint, and a search of the deployed bundle for privileged credentials.
- Does a clean manual audit mean the project is secure?
- It means the project was clean at the moment of the audit, within the scope of the checks performed. Configuration drifts, deploys continue and the snapshot ages immediately — which is why scheduling matters more than thoroughness of any single run.
- Which finding should I fix first if several appear?
- An exposed privileged key outranks everything, because it bypasses every policy at once — rotate first, investigate second. After that, disabled row-level security and anonymously readable tables precede performance-adjacent findings such as unindexed predicates.
Check your project in about ten seconds
Paste a URL. No signup, no writes, nothing stored.
Run the free audit