# What can a repo scan prove about Supabase security, and what needs a live test?

**Answer:** A repo scan can prove what your files say: which tables your migrations create, whether they turn on row level security, what each policy condition is, and whether client code uses a secret key. It cannot prove that production matches those files, which grants the live roles hold, what was changed in the dashboard, or that a policy lets the right user in and keeps the wrong one out. For those you need the database: the Security Advisor on the live project, supabase migration list, and tests that run queries as anon and authenticated.

## Why this matters

Postgres runs two checks before a client touches a table: grants decide whether a role can run an operation at all, and policies decide which rows it applies to. Only part of that lives in your repo. Supabase warns that schema changes made in the dashboard bypass the migration history, and not every project grants the client roles the same privileges, so the files and the live database can drift apart.

## What each side can establish

- In the repo: which tables your migrations create in public, and whether a migration enables row level security on each one. Supabase applies migrations in timestamp order, so a later file can undo an earlier one.
- In the repo: each policy condition as written, for example to anon using (true), which gives every signed-out visitor read access to every row the role can reach through grants.
- In the repo: whether client code carries the secret or service_role key, which bypasses RLS. In Next.js, a variable prefixed NEXT_PUBLIC_ is inlined into the browser bundle at build time, so a secret must never have that prefix.
- In the repo: whether grants are revoked and granted in the same migration as the RLS change, as Supabase's RLS guide recommends.
- Needs the database: which migrations production has applied. supabase migration list shows the local and remote history; a policy that exists only in a local file does not protect production.
- Needs the database: changes made in the dashboard SQL editor or Table Editor. They never reach your migration files, so only the live schema shows them.
- Needs the database: the grants each role holds. On existing projects Supabase grants new public tables to anon and authenticated by default, and not every project does, so read the privileges from the database, for example with has_table_privilege, instead of assuming.
- Needs the database: the Security Advisor findings. Supabase describes its advisors as checks that inspect the live schema, available in the dashboard, through supabase db advisors, the MCP server and the Management API.
- Needs a database test: whether the policies do what you meant. Supabase's procedure is a pgTAP file per table asserting allow and deny for select, insert, update and delete as anon and authenticated, run with supabase test db against a local database with your migrations applied. Until it passes, you do not know.
- Both sides: views and functions. Views bypass RLS by default; on Postgres 15 and above, a view created with security_invoker follows the table policies. A security definer function in an exposed schema can be called over the Data API with its creator's privileges. The files show the ones you wrote; the Advisor shows what exists.
- Needs the dashboard: Auth settings such as the Site URL, the redirect URL list, email confirmations and custom SMTP, which are project settings, not SQL.
- Needs the dashboard: backups and point in time recovery, which depend on your plan.

## Optional repo check

For AI-built Vercel + Supabase apps, `npx -y viberaven@1.5.3 check` covers part of the repo side of this list: public tables that no migration puts under row level security, tables with RLS on and no policies, policies whose condition is a bare `true`, and a Supabase service role key in client code. It is advice, not a gate, and a repository check, not a live database test: it never connects to your project, so every item above that needs the database or the dashboard stays with you. It exits with code 1 when it finds a blocker and writes a `.viberaven` folder that it does not add to `.gitignore`.

## FAQ

### If my migrations are clean, is my production database safe?

Not necessarily. The files show what should exist. Production can differ if a migration was never pushed, if someone changed the schema in the dashboard, or if the project grants the client roles more than you expect. Run the Security Advisor on the live project and compare supabase migration list.

### Can reading the SQL tell me whether a policy works?

Reading a policy shows its condition, not whether it lets the right user in. A test has to run queries as each role. Supabase's guide does that with pgTAP and supabase test db against a local database with your migrations applied.

## Sources

- Supabase, Row Level Security: https://supabase.com/docs/guides/database/postgres/row-level-security
- Supabase, Securing your API: https://supabase.com/docs/guides/api/securing-your-api
- Supabase, Advisors: https://supabase.com/docs/guides/database/database-advisors
- Supabase, Database migrations: https://supabase.com/docs/guides/deployment/database-migrations
- Supabase CLI, migration list: https://supabase.com/docs/reference/cli/supabase-migration-list
- Supabase, API keys: https://supabase.com/docs/guides/api/api-keys
- Supabase, Redirect URLs: https://supabase.com/docs/guides/auth/redirect-urls
- Supabase, Database Backups: https://supabase.com/docs/guides/platform/backups
- Next.js, Environment variables: https://nextjs.org/docs/app/guides/environment-variables

HTML page: https://viberaven.dev/repo-scan-vs-live-supabase-security-test
