How I Would Fix database rules leaking customer data in a Lovable plus Supabase AI-built SaaS app Using Launch Ready.
The symptom is usually simple to spot: one user can see another user's rows, files, or profile data after login. In a Lovable plus Supabase SaaS app, the...
How I Would Fix database rules leaking customer data in a Lovable plus Supabase AI-built SaaS app Using Launch Ready
The symptom is usually simple to spot: one user can see another user's rows, files, or profile data after login. In a Lovable plus Supabase SaaS app, the most likely root cause is broken Row Level Security, usually because a table has no policy, a policy is too broad, or the app is querying a table through an over-privileged key.
The first thing I would inspect is the Supabase dashboard for the exact table permissions and policies, then I would trace which client key the app is using in production. If the frontend is holding a service role key, or if the table has permissive `select` policies, that is a data exposure incident until proven otherwise.
Triage in the First Hour
1. Check whether the leak is active right now.
- Log out and test with two separate user accounts.
- Confirm whether user A can read user B records from list views, detail pages, exports, or API responses.
- If there is any cross-account access, treat it as a live security issue.
2. Inspect Supabase Table Editor and Policies.
- Open every customer-facing table.
- Look for missing RLS, `using (true)`, `with check (true)`, or policies that do not filter by `auth.uid()`.
- Check whether policies exist for `select`, `insert`, `update`, and `delete`.
3. Review environment variables in Lovable and deployment settings.
- Confirm the frontend only uses the public anon key.
- Confirm the service role key is not exposed in browser code, edge functions meant for clients, or build artifacts.
- Verify all secrets are stored server-side only.
4. Check recent builds and previews.
- Compare production with preview and local branches.
- Find any recent schema changes from Lovable-generated code that may have bypassed auth logic.
- Look for newly added API routes that query Supabase directly without filters.
5. Inspect logs and audit signals.
- Review Supabase auth logs for unusual access patterns.
- Check application logs for requests returning large record sets or repeated 200 responses on unauthorized reads.
- Note any spikes in support tickets or user reports tied to data visibility.
6. Freeze risky changes if needed.
- Pause deploys until policies are verified.
- Disable any export endpoint or admin-like feature that could widen exposure.
- If customer data is already exposed, start incident handling and notify only after confirming scope.
Root Causes
| Likely cause | What it looks like | How I confirm it | |---|---|---| | RLS is off on one or more tables | Any authenticated user can read rows they do not own | Check table settings in Supabase and run two-account tests | | Policy uses `true` or missing owner filter | Policies exist but allow everyone | Read policy SQL and verify it matches `auth.uid()` or tenant scope | | Service role key leaked into client code | Browser can query everything directly | Search repo, build output, and deployed env vars for `service_role` | | Join query bypasses ownership checks | Parent table secure, child table leaks through join | Test nested selects and related records across users | | Misconfigured storage bucket policies | Files attached to one account are visible to others | Inspect bucket access rules and signed URL behavior | | AI-generated code skipped auth guards | Lovable scaffolded fast but did not enforce security paths | Diff generated components against secure backend rules |
A quick diagnostic query pattern I use during triage:
select * from pg_policies where schemaname = 'public' order by tablename, policyname;
If a table with customer data has no policies listed here, I assume it is unsafe until I prove otherwise.
The Fix Plan
1. Stop the bleed first.
- Turn on RLS for every customer-owned table.
- Remove any policy that allows broad public reads while you investigate.
- If necessary, temporarily restrict access to authenticated users only.
2. Rebuild policies around ownership or tenant scope.
- For single-user apps, use `auth.uid() = user_id`.
- For multi-tenant apps, use `tenant_id` plus membership checks through a trusted mapping table.
- Apply consistent rules across all CRUD operations, not just reads.
3. Remove privileged keys from anything client-facing.
- The browser should use only the anon key.
- Move admin actions into server-side functions or protected edge functions.
- Rotate any exposed secrets immediately after cleanup.
4. Audit every query path used by Lovable-generated UI.
- Check list pages, detail pages, search endpoints, exports, notifications, webhooks, and background jobs.
- Make sure each path enforces authorization before returning data.
- Do not rely on frontend filtering as your security boundary.
5. Tighten storage access too.
- Use private buckets for customer files unless there is a clear reason not to.
- Generate signed URLs with short expiry windows where needed.
- Verify file paths cannot be guessed across accounts.
6. Add safe defaults before redeploying.
- Deny by default on new tables and buckets.
- Require explicit policies before data becomes readable.
- Document who owns each table and who can access it.
7. Rotate credentials if exposure was real.
- Rotate database passwords if they were reused anywhere sensitive.
- Rotate API keys and third-party tokens that may have been accessible through logs or config leaks.
- Reissue email or storage credentials if they were embedded in build-time variables.
My rule here is simple: do not patch symptoms in React if the actual leak lives in database policy. Fix authorization at the data layer first, then clean up the UI second.
Regression Tests Before Redeploy
I would not ship this fix until these checks pass:
- Two-account isolation test
- User A cannot read User B rows through UI or direct API calls
- Expected result: zero cross-account records returned
- Anonymous access test
- Logged-out users cannot read protected tables
- Expected result: 401 or empty response where appropriate
- Insert/update validation test
- Users can only create records tied to their own identity or tenant
- Expected result: writes fail when ownership does not match
- Storage access test
- One account cannot open another account's private file URL
- Expected result: access denied unless signed URL belongs to current user
- Export/search test
- CSV export and global search do not bypass row filters
- Expected result: exported dataset contains only authorized rows
- Build verification
- Production build uses anon key only on client side
- Expected result: no service role key appears in browser bundle or source maps
- Security acceptance criteria
- All customer tables have RLS enabled
- Every policy has an explicit ownership or tenant condition
- No direct public read path exists for sensitive columns
I also want at least one negative test per endpoint that tries an unauthorized ID from another account. If that passes even once, the fix is incomplete.
Prevention
I would put guardrails in four places so this does not come back after the next Lovable sprint.
1. Security review on every schema change
- Any new table must ship with RLS enabled by default.
- Any new policy must be reviewed before deploy.
- Keep this as part of code review instead of treating it as a separate task nobody owns.
2. Monitoring for abnormal reads
- Alert on spikes in list endpoints returning unusually large datasets.
- Watch for repeated requests against IDs outside normal user scope.
\n- Track failed authorization attempts so you can spot probing early without logging sensitive payloads.
3. Safer AI-generated development workflow
4. UX guardrails that reduce support load \n- Hide admin-only controls from regular users so they do not trigger risky paths by accident.\n- Show clear loading and error states when access fails instead of exposing raw API errors.\n- Use tenant-aware navigation so users never see records outside their workspace.\n\n5. Performance guardrails too\n \n- Add indexes on ownership columns like `user_id` or `tenant_id` so secure queries stay fast.\n- Watch p95 latency on filtered queries; I would aim for under 250 ms on common dashboard reads.\n- Slow authorization often gets "fixed" by removing checks later unless performance is planned early.\n\n## When to Use Launch Ready\n\nLaunch Ready fits when you already have a working Lovable plus Supabase app but need it made production-safe fast. It gives you a clean handoff point instead of leaving secrets,\ndomain routing,\nand monitoring half done.\n\n## References\n\n- https://roadmap.sh/cyber-security\n- https://roadmap.sh/api-security-best-practices\n- https://roadmap.sh/code-review-best-practices\n- https://supabase.com/docs/guides/database/postgres/row-level-security\n- https://supabase.com/docs/guides/auth/row-level-security
---
Take the next step
If this is a problem in your product right now, here is what to do next:
- [Use the free Cyprian tools](/tools) - estimate cost, score app risk, check launch readiness, or pick the right service sprint.
- [Book a discovery call](/contact) - I will tell you honestly whether you need a sprint or if you can DIY the next step.
*Written by Cyprian Tinashe Aarons - senior full-stack and AI engineer helping founders rescue, launch, automate, and scale AI-built products.*
Cyprian Tinashe Aarons — Senior Full Stack & AI Engineer
Cyprian helps founders rescue, secure, deploy, and automate AI-built apps with production-grade engineering, launch systems, and AI integration.