NC · article
Why AI-Built Apps Leak Data: The RLS Boundary Nobody Wrote
Two independent examinations of thousands of production applications found the same failure over and over. Not a novel exploit, not a clever attack — a single missing line of configuration, repeated at scale. The interesting question is not what the fix is. It is why generation produces this particular hole and almost no other.
part of Reviewing AI-Built Software · 3 articles
What Was Actually Found
1 in 10
Lovable-generated production apps with missing or misconfigured RLS — 170 of 1,645
CVE-2025-48757
175
instances of exposed personal data — medical records, bank account numbers — all in live systems
Escape.tech, 5,600 apps scanned, Oct 2025
45%
of AI-generated code failing security tests
Veracode, State of Software Security 2025
Those come from two separate pieces of work. CVE-2025-48757 examined 1,645 production applications generated on one platform and found 170 shipping without working row-level security. Separately, Escape.tech scanned 5,600 AI-built applications and reported over 2,000 high-impact vulnerabilities, more than 400 exposed secrets, and 175 instances of personal data exposure — medical records and bank account numbers among them.
Every one of those was live. None required an exploit to reach. The researchers found them the way anyone would: by asking the database for rows and being given them.
This Is Not a Story About One Tool
Lovable is named in the CVE because that is where somebody did the counting, and the counting was possible because those deployments were public and enumerable. It would be a comfortable conclusion and the wrong one.
The failure is a property of what code generation is asked to do. A prompt describes a feature: users can save notes, teams can share documents, customers can see their orders. Every one of those sentences describes what somebody can do. None of them describes what everybody else cannot. So a correct table gets generated, and a correct query gets generated, and the boundary — which was never in the requirement — is never written.
An implicit boundary in Postgres is not a weak boundary. It is no boundary. And the application works perfectly in testing, because in testing there is one user.
Why It Survives Everything That Should Catch It
This class of bug passes every check a small team normally runs, and it is worth being precise about why, because each near-miss looks like it should have worked.
| The check | Why it passes anyway |
|---|---|
| Manual testing | You are signed in as one user. There is no second tenant whose data could appear, so nothing appears wrong. |
| Code review | Every query is correctly scoped in the application code. The reviewer is reading the right filter — written in the wrong layer. |
| Generated tests | They were written from the same requirement, by the same model, which also never considered a second tenant. |
| A working product | It genuinely works. Data exposure has no symptom until somebody looks, and the people it affects cannot see it either. |
That last row is the one that matters. A memory leak degrades. A broken query errors. Missing row-level security produces a fast, correct, pleasant application — right up until the afternoon somebody points a scanner at it, and then the finding is not a bug report but a disclosure obligation.
The Fix Is Not a Policy Snippet
Search for this problem and you will be handed a policy to paste. The snippet is usually correct and it is not the fix, because the reason you are missing one policy is the same reason you will be missing the next four: nobody ever decided, once, where tenancy lives.
Teams that write policies feature by feature end up with forty individually reasonable policies and no model. They diverge. One reads a session claim, another joins a membership table, a third was replaced by a SECURITY DEFINER function during an incident and now runs as its owner for everybody. Each step was defensible. The result is not a boundary, it is forty approximations of one.
What works is deciding what a tenant is before writing the first policy, then expressing it the same way in every table. On a multi-tenant utility platform I led as fractional CTO, that decision carried a 31-table schema across three separate portals — admins, agencies and tenants — without the policies drifting, because there was only ever one rule being restated.
Above the database, the same discipline becomes layers: SureCiteAI enforces isolation four times over — database RLS, per-tenant vector namespaces, session tokens carrying tenant context, and subdomain routing — so that no single mistake is sufficient to leak.
What To Do This Week
Two queries will tell you whether you are in the ten percent. Count the fraction of tables in your public schema with row-level security enabled, and count the ones that have it enabled with no policy attached. Both are read-only, both run in the Supabase SQL editor, and both are printed in full in the production-readiness piece.
If coverage is not 100%, stop reading and fix that first; nothing else matters while a table is openly readable. If it is 100%, the remaining work is the paths that go around the policies rather than through them — the service-role key, SECURITY DEFINER functions, and storage buckets whose object rules were never aligned with the tables they belong to.
And if you would rather somebody else ran this and told you what it means, that is a Supabase architecture review.
Frequently Asked Questions
What is CVE-2025-48757?
A disclosure concerning applications generated on the Lovable platform, recording that 170 of 1,645 production applications examined were shipping with missing or misconfigured row-level security, exposing real user data to the public internet. That is roughly one deployment in ten. The finding is about what generated applications shipped with, not about a flaw in the act of generating them.
Is this a problem with Lovable specifically?
No, and treating it that way is how the lesson gets missed. Lovable is named because it is where somebody did the measuring, and it is where the measuring was possible because the deployments were public. The same failure appears in anything that generates a working table and a working query without being asked who else exists. What varies between tools is how visible the result is.
How do I check whether my own app has this?
Two SQL queries against your own project. Count what fraction of tables in the public schema have row-level security enabled — the passing value is all of them — and count the tables that have it enabled with no policy attached. Both run read-only in the Supabase SQL editor and take about a minute. The queries are printed in full in the production-readiness piece linked from this article.
We use a server-side API, so nothing hits the database directly. Are we safe?
Only if that is true of every path, including the ones added in a hurry. Supabase exposes the public schema over PostgREST by default, so a table without RLS is readable by anyone holding the anon key — which ships in the client bundle. "Nothing queries that table" describes your code, not what is reachable. The check costs a minute and settles it.
If we fix the policies, are we done?
Policies are the visible half. The durable fix is deciding where tenancy lives and then enforcing it in one place rather than forty, because policies written per table as features arrive will drift apart again within a quarter. Storage buckets, SECURITY DEFINER functions and any path using the service-role key all sit outside the policies you just wrote, and each is a way back around them.
Read Next
Ready to discuss your AI project?
Book a free 30-minute discovery call to explore how AI can transform your business. Or if you already have a codebase, get an instant architecture report at SystemAudit.dev No technical knowledge needed, results in 3 minutes.
About the Author
Nic Chin is an AI Architect and Fractional CTO who helps companies design and deploy production AI systems including RAG pipelines, multi-agent systems, and AI automation platforms. He has delivered enterprise AI solutions across the UK, US, and Europe, and provides AI consulting in Malaysia and Singapore.