Can one of your customers read another customer's data?
Rowseal reviews multi-tenant apps on Postgres, Supabase and Prisma for one kind of bug: data leaking between customers. You get the answer in writing, with proof, the fix, and tests that keep it fixed.
Same query. Two policies.
Signed in as Acme Dental
select * from invoices;
create policy "read invoices"
on invoices for select
using (auth.uid() is not null);This policy checks that a user is logged in. It never checks which company they belong to. It passes every test written with a single user.
| Company | Invoice | Amount |
|---|---|---|
Acme Dental | INV-1042 | 1,280.00 |
Globex Labs (another company) | INV-2210 | 9,400.00 |
Acme Dental | INV-1043 | 640.00 |
Initech (another company) | INV-3307 | 2,150.00 |
Globex Labs (another company) | INV-2211 | 5,725.00 |
3 of 5 rows belong to other companies.
Example data. Fictional companies.
What we check
The ways one customer's data can reach another, in the database and in the app code that talks to it.
- Tables with RLS turned off
- Exposed through the API with no row rules
- Login checked, ownership not
- Policies like
auth.uid() is not null - Writes into other companies' data
- Insert and update policies whose
with checkignores the company - Trusting what users can edit
- Policies that read
user_metadata - Functions that skip tenant checks
security definerfunctions callable from the API- Views that ignore RLS
- Views without
security_invoker - Storage buckets
- Files readable across companies
- RLS that never runs
- Apps connecting as table owner or superuser
- Prisma queries
- Reads and writes missing the tenant filter
- After Supabase's Oct 30 change
- GRANTs added to fix 'permission denied' errors on new tables, without RLS
How an engagement works
Every review is done personally by the founder, never outsourced.
- 20-minute scoping call Your stack, how tenants are separated, what's in scope.
- Fixed quote and written permission Scope, dates, staging only.
- Schema-only dump
pg_dump --schema-only. No customer data leaves your hands. - We try to break it Two test users in two companies, every read and write path in scope.
- Report Each finding with proof, severity, and the exact SQL that fixes it.
- Tests in your repo Plain SQL tests (pgTAP if you already use it) that run in your CI and fail if the leak comes back, plus one retest after you fix.
We need read-only access to a staging database, or a schema dump plus policy definitions. We never need production data or the service_role key for production. NDA available on request. Any dump is deleted within 30 days after the engagement ends.
We never call a database "secure." A clean report says: no isolation failures found in the tested scope.
Engagements
- Tenant Isolation Review Full review, written report, fix SQL, tests, one retest. About five business days; dates fixed in the quote.
- Fix implementation We write and test the fixes with your team.
- Evidence Pack The report, fixes and retest results, formatted for SOC 2 evidence requests, security questionnaires and investor due diligence.
Also available: migration verification. After a database move, we prove every row arrived intact.
Fixed price, quoted after the scoping call.
Built on Rowseal Engine
Reviews use our own checking tool for row-level security, grants and policies. It has more than 6,500 automated tests of its own, tested against PostgreSQL 13 through 18. Before using it on anyone's database, we put it through twelve rounds of hostile security review and fixed all 81 findings. It reports what it could not test instead of passing it.
Request a review
mark@rowseal.com
Tell us your company, your stack, and what's driving the review (SOC 2, an enterprise customer, a fundraise, the Oct 30 change). We'll reply to set up the scoping call.