Why is USING (true) the riskiest RLS policy in Lovable.dev?

If you got pointed here because someone said your Lovable.dev app has a USING (true) policy on a Supabase table, this is the short, calm version of what that means, why the data is reachable from any browser, and which policy bodies to put in its place.
Short answer
A Postgres Row Level Security policy with body USING (true) evaluates to true for every row, so the table returns its full contents to any caller that presents a valid Supabase anon key. According to Supabase's documentation on Row Level Security, the USING clause acts the same way as "adding a WHERE clause to every query." A WHERE clause of true filters nothing. In May 2025, Matt Palmer's CVE-2025-48757 writeup traced 303 exposed endpoints across more than 170 Lovable projects to this exact policy shape and its missing-policy cousin.
What you should know
How does USING (true) actually grant access?
Supabase routes every HTTP request through PostgREST, which converts the request into SQL run under either the anon or authenticated role. Row Level Security policies are applied as implicit WHERE clauses, one per applicable policy, joined with OR for permissive policies and AND for restrictive ones. When a permissive policy says USING (true), the implicit filter contributes a tautology, so the engine returns every row that the SELECT statement would have returned without RLS in place.
The wider trap is that having any policy turns the table from "RLS denied" into "RLS allowed." Per the Supabase docs, with no policy attached the table denies all anon access by default once RLS is enabled. Add one permissive policy of USING (true), and the deny-by-default switch flips, because Postgres only enforces the most permissive applicable policy on a SELECT.
Lawrence has watched audits where this happened because the AI was asked to "make this table readable for the homepage." The generated SQL did exactly that, on every column, for every row, for every visitor on the open internet.
Why does Lovable's security scan miss it?
The scan was released on April 24, 2025, three weeks after Matt Palmer's initial coordinated disclosure to Lovable. Per Lovable's security overview page, the scanner runs four checks: RLS Analysis, Database Security Check, Code Security Review, and Dependency Audit. The first two read the schema and report which tables have RLS enabled and which have at least one policy attached.
That is where the gap sits. The scanner does not parse the body of each policy and decide whether the body actually narrows access. A table with ENABLE ROW LEVEL SECURITY and a USING (true) policy gets the green check, because both surface conditions are met. The Lovable docs themselves note the scanners "cannot guarantee complete security" and recommend an additional professional review for sensitive data, but most builders skim past that line.
The Superblocks writeup of CVE-2025-48757 confirms the same: the scanner "only flagged the presence of RLS, not whether it worked. It failed to detect misconfigured policies, creating a false sense of security."
What does a correct policy look like for each table type?
The correct body depends on the intended access shape. The three patterns below cover most Lovable schemas.
| Table type | Correct USING body | What WITH CHECK adds |
|---|---|---|
| User-owned records (todos, notes, files) | auth.uid() = user_id | Same expression in WITH CHECK so a user cannot move a row to a different user_id |
| Tenant-scoped records (multi-org SaaS) | org_id IN (SELECT org_id FROM memberships WHERE user_id = auth.uid()) | Same join in WITH CHECK so writes stay inside the user's own orgs |
| Public catalog or marketing content | true on SELECT only | auth.uid() IS NOT NULL for INSERT, UPDATE, DELETE |
The third row is the one developers misread. USING (true) on a SELECT for a blog or product catalog is intentional and safe; the same body on a user-data table is the bug. The check is whether the table contains anything that the public should not have already received in a sitemap.
For INSERT and UPDATE on user-owned tables, the WITH CHECK clause is the safety net. Per the Supabase RLS docs, "if no WITH CHECK expression is defined, then the USING expression will be used both to determine which rows are visible and which new rows will be allowed to be added." That sounds convenient until you realise a user who can read their own row can also update it to set user_id to someone else's uuid, gaining write access to their data. A WITH CHECK of auth.uid() = user_id blocks that path.
How do you find every USING (true) policy in your project?
Three checks, in order of confidence.
- Open the Supabase dashboard, go to Authentication then Policies, sort by table name. Any policy whose body reads exactly
trueis the highest-priority fix. - Run the Supabase Performance and Security Advisor. Check 0024, Policy with overly permissive expression, flags USING (true) policies on a per-table basis with a direct link to the policy editor.
- Run a SQL query against pg_policies in the SQL editor:
SELECT schemaname, tablename, policyname, qual FROM pg_policies WHERE qual = 'true';. The qual column holds the USING expression; matching the exact string true returns the policies that need replacement.
After each rewrite, query the table from a fresh incognito window with the anon key. If the response is [] or a 401, the policy is doing its job. If rows come back, the policy is still permissive and the new body is wrong.
For mobile apps that wrap a Lovable backend in a compiled APK or IPA, the same checks need to run from outside the studio. PTKD.com (https://ptkd.com) is one of the scanners that maps the Supabase endpoints out of a built binary and probes each one with the embedded anon key. The report lines up findings against the OWASP MASVS-AUTH category so a developer can see which endpoints respond to unauthenticated queries before submitting the build to App Store Connect or Google Play.
How does this map to OWASP MASVS for the mobile side?
The OWASP MASVS framework, hosted at mas.owasp.org, scopes server-side authorization checks under the MASVS-AUTH category. The control text states the app should perform sensitive operations only after an authorization check has run on the backend, and that the check should be enforced server-side, not by the client. A Supabase table protected by USING (true) violates that control in practice, because the backend is performing the check and finding it trivially satisfied.
The Wiz Research report on vibe-coded apps found that roughly 20% of vibe-coded apps presented at least one critical authorization gap, with permissive or missing RLS on Supabase tables as the largest single category. That number reads as directional, not Apple-published, but it is a useful sanity check on how common the pattern still is fifteen months after the original CVE.
The practical consequence for mobile builders is that App Review and Play Console reviewers do not test RLS policies directly. They test what the app does on launch. A permissive policy can pass review and then surface as a public data leak the first time a security researcher points a fuzzer at the Supabase domain in your bundle. The cost of fixing it before submission is one afternoon. The cost of fixing it after a disclosure is whatever your jurisdiction's breach notification regime requires.
What to watch out for
Three traps that frequently bite after the obvious fixes ship.
First, the service_role key never goes through RLS. Per the Supabase documentation on API keys, the service_role bypasses all row-level checks by design. If that key ended up in your Lovable bundle, no policy patch will help; rotate the key, redeploy, and search past commits for any place the key was hard-coded.
Second, a new feature can quietly re-introduce the bug. A future AI prompt that says "add a comments table for blog posts" will often emit a fresh USING (true) policy because the AI is replaying the catalog pattern. Add a CI check or a weekly cron that runs the pg_policies query above and alerts on any new row.
Third, fixing the policy does not undo prior access. Supabase retains query logs for seven days on the free tier and longer on paid plans. Filter by the anon role and look for queries that returned large row counts from sensitive tables. If you find them, treat the affected records as compromised and follow your standard incident response process.
Key takeaways
- USING (true) is not a policy in any meaningful sense; it is the absence of a filter dressed up as one. Treat it the same as no RLS at all for the operations it covers.
- Replace it with auth.uid() or membership-join bodies for user-owned and tenant-scoped tables. Keep USING (true) only on SELECT for genuinely public catalogs, and pair it with column-level scoping where the underlying table holds non-public columns.
- Lovable's built-in scanner is a smoke test, not an audit. Manually inspect each policy body or run check 0024 in the Supabase Advisor.
- For builders shipping a Lovable backend through a mobile app, an external pre-submission scan from a platform like PTKD.com (https://ptkd.com) maps the exposed Supabase endpoints out of the compiled APK or IPA and flags them against OWASP MASVS-AUTH controls.
- Record the policy review in your project's CHANGELOG so the next AI-generated migration cannot quietly revert it.
- #lovable
- #supabase
- #rls
- #using-true
- #ai-coded apps
- #row-level-security
- #cve-2025-48757
Frequently asked questions
- Is USING (true) ever a valid Supabase RLS policy?
- Yes, for SELECT on tables that are genuinely public, such as a product catalog, a blog feed, or a public leaderboard. The same body on INSERT, UPDATE, DELETE, or on a table that holds user data is the vulnerability shape. The test is whether the table's contents could safely appear in a sitemap aimed at search engines. If the answer is no, replace the policy body.
- Does enabling RLS without writing a policy block all anon access?
- Yes. Per the Supabase documentation, once ENABLE ROW LEVEL SECURITY is set on a table, the table denies all anon and authenticated traffic until at least one permissive policy is added. The risk arrives the moment a USING (true) policy is attached: a single permissive policy applied via OR opens the gate for the operation it covers (SELECT, INSERT, UPDATE, or DELETE).
- Will the Supabase Performance Advisor catch every USING (true) policy?
- Check 0024, Policy with overly permissive expression, in the Supabase Performance and Security Advisor flags USING (true) policies and lists them per table. The advisor catches the literal true body; it does not always catch logically permissive bodies like auth.role() = 'anon' or id IS NOT NULL. Pair the advisor output with a manual review of each policy body and a live query test from an incognito session.
- How is USING (true) different from leaking the service_role key?
- The service_role key bypasses RLS entirely, so leaking it means the policy posture is irrelevant: any caller with the key reads or writes anything. USING (true) is a flaw in the policy itself, exploited with the anon key that ships in every Lovable bundle. Both end with public data, but the fix paths are different. Rotate the service_role key, or rewrite the policy bodies.
- Should I take my Lovable app offline while I fix the policies?
- Usually no. Replacing USING (true) with auth.uid() checks takes minutes per table. The exposure during the fix window is the same exposure that already existed for the prior weeks or months. For apps that hold regulated data (health, financial, identity), the calculation flips: pause new traffic, fix in a single migration, and notify affected users in line with your standard incident response plan.
Keep reading
Journal · AI-coded appsLovable.dev Supabase RLS bypass: how it happens, how to fix itThe CVE-2025-48757 attack vector that exposed 170+ Lovable apps, why Lovable's own security scan misses it, and the policy patterns that actually close the gap.10 min read
Journal · AI-coded appsWhy my Lovable Supabase data is still public even with RLS enabledEnabling Row Level Security is not the same as writing a policy that filters anything. Why USING (true) is the AI's default, and the patterns that actually scope access.9 min read
Journal · AI-coded appsWhat should I check in my Lovable app before App Store submission?A pre-submission security pass for Lovable builds: Supabase RLS, exposed keys, Edge Functions, and the App Review and Play policies that catch them.8 min read
Journal · AI-coded appsBolt.new vs Lovable: which is safer to ship to production?A side by side look at how Bolt.new and Lovable handle secrets, Supabase RLS, and server side logic, and what each builder gets wrong by default.8 min read
Scan your app in minutes
Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.
Try PTKD free