Supabase security audit tool: how to audit your Supabase project for free
Short answer: the best free Supabase security audit tool is already in your project: the Security Advisor. Run it, fix every error-level finding, review the warnings, then do two things it can't: test your data as a stranger and as two different users, and check what your live website exposes. This guide walks through all of it. Only audit projects you own.
Where SniffPup fits: Supabase's advisors look at your database from the inside. A free SniffPup scan looks at your live URL from the outside, passively: secret keys in your HTML and JavaScript (including Supabase service_role and sb_secret_ keys, which bypass RLS), public /.env and /.git files, a public source map and your security headers. It never touches your database. Use both. For RLS itself, see the Supabase RLS checker guide.
1. Run the advisors
Supabase's advisors are deterministic checks that ship with the platform. You can run them four ways:
- Studio: Advisors, then Security Advisor (and Performance Advisor).
- CLI:
supabase db advisors - MCP: the
get_advisorstool withtypeset tosecurity, so your AI coding agent can run the audit and read the results. - The Management API, if you want it in a script or CI.
Supabase's own advisor docs explain every lint and most include the SQL to fix it.
2. Fix the error-level findings first
These are the security lints Supabase marks as ERROR. Each one can expose data to anyone with your project URL and public key:
- Table publicly accessible (
rls_disabled_in_public): RLS is off on apublictable, so anyone with the URL can read, edit and delete its rows. Fix:alter table public.your_table enable row level security;then add policies. - Sensitive data publicly accessible (
sensitive_columns_exposed): the same problem on a table with columns named like passwords, tokens, API keys or personal IDs. - View bypasses row-level security (
security_definer_view): recreate the viewwith (security_invoker=on). - User data exposed through a view (
auth_users_exposed): a view overauth.users. Use apublic.profilestable filled by a trigger instead. - Security policy relies on user-editable data (
rls_references_user_metadata): users can change their ownuser_metadata, so it must never decide access (for example anis_adminflag). - Queue exposed without protection (
insecure_queue_exposed_in_api), if you use Supabase Queues.
3. Review the security warnings
- Always-true policies (
permissive_rls_policy):using (true)orwith check (true). Fine for a public product list, a leak for profiles or orders. - Security definer functions anyone can call (
anon_security_definer_function_executableand the matchingauthenticatedlint). These run as their owner and skip RLS, and PostgREST exposes them at/rest/v1/rpc/. Revokeexecutefromanon,authenticatedandpublic, or switch them tosecurity invoker, unless they are meant to be public endpoints. - Mutable function search path (
function_search_path_mutable): addset search_path = ''and fully qualify names. - Public buckets that allow listing (
public_bucket_allows_listing): public files are readable by URL already; a broad select policy also lets clients list them. - Tables visible in GraphQL (the
pg_graphqllints): if you don't use GraphQL, Supabase suggests turning the extension off. - Materialized views or foreign tables in the API: RLS can't protect them, so revoke
selectfrom the API roles. - Anonymous sign-ins enabled (info): anonymous users get the same
authenticatedrole, so check that your policies mean what you think.
Some findings are intentional. Check each one against who should see what in your app before you change it.
4. Test like a stranger, then like another user
Advisors don't know your app's rules. Two quick tests fill the gap:
- Signed out: with your project URL and public anon key, ask for one row of a table that should be private (the curl is in the RLS checker guide). A row back means anonymous visitors can read it.
- Two users: sign up two test accounts and, as user B, try to load user A's rows. If that works, a policy checks that someone is signed in but not that the row is theirs.
5. Check your keys and your live site
The anon or sb_publishable_ key is meant to be public. The service_role and sb_secret_ keys are not: they skip every policy you just audited. Make sure they live only in server code (Edge Functions, API routes) and never in VITE_ or NEXT_PUBLIC_ variables. Then check the published site, because that's what strangers actually load. A free SniffPup scan searches your HTML and same-origin JavaScript for secret keys (masked in the report), tests for public /.env and /.git files and source maps, and checks your headers. If a secret key did leak, follow what to do after leaking an API key: rotate it first.
6. Repeat after every change
Tables created with raw SQL or migrations don't get RLS by default, which is how an AI-written migration often leaves one open. Re-run the advisors after each schema change and re-scan your live site after each deploy. The vibe-coded app security checklist covers the rest of your app (auth, payments, admin pages).
FAQ
What is the best Supabase security audit tool?
Start with the one Supabase ships: the Security Advisor. It reads your project from the inside and runs deterministic checks (lints) for tables with RLS off, always-true policies, security definer views and functions, policies that trust user_metadata, listable public buckets and more. You can run it in Studio, with the CLI (supabase db advisors), through the Supabase MCP get_advisors tool or the Management API. It can't see your website, so pair it with an outside check of your live URL, such as a free SniffPup scan.
Can SniffPup audit my Supabase database?
Not from the inside. SniffPup's free scan only reads what your live website serves to a browser, so it can't see your tables, policies or functions. It does flag a Supabase service_role or sb_secret key in your HTML or JavaScript as critical, because those keys bypass Row Level Security, and it tells you when your site talks to Supabase. On Pro, after you prove you own the site, a read-only deep check uses your public anon key to count which tables an anonymous visitor can read, without downloading rows or writing anything.
If Security Advisor shows no errors, is my Supabase project secure?
Not necessarily. Advisors catch known misconfigurations, and Supabase's docs note some findings may be intentional. They don't know who should see which rows in your app, so a policy that checks someone is signed in but not that the row belongs to them can still pass. Test as two different users, and check your live site for leaked secret keys.
How often should I run a Supabase security audit?
After every change that adds a table, view, function, bucket or policy, and before every launch. AI-written migrations are a common way a new table ends up without RLS, so re-run the advisors after each one.
A score of 100 means no issues were found in our checks. It is not proof that your app is secure.