fixes / launch-ready

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.*

Next steps
About the author

Cyprian Tinashe AaronsSenior Full Stack & AI Engineer

Cyprian helps founders rescue, secure, deploy, and automate AI-built apps with production-grade engineering, launch systems, and AI integration.