- Home
- Skills
- Data & Databases
- Supabase RLS Audit + Cross-Tenant Leak Test
More screenshots
Works with the AI Agents you already use
Supabase RLS Audit + Cross-Tenant Leak Test
Audit Supabase RLS like a senior reviewer, then prove tenant isolation: your agent tries to read and write another tenant's rows in every table. Exact fix SQL + a CI guard for every PR.
$19
Supabase RLS Audit + Cross-Tenant Leak Test
Example session with this skill installed
Audit my Supabase project for tenant data leaks before we launch. The tenant column is tenant_id. I'm running supabase start locally, so you can run the leak test there.
- Read your context and instructions
- Compiled the supabase rls audit
I ran the 20 database checks and the cross-tenant leak test on your local database. Tenant isolation does not hold: a user of tenant A can read tenant B's documents, and anyone can unlock tenant B's invoices by editing their own user_metadata. Inserts into another tenant are blocked everywhere.
Supabase security audit — local (supabase start)
Verdict: 2 tables leak across tenants and 1 table has RLS disabled. 13 high-severity issues need fixing too; two of them cause the leaks.
Severity Findings
Critical 3 (2 proven leaks + 1 table without RLS)
High 13
Medium 11
Low 2
Scope: schema public + storage, tenant column tenant_id, database: local, leak test run as a real user of tenant A.
Leak test
Table Read B Update B Delete B Insert B Forged metadata Result
public.documents 1 row 0 0 denied 1 row LEAK
public.invoices 0 0 0 denied 1 row LEAK
public.memberships 0 denied denied denied 0 PASS
public.orders 0 denied denied denied 0 PASS
Every write attempt was rolled back; your data is unchanged.
Findings
- [Critical] Any signed-in user reads every tenant's documents — public.documents
Evidence: leak test LEAK (reads 1 row of tenant B) + check R07: policy members_read_all is using (true).
Why it matters: every customer can read every other customer's documents through the API.
Fix
drop policy members_read_all on public.documents;
create policy documents_select on public.documents for select to authenticated
using (tenant_id in (select private.my_tenants()));
2. [Critical] Tenant taken from user-editable metadata — public.invoices
Evidence: leak test LEAK (a forged user_metadata unlocks 1 row of tenant B) + check R04 on policy tenant_from_metadata.
Why it matters: any user can call auth.updateUser() to point their metadata at another tenant and read its invoices.
Fix
drop policy tenant_from_metadata on public.invoices;
create policy invoices_select on public.invoices for select to authenticated
using (tenant_id in (select private.my_tenants()));
create index on public.invoices (tenant_id); -- also fixes R08
- [Critical] RLS disabled — public.audit_log
Evidence: check R01; anon and authenticated hold INSERT, UPDATE and DELETE on it.
Fix
alter table public.audit_log enable row level security;
revoke all on public.audit_log from anon, authenticated;
- [High] Write grants with no policy behind them — announcements, audit_log, invoices, notes
Evidence: check R10. Today only RLS's default deny stops these writes; one future permissive policy would open them.
Fix: revoke insert, update, delete on public.announcements, public.invoices, public.notes from anon; and keep only the writes each role really needs.
…plus 7 more high findings with their fixes:
an INSERT policy with check (true) on notes;
a SECURITY DEFINER function callable by anon, with a mutable search_path;
a view without security_invoker;
an exposed materialized view;
a storage policy that ignores the bucket;
a role with BYPASSRLS.
Fixes 1 and 2 use the private.my_tenants() helper from the secure pattern; I'll create it in the same migration.
What's already right
orders and memberships are fully isolated: no reads, writes or forged-metadata access across tenants.
No table accepts an insert into another tenant.
Not checked
Code checks (service keys in the client, Edge Functions with verify_jwt = false): no repo access in this session.
Next step: say "apply fixes 1–3 as a migration" and I'll write the migration file, re-run the leak test, and show before/after. When everything passes, I can install the CI guard so a future PR can't reopen these leaks.
Connects securely to your tools. The creator never sees your data.
About this skill
"Can one customer see another customer's data?" On Supabase, the answer lives in your RLS policies, grants and functions. One using (true), one policy that trusts user_metadata, or one SECURITY DEFINER function in public is enough to leak every tenant's rows through the public API.
This skill makes your coding agent answer that question with evidence, not guesses.
What it does
- Audit — 20 read-only checks Reads only Postgres catalogs, never your data. Catches:
tables without RLS; permissive or role-less policies; policies that scope neither tenant nor user; trust in user-editable metadata; SECURITY DEFINER functions and views that bypass RLS, and functions with a mutable search_path; leftover write grants and dangerous default privileges (including Postgres 17's new MAINTAIN privilege); public storage buckets and loose storage policies; roles that bypass RLS, unindexed tenant columns and slow per-row auth.uid() calls. It merges the results with Supabase's Security Advisor.
- Leak test — proof, not suspicion On staging, a branch or a local database, the agent signs in as a real user of tenant A, exactly like the Data API does. Then, in every table with your tenant column, it tries to:
read tenant B's rows; update them; delete them; insert into tenant B. It repeats the read with a forged user_metadata pointing at tenant B, and once more as anonymous. Every write is rolled back automatically. Each table gets PASS, LEAK (with exactly what crossed) or INCONCLUSIVE; it never reports a false "safe".
-
Code review Looks for service keys reachable by the browser, Edge Functions with verify_jwt = false, service-key functions that don't check tenant membership, client-side writes, migration drift and secrets in git history.
-
Report with fixes A prioritized report a founder reads in 30 seconds and an engineer can act on. Every finding comes with evidence and ready-to-adapt fix SQL, plus a reference secure multi-tenant pattern.
-
CI guard — keep it fixed A GitHub Action that, on every pull request touching supabase/, starts a throwaway Supabase database in the runner. It applies your migrations, re-runs the checks and the leak test, and fails the PR on critical or high findings or any leak.
No secrets needed; it never connects to your projects. Reviewed exceptions go in an allowlist with a reason. Safety by design Database checks are read-only. The leak test refuses to run without explicit setup, never runs on production, and rolls back every write. The agent proposes fixes and applies nothing without your approval. What's inside SKILL.md: the full audit workflow for your agent. queries/audit.sql: the 20 checks. queries/leak_test.sql: the automated cross-tenant leak test. references/: what each check means, fix templates, the secure pattern and the report template. ci/: the guard script and the GitHub Actions workflow.
Frameworks & tools
Specifically built for Supabase projects using PostgreSQL, RLS, and Auth. Supports GitHub Actions for CI integration and works with local Supabase CLI environments.
Why this beats prompting it yourself
Writing a reliable cross-tenant leak test that covers forged JWT metadata and transactional rollbacks is complex. This skill uses a standardized battery of 20+ specific RLS checks and a proven testing harness that prevents false positives by verifying isolation with actual data operations.
Use cases
- Auditing a Supabase SaaS project before a public launch.
- Verifying that new database migrations haven't introduced RLS regressions.
- Investigating if a specific user can access another customer's data.
- Hardening Edge Functions and storage bucket policies against unauthorized access.
Known limitations
The automated leak test cannot run on production databases for safety reasons. It requires a staging, branch, or local database environment to execute write-based isolation tests.
"Can one customer see another customer's data?" On Supabase, the answer lives in your RLS policies, grants and functions. One using (true), one policy that trusts user_metadata, or one SECURITY DEFINER function in public is enough to leak every tenant's rows through the public API.
This skill makes your coding agent answer that question with evidence, not guesses.
What it does
1. Audit — 20 read-only checks Reads only Postgres catalogs, never your data. Catches:
- tables without RLS;
- permissive or role-less policies;
- policies that scope neither tenant nor user;
- trust in user-editable metadata;
- SECURITY DEFINER functions and views that bypass RLS, and functions with a mutable search_path;
- leftover write grants and dangerous default privileges (including Postgres 17's new MAINTAIN privilege);
- public storage buckets and loose storage policies;
- roles that bypass RLS, unindexed tenant columns and slow per-row
auth.uid()calls.
It merges the results with Supabase's Security Advisor.
2. Leak test — proof, not suspicion On staging, a branch or a local database, the agent signs in as a real user of tenant A, exactly like the Data API does. Then, in every table with your tenant column, it tries to:
- read tenant B's rows;
- update them;
- delete them;
- insert into tenant B.
It repeats the read with a forged user_metadata pointing at tenant B, and once more as anonymous. Every write is rolled back automatically. Each table gets PASS, LEAK (with exactly what crossed) or INCONCLUSIVE; it never reports a false "safe".
3. Code review
Looks for service keys reachable by the browser, Edge Functions with verify_jwt = false, service-key functions that don't check tenant membership, client-side writes, migration drift and secrets in git history.
4. Report with fixes A prioritized report a founder reads in 30 seconds and an engineer can act on. Every finding comes with evidence and ready-to-adapt fix SQL, plus a reference secure multi-tenant pattern.
5. CI guard — keep it fixed
A GitHub Action that, on every pull request touching supabase/, starts a throwaway Supabase database in the runner. It applies your migrations, re-runs the checks and the leak test, and fails the PR on critical or high findings or any leak.
- No secrets needed; it never connects to your projects.
- Reviewed exceptions go in an allowlist with a reason.
Safety by design
- Database checks are read-only.
- The leak test refuses to run without explicit setup, never runs on production, and rolls back every write.
- The agent proposes fixes and applies nothing without your approval.
What's inside
SKILL.md: the full audit workflow for your agent.queries/audit.sql: the 20 checks.queries/leak_test.sql: the automated cross-tenant leak test.references/: what each check means, fix templates, the secure pattern and the report template.ci/: the guard script and the GitHub Actions workflow.
Tested
- Every check and the leak test are covered by an automated test suite, run against a Postgres database seeded with real-world RLS mistakes.
- The leak test and the CI guard were run on real Supabase stacks, Postgres 15 and 17:
- a deliberately broken app fails with exactly its 2 seeded leaks;
- the secure pattern passes with zero false positives.
- The CI workflow was also run on GitHub against a production SaaS schema: it passes the clean schema and blocks a pull request that adds a table without RLS.
- A write test that can't run (for example on a read-only connection) reports INCONCLUSIVE, never a false PASS.
Find more skills like this on the Claude Skills Marketplace, the Codex Skills Marketplace and the Gemini Skills Marketplace, or browse Claude Skills for Data & Databases.
The leak test needs staging, a Supabase branch or a local database, plus one test user in tenant A and a tenant B with data; without those it is skipped and the report says so. Multi-tenant checks assume one consistent tenant column across tables (single-tenant apps are audited per user instead). Auth settings such as leaked-password protection and MFA come from the Security Advisor, not SQL. Code checks need repo access.
How to use
- 1
Connect your agent to the Agensi MCP
Copy the prompt and paste it into Claude, ChatGPT, Cursor or any agent that supports MCP. It sets up the connection for you. Manual setup
- 2
Sign in to Agensi
Your agent shows a sign-in link. Sign in and click Allow.
- 3
Ask for what you need
Your agent loads this skill and uses it on your task. It always asks before buying a skill.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill goes through the trust checks below.
Security scanned
Checked by our 8-point scan before listing
Fresh listing
Recently published to Agensi
Money-back guarantee
Doesn't do what its page promises? You get your money back.
Trust & safety
Security scan
Security scan passed 1 day ago
- Software
Python
GitSupabase project (Postgres 15+)
A way to run SQL: Supabase MCP, psqlorthe SQL Editor- Optional
- GitHub Actions (CI guard)Supabase CLI
Docker (local leak test)
Run the leak test on staging, a Supabase branch or a local `supabase start`, never on production. The audit itself is read-only and safe anywhere.
Permissions
File Scopes
Terminal and network: to run SQL against your database (MCP, psql) and the CI guard. Read files: migrations, config.toml and client code, to review policies and look for leaked keys. Write files: only the report, a migration you approve, and the CI guard files. Environment variables: reads the database URL (never prints keys or secrets).