How I Would Fix database rules leaking customer data in a Cursor-built Next.js automation-heavy service business Using Launch Ready.
The symptom is usually not subtle. A founder notices one customer can see another customer's records, an internal admin screen shows too much, or a...
How I Would Fix database rules leaking customer data in a Cursor-built Next.js automation-heavy service business Using Launch Ready
The symptom is usually not subtle. A founder notices one customer can see another customer's records, an internal admin screen shows too much, or a support ticket includes data that should never have left the database.
The most likely root cause is weak authorization at the data layer, not just a bad UI check. In Cursor-built Next.js apps, I often find the frontend looks fine, but the database rules, API routes, or server actions are trusting client input too much. The first thing I would inspect is the exact path from browser to database: which route reads the data, which user context is attached, and whether the database policy actually blocks cross-tenant access.
Triage in the First Hour
1. Freeze risky changes.
- Pause deployments from Cursor or any auto-generated branch.
- Stop marketing traffic if the leak is active and customer data exposure is ongoing.
- If needed, disable write access to the affected tables before you make it worse.
2. Confirm scope.
- Check which tables are exposed.
- Identify whether the leak affects all users or only certain roles like admins, operators, or contractors.
- Look for evidence in logs of cross-account reads.
3. Inspect auth context in production.
- Verify how user identity reaches your API route or server action.
- Check whether tenant ID, org ID, or account ID is derived from trusted session state or from request body fields.
- Confirm whether service-role credentials are being used where they should not be.
4. Review database rules first.
- Open row-level security policies, access rules, triggers, and views.
- Look for broad allow statements like "select all authenticated users."
- Check whether update and select policies differ in dangerous ways.
5. Check recent deploys and generated code.
- Review the last 3 commits from Cursor-generated changes.
- Search for direct DB queries bypassing policy checks.
- Look for "temporary" debug code that was never removed.
6. Inspect logs and monitoring.
- Review app logs for requests returning other tenants' IDs or email addresses.
- Check database audit logs if available.
- Look for spikes in read volume or unusual access patterns.
7. Validate external surface area.
- Test public pages, authenticated dashboards, webhooks, and automation endpoints separately.
- Check any background jobs that read customer records and send emails or sync data.
8. Preserve evidence.
- Export relevant logs before rotating them.
- Record affected table names, timestamps, and deployment hashes.
-- Quick sanity check pattern -- Replace table_name and tenant_id with your own schema fields SELECT * FROM table_name WHERE tenant_id != 'current_tenant_id' LIMIT 20;
Root Causes
1. Missing row-level security on sensitive tables.
- Confirmation: tables contain customer records but have no RLS enabled or no restrictive policies attached.
- What I look for: any table storing contacts, bookings, automations, invoices, messages, notes, or files without tenant filtering.
2. Policies allow too much by default.
- Confirmation: there is an "authenticated users can select" rule with no tenant match condition.
- What I look for: policies that check login status but not ownership, org membership, or role scope.
3. Server-side code uses a privileged key everywhere.
- Confirmation: Next.js route handlers or server actions query the database with admin credentials for normal user requests.
- What I look for: service-role keys in shared helpers that should only be used for cron jobs or backend-only tasks.
4. Tenant ID comes from the client instead of trusted session state.
- Confirmation: request payload includes org_id or account_id and the backend trusts it directly.
- What I look for: forms and API calls where a user can swap IDs and access another account's data.
5. Views or joins expose more than intended.
- Confirmation: a view returns columns from related tables without filtering by tenant ownership on every join path.
- What I look for: "convenience" views used by dashboards and automations that flatten multiple entities into one result set.
6. Automation jobs bypass normal app controls.
- Confirmation: background workers pull broad datasets to send emails, create tasks, or sync CRM records.
- What I look for: scheduled jobs using unrestricted queries because they were easier to ship fast.
The Fix Plan
My rule here is simple: fix authorization at the data layer first, then clean up application code second. If you only patch the UI validation in a Next.js app, you will ship the same leak again through a new endpoint next week.
1. Lock down direct access immediately.
- Disable public read paths on sensitive tables until policies are verified.
- If you use Supabase or similar tooling, turn on row-level security where it is missing.
- Remove broad allow-all policies before adding new ones.
2. Define one source of truth for tenancy.
- Pick one field such as `org_id` or `account_id`.
- Make sure every sensitive query filters on that field using trusted session claims or server-side session lookup.
- Do not accept tenancy identifiers from raw request bodies unless they are validated against the authenticated user.
3. Split privileged operations from user operations.
- Keep service-role credentials only in backend-only jobs like migrations, webhook reconciliation, and internal admin scripts.
- For normal dashboard requests, use least-privilege credentials tied to the current user session.
- Move shared DB helpers into two paths: one safe for user traffic and one explicitly privileged.
4. Rewrite unsafe queries defensively.
- Add explicit tenant filters to every select/update/delete touching customer data.
\- Audit joins so related tables cannot leak across accounts through a parent record mismatch. \- Remove any fallback logic that returns "all rows" when filters are missing.
5. Fix automation endpoints separately from UI routes. \- Webhooks should verify signatures before reading any customer record unrelated to the event payload.\n \- Background jobs should process only scoped queues per tenant when possible.\n \- Any bulk sync job should log how many records it touched and which tenant it belonged to.
6. Tighten secrets handling while you are here.\n \- Rotate exposed keys if there is any chance they were logged or committed.\n \- Move environment variables out of local `.env` drift and into production secret storage.\n \- Verify Cloudflare headers, deployment settings, and preview environments do not expose prod secrets.\n 7. Add audit logging around sensitive reads.\n \- Log who accessed what resource type and when.\n \- Avoid logging raw customer content.\n \- Keep logs useful enough to investigate future incidents without creating another privacy problem.\n 8. Ship behind a controlled redeploy.\n \- Deploy to staging first with production-like data shape but sanitized records.\n \- Run targeted checks against known tenant boundaries.\n \- Then push production during a low-risk window with monitoring open beside you.\n
Regression Tests Before Redeploy
Before I let this back into production,\nI want tests that prove tenant isolation at both the API and database layers.\nThis is where founders usually under-test because everything "looks fine" in the UI.\nThat is how leaks survive.\n 1. Authentication tests \-\tUnauthenticated requests return 401 or redirect correctly.\n\-\tAuthenticated users cannot access another user's resources by swapping IDs.\n\-\tExpired sessions fail closed.\n\n2. Authorization tests\n\-\tUser A cannot read User B's records through dashboard pages,\nAPI routes,\nor server actions.\n\-\tAdmin-only paths require explicit admin role checks.\n\-\tService-role paths are inaccessible from browser requests.\n\n3. Database policy tests\n\-\tEach sensitive table has RLS enabled if supported by your stack.\n\-\tSelect,\ninsert,\nupdate,\nand delete policies are tested separately.\n\-\tCross-tenant queries return zero rows,\nnnot partial leaks.\n\n4. Automation tests\n\-\tWebhooks reject invalid signatures.\n\-\tScheduled jobs process only scoped records.\n\-\tEmail sends never include another tenant's contact list by mistake.\n\n5. Negative case checks\n\-\tMissing tenant context fails closed,\nnnot open.\n\-\tMalformed IDs do not trigger broad queries.\n\-\tDebug endpoints are removed before release.\n\n6. Acceptance criteria\n\-\t0 known cross-tenant reads in manual test pass.\n\-\t100 percent of sensitive routes have explicit authorization checks documented in code review notes.\n't\-\tp95 response time stays under 300 ms on key dashboard reads after policy changes,\so you do not trade security for a broken experience.'\н\
Prevention
I would put guardrails around three things: code review,\nauthorization design,\nand observability."\ \n1." Code review guardrails\n\u2013 Never approve generated code touching auth,\ndata access,\nor secrets without human review."\ \u2013 Require reviewers to answer one question:\ndoes this query prove ownership?"\ \u2013 Reject any change that trusts client-supplied account IDs without validation."\ \n2." Security guardrails"\u2013 Use least privilege everywhere."\u2013 Rotate secrets after incidents."\u2013 Keep preview environments isolated from production credentials."\u2013 Set rate limits on auth-sensitive endpoints so attackers cannot brute-force edge cases."\u2010"\u200b" 3." Monitoring guardrails"\u2013 Alert on unusual cross-account reads,"\nbulk exports,"\nand spikes in failed authorization checks."\u2013 Track p95 latency on protected endpoints because heavy policy logic can hurt UX if you never measure it."\u2010"\u200b" 4." UX guardrails"\u2013 Show clear permission errors instead of silent failures."\u2013 Make empty states distinguish between "no data" and "no access."\u2013 Do not expose internal identifiers in URLs unless needed."\u200b" 5." Performance guardrails"\u2013 Index tenant ID plus created_at on hot tables."\u2013 Cache safe public assets at Cloudflare."\u2013 Watch query plans after adding stricter filters so your dashboard does not slow down under load."\u200b"
When to Use Launch Ready
Use Launch Ready when you need me to stop an incident from becoming a reputation problem,\nand you want production-safe deployment work done fast without hiring full-time staff yet."\ This fits best if your product already works,\nbut your domain,email,infrastructure,and security posture are not ready for real customers."\
If your issue includes leaked customer data,"I would ask you to prepare:\" \- repo access," \- hosting access," \- database console access," \- Cloudflare account access," \- current deploy URL," \- list of affected roles," \- sample failing requests," \- last 5 commits," \- any compliance constraints like GDPR or HIPAA-adjacent handling.\"\ If you already know there was exposure,"do not wait for perfection before booking help.""Contain first,"then harden.""That order saves time,"support load,"and avoidable customer churn."
References
- https://roadmap.sh/api-security-best-practices
- https://roadmap.sh/code-review-best-practices
- https://roadmap.sh/qa
- https://supabase.com/docs/guides/database/postgres/row-level-security
- https://nextjs.org/docs/app/building-your-application/authentication
---
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.