fixes / launch-ready

How I Would Fix database rules leaking customer data in a Supabase and Edge Functions AI-built SaaS app Using Launch Ready.

The symptom is usually ugly and obvious once a founder looks closely: one customer can see another customer's records, exports include private rows, or an...

How I Would Fix database rules leaking customer data in a Supabase and Edge Functions AI-built SaaS app Using Launch Ready

The symptom is usually ugly and obvious once a founder looks closely: one customer can see another customer's records, exports include private rows, or an AI feature starts returning data it should never have access to. In a Supabase and Edge Functions SaaS app, my first assumption is not "the AI broke it", it is that row-level security, service role usage, or function auth boundaries are wrong.

The first thing I would inspect is the exact path from browser to database. I want to know whether the leak comes from a public client query, an Edge Function using the service role key too broadly, or a policy that looks correct but matches too many rows.

Triage in the First Hour

1. Check the affected tables in Supabase and confirm which ones contain customer scoped data. 2. Open the Row Level Security policies for those tables and look for broad `using (true)` or `with check (true)` rules. 3. Inspect every Edge Function that reads or writes customer data. 4. Verify whether any function uses the Supabase service role key for normal user requests. 5. Review recent deploys, migrations, and policy changes from the last 7 days. 6. Check auth claims in production sessions, especially `user_id`, `org_id`, `tenant_id`, or similar fields. 7. Look at logs for repeated cross-tenant reads, 403s that suddenly disappeared, or unexpectedly high query volume. 8. Confirm whether frontend code is calling Supabase directly with the anon key for data that should be server-only. 9. Audit storage buckets if files or generated reports are part of the leak. 10. Freeze new releases until you know which path exposed data.

If I had 60 minutes only, I would prioritize policy review and function review before touching UI code. Most leaks happen because access control was assumed instead of enforced.

A quick diagnostic query pattern I would look for is this kind of tenant filter discipline:

select *
from customer_records
where org_id = auth.jwt() ->> 'org_id'
limit 20;

If your app relies on something like this but the policy does not enforce it at the database layer, you do not have security. You have a suggestion.

Root Causes

| Likely cause | What it looks like | How I confirm it | | --- | --- | --- | | Missing RLS | Anyone with a valid client key can read rows | Table policies are absent or RLS is off | | Overbroad policy | Users can see all rows in a table | Policy uses `true`, weak joins, or wrong tenant field | | Service role misuse | Edge Function returns everything because it bypasses RLS | Function uses service key for user-scoped reads | | Bad JWT claim mapping | Policies check `org_id` but token has no reliable org claim | Compare auth token claims to policy conditions | | Broken ownership model | Rows do not store tenant/user ownership consistently | Inspect schema for nullable or missing foreign keys | | Storage exposure | Private files are reachable by predictable URLs or public bucket settings | Check bucket visibility and signed URL flow |

My usual pattern in AI-built apps is this: the frontend works fine in testing because the founder used one account only, then production exposes tenant mixups as soon as multiple customers sign up. That turns into support load, trust loss, and sometimes a compliance problem if customer data crosses accounts.

The Fix Plan

First, I would stop the bleeding. If there is active leakage, I would temporarily disable the risky Edge Function routes or gate them behind admin access while I fix authorization properly.

Second, I would make authorization explicit at the database layer. In Supabase, that means enabling RLS on every customer-owned table and writing policies that match one tenant only.

Third, I would remove any unnecessary use of the service role key. The service role should be reserved for truly privileged server tasks like internal admin jobs, not normal request handling.

Fourth, I would standardize ownership fields across tables. Every row that belongs to a customer should have a clear `user_id`, `org_id`, or `tenant_id`, plus foreign keys where possible.

Fifth, I would refactor Edge Functions so they verify identity first and then call Supabase with the least privilege needed. If a function needs elevated rights for one step only, I split that step out and keep the rest scoped to the user.

Sixth, I would add logging around access decisions without logging private payloads. I want enough signal to debug future incidents without creating another privacy issue.

A safe repair sequence looks like this:

1. Turn on RLS for all sensitive tables. 2. Replace broad policies with tenant-specific policies. 3. Remove direct client access to any table that should be server-only. 4. Audit all Edge Functions for service role usage. 5. Add explicit auth checks at function entry. 6. Redeploy behind feature flags where possible. 7. Validate with two test tenants before reopening traffic.

