SKILL.md
Reviews Supabase Row Level Security for a project and reports tables that leak data, with the SQL to fix each one. Use when asked to check Supabase security, RLS, policies, or "can users see each other's data".
Part of Supabase RLS Review by Admin
mkdir -p ~/.claude/skills/supabase-rls-review && curl -fsSL "https://wonly.dhfm.in/builder-4392ac/supabase-rls-review/skill/?download=1" -o ~/.claude/skills/supabase-rls-review/SKILL.md
Supabase RLS review
You check that every table exposed through the Supabase API is protected, and you prove it with queries instead of guessing.
Steps
- List the tables in the
publicschema (and any other schema exposed in API settings). If the Supabase MCP is connected, use it read-only; otherwise ask forsupabase/schema.sqlor apg_dump --schema-only. - For each table, record:
- Is RLS enabled? (
select relname, relrowsecurity from pg_class where relnamespace = 'public'::regnamespace and relkind = 'r';) - Which policies exist, for which command (select, insert, update, delete) and role.
- Is RLS enabled? (
- Flag, highest risk first:
- RLS disabled on a table with user data. This is a leak: anyone with the anon key can read it.
- A
selectpolicy ofusing (true)on personal data. updatepolicies without awith check, so a user can move a row to someone else or change their own role.- Policies that trust columns the user can write, such as
roleon their own profile. security definerfunctions withoutset search_path = ''.- Views that bypass RLS (missing
security_invoker = trueon Postgres 15+).
- For each finding, write the fix as SQL the user can review, for example:
alter table public.notes enable row level security;
create policy "own notes" on public.notes
for select using ((select auth.uid()) = user_id);
- Give a test plan with two accounts, A and B: what A must not be able to read or change of B's data, through the app and through direct API calls with A's token.
Rules
- Never print or ask for the service role key. Never run write queries yourself; hand over SQL.
- Prefer
(select auth.uid())overauth.uid()in policies, so it is evaluated once per query. - If you are unsure whether a column is personal data, ask.
Output
A table of findings (table, problem, risk, fix), then the SQL, then the two-account test plan.
Source, 41 lines
---
name: supabase-rls-review
description: Reviews Supabase Row Level Security for a project and reports tables that leak data, with the SQL to fix each one. Use when asked to check Supabase security, RLS, policies, or "can users see each other's data".
---
# Supabase RLS review
You check that every table exposed through the Supabase API is protected, and you prove it with queries instead of guessing.
## Steps
1. List the tables in the `public` schema (and any other schema exposed in API settings). If the Supabase MCP is connected, use it read-only; otherwise ask for `supabase/schema.sql` or a `pg_dump --schema-only`.
2. For each table, record:
- Is RLS enabled? (`select relname, relrowsecurity from pg_class where relnamespace = 'public'::regnamespace and relkind = 'r';`)
- Which policies exist, for which command (select, insert, update, delete) and role.
3. Flag, highest risk first:
- RLS disabled on a table with user data. This is a leak: anyone with the anon key can read it.
- A `select` policy of `using (true)` on personal data.
- `update` policies without a `with check`, so a user can move a row to someone else or change their own role.
- Policies that trust columns the user can write, such as `role` on their own profile.
- `security definer` functions without `set search_path = ''`.
- Views that bypass RLS (missing `security_invoker = true` on Postgres 15+).
4. For each finding, write the fix as SQL the user can review, for example:
```sql
alter table public.notes enable row level security;
create policy "own notes" on public.notes
for select using ((select auth.uid()) = user_id);
```
5. Give a test plan with two accounts, A and B: what A must not be able to read or change of B's data, through the app and through direct API calls with A's token.
## Rules
- Never print or ask for the service role key. Never run write queries yourself; hand over SQL.
- Prefer `(select auth.uid())` over `auth.uid()` in policies, so it is evaluated once per query.
- If you are unsure whether a column is personal data, ask.
## Output
A table of findings (table, problem, risk, fix), then the SQL, then the two-account test plan.