BuiltSafely

Free tool · Supabase

Supabase self-check

Run one read-only query in your own Supabase SQL Editor, paste the result below, and get a plain-English list of tables that are readable or writable through your Data API, tables that will hit 42501, and public storage buckets.

Your result never leaves your browser. The report is built by JavaScript on this page. We don't send, store or log what you paste: the page has no form that submits it, makes no network requests with it, and its security policy blocks this page from connecting anywhere. It never asks for your project URL or keys, and it can't contact your Supabase project. It can only check a project you're signed in to, because you run the query yourself.

Step 1: Run this query in your own project

In the Supabase dashboard, open SQL Editor, start a new query, paste this and click Run. It only reads Postgres catalog information (table names, RLS flags, policy counts, grants and public bucket names). It reads no rows from your tables and changes nothing. Please read it before you run it: you should never run SQL you don't understand.

-- BuiltSafely Supabase self-check v1 (read-only)
-- Reads Postgres catalog metadata only: table names, RLS flags, policy
-- counts, grants and public bucket names. It reads no rows from your
-- tables and changes nothing. Run it in your own project's SQL Editor.
with t as (
  select c.oid, c.relname as name, c.relkind as kind,
         c.relrowsecurity as rls,
         coalesce(array_to_string(c.reloptions, ',') ~* 'security_invoker=(true|on|1)', false) as invoker
  from pg_catalog.pg_class c
  join pg_catalog.pg_namespace n on n.oid = c.relnamespace
  where n.nspname = 'public' and c.relkind in ('r', 'p', 'v', 'm')
),
r as (
  select exists (select 1 from pg_catalog.pg_roles where rolname = 'anon') as has_anon,
         exists (select 1 from pg_catalog.pg_roles where rolname = 'authenticated') as has_auth
)
select json_build_object(
  'tool', 'builtsafely-supabase-check',
  'v', 1,
  'tables', coalesce((
    select json_agg(json_build_object(
      'name', t.name,
      'kind', t.kind,
      'rls', t.rls,
      'invoker', t.invoker,
      'policies', (select count(*) from pg_catalog.pg_policies p
                   where p.schemaname = 'public' and p.tablename = t.name),
      'open', (select count(*) from pg_catalog.pg_policies p
               where p.schemaname = 'public' and p.tablename = t.name
                 and (p.qual = 'true' or p.with_check = 'true')
                 and (p.roles && array['public', 'anon']::name[])),
      'anon', case when r.has_anon then concat_ws(',',
                case when has_table_privilege('anon', t.oid, 'SELECT') then 'S' end,
                case when has_table_privilege('anon', t.oid, 'INSERT') then 'I' end,
                case when has_table_privilege('anon', t.oid, 'UPDATE') then 'U' end,
                case when has_table_privilege('anon', t.oid, 'DELETE') then 'D' end) end,
      'auth', case when r.has_auth then concat_ws(',',
                case when has_table_privilege('authenticated', t.oid, 'SELECT') then 'S' end,
                case when has_table_privilege('authenticated', t.oid, 'INSERT') then 'I' end,
                case when has_table_privilege('authenticated', t.oid, 'UPDATE') then 'U' end,
                case when has_table_privilege('authenticated', t.oid, 'DELETE') then 'D' end) end
    ) order by t.name)
    from t, r
  ), '[]'::json),
  'public_buckets', coalesce((
    select json_agg(b.name order by b.name) from storage.buckets b where b.public
  ), '[]'::json)
) as builtsafely_check;

It returns one row with one column, builtsafely_check, holding a single JSON value. Tested on PostgreSQL 17 with Supabase-style anon and authenticated roles and a storage.buckets table.

Step 2: Paste the result here

Copy the result cell (or use the results copy or export option as JSON or CSV) and paste it below. Table names are the only project details in it.

What it checks

It covers the public schema only. If you expose other schemas in your API settings, review them too. It can't tell whether a policy's logic is right, so test with two accounts and run Supabase's Security Advisor as well (how).

This self-check is educational and based on Supabase's and PostgreSQL's public docs. It isn't a security audit and can't prove your app is safe. Disclaimer.

Want copy-paste prompts for your AI builder? See our security prompts.