Free & open source
Prove your Postgres RLS, tests generated for you.
Reads your Row-Level Security policies. Generates the pgTAP suite and the seed data that prove them.
- Per table, per command and per identity, showing who can read and write which rows
- Owner, other user, other tenant, anon
- Two tenants seeded; one cannot touch the other's rows
- Unprotected table fails CI through a built-in guard
Apache-2.0, emits pgTAP, Supabase & any Postgres
One command produces a per-identity access report that flags a table anon can read.
Why the data matters
- A generated test only proves something if its data matches the policy and the identity
- Reverse-predicate seeding works from each policy to the rows that satisfy and violate it
- "The owner can see their row" is checked against a row that is actually theirs
- "Another tenant cannot" against a real, different tenant
- Never emits a false-passing test
- Runs against a local or throwaway database, never production
The report
One report per schema, dated and reproducible. Each table shows a row-level grid and a column-level security grid, side by side, so it reads like a permissions table.
Each table carries a row-level grid and a column-level security grid, side by side. Columns that a broader grant leaks past are flagged red.
- Row level, a tick means the identity can act, a dot means blocked
- A tick where a dot belongs is the security hole, flagged red
- Column level security, the columns each role is granted and any a broader grant leaks past
- A summary line counts tables, exposed tables, problems, enforced as declared, and bypass surfaces
- service_role is shown for completeness; it bypasses RLS by design
- A table with RLS off is flagged loud, with no row-level protection at all
- Dated and reproducible, so a run stands as access-control evidence
--all-schemas writes one dashboard for the whole database, a schema picker beside each report
The two-membership user
Most suites test membership policies with users who belong to exactly one team. Real users belong to two, and that is where a class of bugs lives.
- A scalar subquery keyed on
auth.uid() errors the moment a user joins a second team
- A
LIMIT 1 "fix" silently shows one of their teams at random
- An over-broad rewrite shows a user more than their memberships grant
- rlsautotest seeds one identity in two tenants, probes what it sees, and pins the exact count as a test
- Any later drift into those shapes turns the suite red in CI
lint flags the scalar-subquery shape statically, with the fix
Beyond the policies
Correct policies can still be undone by how the data is reached. rlsautotest maps that surface straight from the catalog and reports each object or role that can sidestep RLS.
- Owner-rights (
SECURITY DEFINER) views and functions a client can reach
- Materialized views, a frozen owner-populated copy of their source
BYPASSRLS and superuser roles a client can become
- Tables with RLS enabled but not
FORCEd
- Column grants a broader grant quietly widens past, named column by column
- Each is a review flag with a severity and a reason, not a pass or fail
Two layers where tenant data leaks. One tool for each. Use both.
A test at one layer says nothing about the other.
- The policy layer, where Row-Level Security governs direct table access
- The code layer, where SECURITY DEFINER functions, views and owner-run triggers bypass RLS by design
rlsautotest
Policy layer
- Proves each RLS policy, per table, per command and per identity
- Fails CI on a reachable table with no policy
- Names every SECURITY DEFINER function, view and trigger as a suspect
It cannot judge the suspects; it sees that code bypasses RLS, not whether it leaks.
rlsautotest, free and open source →
UnitAutogen seccover
Code layer
- Runs each suspect as two tenants with generated data on both sides
- A verdict per routine, enforced or leaking, with the table, command and channel
- A PII pass for personal data escaping through error messages
It cannot stand in for the policies; direct table access is rlsautotest's job.
seccover, in the UnitAutogen engine →
Complete leakage detection needs both.
- rlsautotest verifies the RLS policies
- UnitAutogen verifies the functions, views and triggers that bypass them
Get started
pip install rlsautotest
# quick check: write an access report, open it in your browser (nothing saved)
rlsautotest --db-url "$DATABASE_URL" --schema public --html rls-report.html
# or generate a pgTAP suite to commit and run in CI
rlsautotest --db-url "$DATABASE_URL" --schema public --emit supabase/
- Native pgTAP:
supabase test db, pg_prove or psql
- Nothing to install on the database
- Free and open source under Apache-2.0
- Beta, validated on real Supabase schemas and a multi-tenant corpus
What platforms suggest
rlsautotest generates exactly the RLS proof PostgreSQL, Supabase and Neon tell you to have.