Supabase Security & compliance

Schemas beyond public exposed through the data API

Part of the Auth hardening & exposure config check · fix arrives as a guide

What it is

Schemas beyond public are exposed through the Data API.

Why it matters

Those schemas usually hold the things deliberately kept out of the API — internal tables, audit records, staging data — and exposing them makes all of it reachable with an anon or authenticated key, subject only to RLS that may never have been written for them.

Check what your API exposes right now

The list is at Project Settings → API → Exposed schemas. Anything on it is queryable through PostgREST by anyone holding the anon key — they just have to name the schema:

curl "https://YOUR-PROJECT.supabase.co/rest/v1/events?select=*" \ -H "apikey: $ANON_KEY" \ -H "Accept-Profile: analytics" # ← schema selector; try each exposed schema

If a schema you think of as internal answers that request, this finding is not theoretical.

How internal schemas end up exposed

Someone needed one table from analytics in the app, exposing the schema was the one-line change that worked, and the ticket closed. But exposure is per-schema while security is per-table: every table in that schema is now reachable, under whatever grants and RLS each happens to have — and internal schemas are exactly where RLS was never enabled, because “it’s internal”.

Fix it manually

Pull the schema back to the app through views in public rather than pushing the schema out to the API:

-- expose one table's safe surface, not the schema create view public.daily_signups with (security_invoker = true) as select day, count from analytics.daily_signups where day > now() - interval '90 days'; -- then remove `analytics` from Exposed schemas in the API settings

If a second schema genuinely must be exposed, treat it like public: RLS enabled on every table, policies per command and role, and the same review bar for new tables from then on.

How lumioguard fixes it

The scan reads the exposed-schema list and audits RLS coverage for every table in each — the finding names the tables reachable without policies. The fix arrives as a guide: which schema to unexpose, or the view-based surface to build if the data is genuinely needed.

Run them all on your app

Connect your repo and your live services with read-only scopes. The first scan is free, and nothing changes without your approval.