How I Would Fix database rules leaking customer data in a Lovable plus Supabase client portal Using Launch Ready.
If a client portal is leaking customer data, I treat it as a production incident, not a UI bug. The usual symptom is simple: one logged-in customer can...
How I Would Fix database rules leaking customer data in a Lovable plus Supabase client portal Using Launch Ready
If a client portal is leaking customer data, I treat it as a production incident, not a UI bug. The usual symptom is simple: one logged-in customer can see another customer's records, files, invoices, or profile details.
The most likely root cause is broken row-level security in Supabase, usually because a table has no RLS policy, the policy is too broad, or the app is querying through an admin key by mistake. The first thing I would inspect is whether the frontend or server code is using the service role key anywhere it should not, because that instantly turns every permission mistake into a data leak.
Triage in the First Hour
1. Check whether the leak is reproducible in a normal user session.
- Log in as two different customer accounts.
- Compare what each account can see in the portal.
- Confirm whether the issue affects one table, multiple tables, or all customer data.
2. Inspect Supabase auth and RLS status first.
- Open the affected tables in Supabase.
- Confirm RLS is enabled on every customer-facing table.
- Review policies for `select`, `insert`, `update`, and `delete`.
3. Check recent deploys from Lovable and any manual edits.
- Look for schema changes, new queries, new edge functions, or copied SQL from AI output.
- Identify the last release before the leak started.
4. Review environment variables and secrets handling.
- Confirm no service role key was exposed to the browser bundle.
- Verify public and private keys are separated correctly.
- Check Cloudflare logs or app logs for unusual access patterns.
5. Inspect database logs and Supabase dashboard activity.
- Look for queries returning more rows than expected.
- Check whether requests are coming from authenticated users or privileged backend code.
6. Audit the frontend data-fetching code.
- Search for `.from(...).select('*')` without filters tied to `auth.uid()`.
- Check for joins that accidentally expose related records.
7. Freeze risky changes until containment is clear.
- Pause non-essential deploys.
- If needed, temporarily disable the affected portal area while keeping login and support available.
-- Quick RLS sanity check for a customer-owned table alter table public.invoices enable row level security; create policy "customers can read own invoices" on public.invoices for select using (customer_id = auth.uid());
Root Causes
| Likely cause | What it looks like | How I confirm it | |---|---|---| | RLS disabled | Any authenticated user can read all rows | Check table settings in Supabase and test with two accounts | | Overly broad policy | Policy uses `true`, missing ownership checks, or weak role logic | Read each policy line by line and compare against intended access model | | Service role key used in client code | Frontend bypasses all database permissions | Search repo, build output, and Lovable env settings for `service_role` | | Wrong ownership field | Policies check `user_id` but rows use `customer_id`, org ID, or email | Compare schema to policy conditions and sample records | | Broken join or view | A view exposes columns from related tables without filtering | Inspect views, RPC functions, and joins used by portal screens | | Missing tenant isolation | Multi-tenant portal lacks org scoping everywhere | Test cross-account access across every endpoint and query path |
The biggest business risk here is not just exposure of data. It is lost trust, possible legal exposure under GDPR or UK GDPR, support load, app store rejection if mobile is involved, and delayed launch while you clean up a mess that should have been blocked earlier.
The Fix Plan
1. Contain first, then repair.
- If customer data is actively exposed, disable the affected feature or route until controls are fixed.
- Do not keep shipping cosmetic changes while access control is broken.
2. Inventory every sensitive table and query path.
- List profiles, invoices, documents, messages, subscriptions, projects, notes, attachments, and audit logs.
- Trace where each screen reads data from: direct table query, view, RPC function, or edge function.
3. Enforce least privilege at the database layer.
- Turn on RLS for every customer-owned table.
- Write explicit policies based on authenticated identity and tenant ownership.
- Avoid policies that depend on frontend filtering alone.
4. Remove privileged keys from browser-accessible code.
- Use only anon/public keys in client-side Lovable pages.
- Move admin operations to server-side functions with strict checks.
- Rotate any exposed secrets immediately after cleanup.
5. Fix ownership logic consistently.
- Pick one source of truth: `user_id`, `org_id`, or both if this is multi-tenant B2B.
- Update schema columns so every record can be tied back to exactly one allowed scope.
6. Review views and functions with the same rigor as tables.
- Views can leak data just as easily as tables if they are not filtered correctly.
- RPC functions should validate identity before returning anything sensitive.
7. Add safe logging and monitoring before redeploying.
- Log denied access attempts without storing sensitive payloads.
- Add alerts for unexpected spikes in row counts returned per request.
8. Ship with rollback ready.
- Keep a backup of the last known good migration set.
- Deploy during a low-traffic window if possible.
- Verify that rollback does not re-open old permissions.
My rule here is simple: fix authorization at the database level first, then clean up frontend assumptions second. If you only patch the UI query logic, one missed endpoint will leak data again.
Regression Tests Before Redeploy
I would not redeploy this until these checks pass:
1. Cross-account access test
- User A cannot read User B's records through any screen or API call.
- User B cannot update or delete User A's rows either.
2. Anonymous access test
- Logged-out users get blocked from every protected dataset.
- Public pages still load if they are meant to be public.
3. Role-based access test
- Admins can see admin-only records only through approved flows.
- Support staff cannot see more than their assigned scope.
4. Direct API test
- Hit Supabase endpoints directly instead of only testing the UI.
- Confirm policies still hold even when requests bypass Lovable screens.
5. Query shape review
- No unfiltered `.select('*')` against sensitive tables unless RLS fully protects it and that behavior is intentional.
- No accidental exposure through nested joins or views.
6. Edge case coverage
- Empty account state returns zero rows cleanly.
- Deleted users do not retain visible access paths.
\n- Shared organizations do not leak members across tenants.
7. Acceptance criteria \n- 100 percent of sensitive tables have RLS enabled.\n- 0 cross-account reads are possible in test.\n- 0 service role keys appear in client bundles.\n- Portal pages load within 2 seconds p95 after fixes.\n- Lighthouse performance stays above 85 on key pages after any added guards.\n 8. Security review gate \n- One person other than me reviews every policy before merge.\n- Any exception must be documented with business justification.\n
Prevention
I would put guardrails around three layers: database rules, code review, and monitoring.
- Database guardrails:
\n Every new table ships with RLS enabled by default.\n Every new policy must state who owns the row and why.\n Every admin path gets isolated into server-side code only.\n
- Code review guardrails:
\n Reviewers check behavior before style.\n They look for auth context usage, tenant filters, secret handling, joins, views, and RPC calls.\n I would reject any change that adds a sensitive query without an explicit access rule.\n
- Monitoring guardrails:
\n Alert on unusually high row counts per request.\n Alert on repeated denied reads from one account.\n Track error spikes after deploys so bad policies do not sit unnoticed for days.\n
- UX guardrails:
\n Show clear empty states when users have no records rather than falling back to broad queries.\n Make loading and error states obvious so developers are less tempted to "just return everything" during debugging.\n
- Performance guardrails:
\n Index tenant IDs and ownership fields used by policies.\n Keep p95 query latency under 200 ms on core portal reads.\n Avoid expensive joins that force developers to weaken security later for speed.\n
When to Use Launch Ready
Use Launch Ready when you need me to stabilize this fast without turning your product into a bigger rebuild project. deployment hardening, environment variables, secret cleanup, uptime monitoring, and a handover checklist so you know what changed.
This sprint fits best when:
- The product already works but security or deployment quality is blocking launch.
- You need production-safe hosting before sending real customers into the portal.
- You want one senior engineer to fix infra risk instead of paying piecemeal for five small contractors who each touch a different layer badly.
What I would want from you before starting:
- Access to Lovable project settings and source export if available
- Supabase project access with admin rights
- Domain registrar access
- Cloudflare account access if already set up
- A list of which tables contain customer data
- Any screenshots or examples of leaked records
If you are unsure whether this needs a full rescue sprint or just Launch Ready setup work first, I would start with Launch Ready because it gives us control over DNS, SSL, monitoring, and deployment hygiene while we isolate the data leak properly.
Delivery Map
References
1. https://roadmap.sh/api-security-best-practices 2. https://roadmap.sh/code-review-best-practices 3. https://supabase.com/docs/guides/database/postgres/row-level-security 4. https://supabase.com/docs/guides/auth/row-level-security 5. https://supabase.com/docs/guides/platform/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.