My recommendation is to fix this in one controlled sprint instead of patching symptoms over several days. Partial fixes often leave one backdoor open while another gets closed.

Here is how I would think about flow control during the repair:

If you discover that multiple tables share inconsistent ownership logic, do not try to salvage each one manually in isolation. Standardize the pattern first, then apply it everywhere.

Regression Tests Before Redeploy

Before shipping anything back to production, I would run tests against at least two seeded tenants: Tenant A and Tenant B. The goal is simple: prove Tenant A cannot read, update, export, or infer Tenant B's records through any supported path.

Acceptance criteria:

  • Tenant A can only see its own rows in direct queries.
  • Tenant A cannot access Tenant B data through Edge Functions.
  • Anonymous users cannot read protected tables.
  • Service role operations are limited to internal jobs only.
  • Storage objects tied to one tenant cannot be fetched by another tenant.
  • No endpoint returns more than expected columns or hidden metadata.
  • Logs show denied requests where appropriate without exposing sensitive values.

QA checks I would run:

1. Direct browser query against protected resources with anon credentials. 2. Authenticated request as Tenant A against records owned by Tenant B. 3. Same test through each Edge Function route that touches customer data. 4. Signed URL tests for any file downloads or generated assets. 5. Negative tests for missing JWTs, expired JWTs, and malformed claims. 6. Regression test after re-running migrations locally and in staging. 7. Smoke test on mobile and desktop if any dashboard views changed.

I also want one practical performance check here: make sure new authorization logic does not blow up response times beyond reason. If policy changes push p95 API latency above 400 ms on common reads, I would inspect indexes and query plans before launch because slow dashboards create support tickets fast.

Prevention

The biggest prevention step is boring but effective: treat access control as part of schema design, not as app code decoration.

What I would put in place:

  • RLS required on every table with customer data.
  • A code review checklist item for every new table and every new Edge Function.
  • Separate roles for public reads, authenticated reads, admin tasks, and background jobs.
  • Minimal secrets exposure in deployment environments.
  • Centralized logging for auth failures and denied requests.
  • Alerts on unusual cross-tenant access patterns or spikes in denied queries.
  • A staging dataset with at least two tenants so cross-access bugs are visible early.

I also recommend adding guardrails around AI features specifically. If an LLM-powered function can query customer data, it needs hard boundaries on what context it receives and what tools it can call; otherwise prompt injection can turn into unauthorized retrieval very quickly.

From a UX angle, do not hide permission failures behind generic errors forever either. Clear "you do not have access" states reduce support confusion and help founders spot broken flows faster during QA.

On performance and maintainability: keep policies simple enough that another engineer can reason about them six months later. Complex nested joins inside RLS often become fragile under load and hard to debug when something fails at 2 am.

When to Use Launch Ready

Use Launch Ready when you need this fixed fast without turning your team into part-time infrastructure engineers.

  • Domain setup
  • Email setup
  • Cloudflare
  • SSL
  • Deployment
  • Secrets handling
  • Monitoring
  • DNS redirects
  • Subdomains
  • Caching
  • DDoS protection
  • SPF/DKIM/DMARC
  • Production deployment
  • Environment variables
  • Uptime monitoring
  • Handover checklist

This sprint fits best after you have identified the leak source but before you push a risky redeploy live again. If your app already has working product logic but weak deployment hygiene or secret handling around Supabase and Edge Functions, this is exactly where I step in.

What you should prepare before booking: 1. Supabase project access with admin permissions where needed. 2. Repository access for frontend and Edge Functions code. 3. List of affected tables and endpoints if you already know them. 4. Current domain registrar access if DNS changes are needed. 5. Any recent incident notes from support or customers. 6. Production environment variable list minus secret values shared securely.

My advice is simple: if customer data has leaked once, do not treat launch setup as separate from security cleanup again later on some vague roadmap date next month.

References

1. Roadmap.sh API Security Best Practices - https://roadmap.sh/api-security-best-practices 2. Roadmap.sh Cyber Security - https://roadmap.sh/cyber-security 3. Roadmap.sh Code Review Best Practices - https://roadmap.sh/code-review-best-practices 4. Supabase Row Level Security docs - https://supabase.com/docs/guides/database/postgres/row-level-security 5. Supabase Edge Functions docs - https://supabase.com/docs/guides/functions

---

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.