Your Supabase app works. The question is whether it still works when there is more than one of you in it.
Row-level security, storage rules and auth flows are each straightforward on their own. What breaks platforms is the boundary between them — and it breaks quietly, as inconsistent results for some users rather than as an error anybody can reproduce.
I review the data-access model of an existing Supabase platform and report where the boundary actually holds, before you build the next phase on top of it.
I have operated row-level security in production, not just read about it. As fractional CTO of a multi-tenant utility and IoT platform I ran RLS across a 31-table PostgreSQL schema serving three separate portals — admins, agencies and tenants. And SureCiteAI, which is live and open source, enforces tenant isolation in four named layers with Supabase RLS as the first of them.
What actually goes wrong
The failure has a documented shape.
1 in 10
Lovable-built production apps leaking real user data — 170 of 1,645, CVE-2025-48757
5,600
AI-built applications scanned by Escape.tech, October 2025
175
instances of exposed personal data — medical records, bank accounts — all live
CVE-2025-48757 recorded 170 of 1,645 Lovable-generated production applications shipping with missing or misconfigured row-level security. In a separate scan of 5,600 AI-built applications, Escape.tech found over 2,000 high-impact vulnerabilities, more than 400 exposed secrets, and 175 instances of personal data exposure including medical records and bank account numbers. Every one of those was in a live production system.
That is not a story about bad tools. It is a story about what generation optimises for. An AI coding assistant asked for a table and a query produces a correct table and a correct query. Nothing in the request said who else exists, so the boundary is left implicit — and an implicit boundary in Postgres means no boundary at all.
Why it shows up as “inconsistent results”
Almost nobody arrives at this review saying they have a security problem. They arrive saying multi-user behaviour is inconsistent: the same screen shows different things to different people, or the same person sees different things depending on how they signed in. That symptom has four usual causes, and all four are the boundary.
- A policy reads a session claim that is not always populated, so the row set depends on the auth path taken.
- A query path runs under the service-role key and therefore skips RLS entirely — often a server action added for one legitimate reason and reused since.
- A
SECURITY DEFINERfunction, introduced to make a permissions error go away, now executes as its owner for everybody. - Storage object policies were never aligned with the table policies that reference the same files.
Each of these is individually reasonable at the moment it is written. The problem is cumulative, and it is not visible in any single diff — which is why it survives code review and arrives as a bug report nobody can reproduce.
Why the review belongs before the next phase, not after
Where tenancy lives is one of the few decisions in a platform that is expensive to reverse. Every table added after it is settled inherits it for free; every table added before it is settled has to be revisited, along with every policy, every function and every client query that touched it.
The cost doesn’t rise with the size of your schema. It rises with how many places the old assumption has already been copied.
That is the honest argument for reviewing now rather than at the point where something leaks: not that a breach is imminent, but that this is the cheapest week it will ever be to change.
Four layers, and the order matters
This is the structure the review works through, and it is the same one SureCiteAI runs in production.
Database
Row-level security on every table, so a query physically cannot return another tenant’s rows. This is the only layer that holds when every other one is wrong, which is why it is written first and never last.
Auth
The session token has to carry tenant context, and middleware has to validate it on every request. A policy that reads a claim the token does not always contain is the most common cause of results that differ between users.
Storage
Bucket and object policies are a separate policy language from table RLS, and they are the layer most often left open. A file reference secured in the database is not a file secured in the bucket.
Realtime and functions
Subscriptions, edge functions and RPCs each have their own path to the data. SECURITY DEFINER functions in particular run as their owner, which is usually how a permissions error was made to go away months ago.
What the review covers
- Tenancy model as built. Where the boundary is enforced today, which is not always where the team believes it is.
- Policies, table by table. Every RLS policy read against the queries that actually run, including the tables with no policy at all.
- Bypass paths, enumerated. Service-role key usage,
SECURITY DEFINERfunction bodies, RPC authorisation, realtime subscriptions and edge functions. - Storage rules. Bucket and object policies checked against the table policies governing the same records.
- Auth flows. What the session token carries, where it is validated, and what happens on the paths where it is not.
- Structural decisions ahead. The choices that get more expensive with every table added, called out before the next phase of development rather than after it.
Who this is for
Teams with a working Supabase platform and a reason to trust it less than they would like: a product built fast and now carrying real customers, a multi-user feature behaving unpredictably, an enterprise customer asking questions the team cannot currently answer, or a funded next phase that will multiply the schema.
It is not for greenfield projects. If there is no platform yet, there is nothing to review — the useful conversation is about building it correctly the first time.
If that is not your problem
This review is about the data-access model. Three neighbouring concerns are separate pieces of work:
How engagements work
A short call first, to establish what the platform does and what is worrying you. Read access to the schema, policies and repository, scoped however your security posture requires — a staging project or a schema dump is often enough to start. The review produces a written report, and the findings are ordered by what a user could reach today rather than by how interesting they are.
Engagements run either as a fixed review with a report, or as ongoing fractional CTO support through the next phase of development where the review is the first week of it. Which fits depends on whether you need to know, or need someone alongside the team while it is fixed.
Frequently asked questions
What is a Supabase architecture review?
An independent read of how your platform decides who can see what. It covers row-level security policies table by table, storage bucket rules, the auth flow and what a session token actually carries, and the paths that bypass all three - RPC functions marked SECURITY DEFINER, service-role keys used from the client, realtime subscriptions and edge functions. The output is a written report of where the boundary holds, where it does not, and what to change before you build the next phase on top of it.
How is this different from a penetration test?
A penetration test asks whether an attacker can get in. This asks whether your ordinary users can see each other, which is a different and more common failure. Most Supabase data exposure is not an intrusion - it is a policy that was never written, or one that is silently bypassed by a service-role key, and the people seeing the wrong records are legitimate signed-in users. A pentest reads endpoints; this reads policies, function bodies and the tenancy model underneath them.
Our app was built quickly with AI coding tools. Is that a problem?
It is the most common reason people ask for this review, and the pattern is documented rather than anecdotal. CVE-2025-48757 recorded 170 of 1,645 Lovable-generated production applications shipping with missing or misconfigured row-level security - roughly one deployment in ten exposing real user data to the public internet. AI tools generate the table and the query correctly and leave the boundary implicit, because nothing in the prompt said who else exists.
We are seeing inconsistent results with multiple users. What causes that?
Usually one of four things, and all four are the same boundary:
- A policy reads a session claim that is not always populated, so the same query returns different rows depending on how the user signed in.
- A query path runs under the service-role key and therefore skips RLS entirely.
- A SECURITY DEFINER function, added to fix a permissions error, now runs as its owner for everyone.
- A storage bucket’s object policies were never aligned with the table policies governing the same records.
All four present as intermittent bugs, which is why they survive code review.
Do you need access to our Supabase project?
Read access to the schema, policies and the application repository is enough, and it can be scoped to a staging project or a schema dump. Where access cannot be granted, a review of an exported schema, the policy definitions and the auth configuration still produces most of the findings - what it cannot do is verify behaviour, which is the part that catches the bypasses.
What do we receive at the end?
A written report: the tenancy model as it currently exists rather than as intended, a table-by-table policy assessment, the enumerated bypass paths, findings ordered by what a user could actually reach today, and a remediation sequence that states what each fix costs and what leaving it costs. Where the review finds the foundation is sound, it says so and names the specific things to hold to as the schema grows.
Is Supabase production-ready?
Yes, and that is rarely the real question. Postgres row-level security is a mature mechanism and Supabase exposes it faithfully. What is usually not production-ready is a specific implementation of it: policies written per table as features were added, with no single decision about where tenancy lives. The mechanism scales; an accumulation of individually reasonable policies does not.
Reviewing a platform before the next phase?
Tell me what the platform does and what is behaving oddly. If a review is not what you need, I will say so and tell you what is.
Last updated