How I Would Fix database rules leaking customer data in a Cursor-built Next.js AI-built SaaS app Using Launch Ready.
If customer records are showing up in the wrong account, the symptom is usually not 'the database is broken.' It is usually an authorization gap somewhere...
How I Would Fix database rules leaking customer data in a Cursor-built Next.js AI-built SaaS app Using Launch Ready
If customer records are showing up in the wrong account, the symptom is usually not "the database is broken." It is usually an authorization gap somewhere between the Next.js app, the API route, and the database rules.
The most likely root cause is one of these: a missing tenant filter, a misconfigured row-level security policy, or a server action that trusts client input too much. The first thing I would inspect is the exact path from browser request to database query, because leaked data almost always comes from one bad assumption in that chain.
If the leak is happening around launch or after a rushed Cursor build, I would treat it as both a security issue and a shipping issue.
Triage in the First Hour
1. Confirm scope.
- Which tables leaked?
- Which user roles saw data they should not see?
- Was it one tenant, multiple tenants, or admin-only data?
2. Freeze risky paths.
- Temporarily disable exports, admin views, and any bulk list endpoints.
- If needed, put the affected feature behind a maintenance flag.
3. Check recent deploys.
- Review Vercel, Netlify, or Cloudflare deploy history.
- Look for the last Cursor-generated commit that touched auth, data fetching, or schema files.
4. Inspect logs.
- API route logs
- Server action logs
- Database audit logs
- Auth provider logs
- Error monitoring like Sentry
5. Verify identity and tenancy mapping.
- Confirm session user ID
- Confirm tenant ID on every request
- Compare what the frontend sends vs what the backend trusts
6. Review database rules.
- Row-level security policies
- Grants on tables and views
- Service role usage
- Any direct client access to sensitive tables
7. Check builds and environment variables.
- Are production env vars different from staging?
- Is a service role key exposed in client code?
- Did someone accidentally ship `.env.local` assumptions into production?
8. Reproduce safely with test accounts.
- Use two separate users in two separate tenants.
- Confirm whether one account can read another account's records without special privileges.
9. Capture evidence before changing anything.
- Save queries
- Save screenshots
- Save policy definitions
- Save request payloads
10. Decide if this is a hotfix or rollback.
- If customer data is actively exposed, rollback first.
- If exposure is limited and you understand the path, patch first with tight monitoring.
-- Quick checks I would run against Postgres with RLS enabled
select schemaname, tablename, rowsecurity
from pg_tables t
join pg_class c on c.relname = t.tablename
join pg_namespace n on n.nspname = t.schemaname;
select *
from pg_policies
where tablename in ('customers', 'orders', 'messages');Root Causes
1. Missing tenant filter in queries.
- What happens: the app queries `select * from customers` instead of `where tenant_id = currentTenantId`.
- How to confirm: inspect server actions and API routes for list/detail endpoints without tenant scoping.
2. Row-level security exists but is not enforced.
- What happens: policies are written but RLS is off, or the app uses a privileged key that bypasses them.
- How to confirm: check table settings and see whether requests use anon/public access or service credentials.
3. Client-side trust of user-supplied IDs.
- What happens: the frontend sends `tenantId`, `customerId`, or `accountId`, and the backend accepts it without verifying ownership.
- How to confirm: search for direct use of request body IDs in queries without checking session claims.
4. Shared service role key used too broadly.
- What happens: one server function uses admin privileges for everything instead of only narrow internal jobs.
- How to confirm: trace env vars and find where elevated keys are imported into routes meant for end users.
5. Misconfigured views or joins.
- What happens: a view joins customer tables across tenants or omits a join condition entirely.
- How to confirm: review SQL views and ORM query builders for missing `tenant_id` constraints on every joined table.
6. Cache leakage or ISR mistakes.
- What happens: cached pages or API responses are reused across users because cache keys do not vary by auth context.
- How to confirm: inspect CDN headers, Next.js caching behavior, and any static generation around authenticated pages.
The Fix Plan
I would fix this in layers so we stop the leak first and then harden the whole path.
1. Contain exposure immediately.
- Disable any endpoint that returns broad customer lists if it cannot be trusted yet.
- Clear CDN caches if authenticated content was cached incorrectly.
- Rotate any keys that may have been exposed in client bundles or logs.
2. Make tenant ownership server-side only.
- Never accept `tenantId` from the browser as proof of access.
- Derive tenant membership from session claims or a server lookup tied to authenticated user ID.
3. Turn on strict database protection.
- Enable row-level security on sensitive tables.
- Write policies that allow reads only when `tenant_id` matches the authenticated tenant context.
- Remove broad grants from public roles where possible.
4. Reduce privilege on application paths. The default rule should be least privilege. Only background jobs or admin-only maintenance tasks should use elevated credentials.
5. Audit all list and detail endpoints. I would search every route handler, server action, ORM query, and direct SQL statement that touches customer data.
6. Fix caching boundaries in Next.js. Authenticated responses should not be cached like public marketing pages unless cache keys are intentionally partitioned by user or tenant.
7. Add explicit authorization checks before every sensitive fetch. Each request should verify:
- who is calling,
- which tenant they belong to,
- which resource they want,
before any database read happens.
8. Patch then test before expanding scope. I would fix one path end-to-end first, verify no leakage remains there, then repeat across similar endpoints instead of rewriting everything at once.
9. Log denials without leaking data into logs. Security logs should record denied access attempts and resource IDs where appropriate, but never dump full customer records into application logs.
10. Ship with rollback ready. If there is any uncertainty after testing, I would deploy behind a feature flag so we can revert quickly without breaking all customers.
Regression Tests Before Redeploy
I would not redeploy until these pass:
1. Cross-tenant read test
- User A cannot view User B's records through UI or API.
Acceptance criteria: 0 successful unauthorized reads across 2 tenants and 10 repeated attempts.
2. Direct endpoint test
- Hitting list and detail endpoints with valid auth but wrong resource ownership returns 403 or empty results as designed.
3. Role separation test - Regular users cannot access admin-only tables or exports even if they guess URLs or IDs.
4. Cache isolation test Authenticated pages do not show another user's content after refresh or back navigation.
5. RLS enforcement test Sensitive tables reject unauthorized reads even when queried outside the app layer.
6. Negative input test Malformed IDs, empty tenant values, and tampered payloads fail safely without exposing schema details.
7. Observability check Security events appear in logs with enough context to investigate but no PII dump.
8. Manual QA with two accounts I would use two real test users across desktop and mobile flows to confirm no cross-account bleed anywhere visible in UI state.
9. Deployment smoke test After deploy, validate login flow, dashboard load time under 2 seconds p95 for basic pages, and no auth-related errors in monitoring for 30 minutes.
Prevention
I would stop this from coming back with guardrails at three levels: code review, security controls, and product design.
- Code review guardrails:
I review every change that touches auth or data access like production code should be reviewed: behavior first, style second. Any query without an explicit ownership check gets blocked until fixed.
- API security guardrails:
Use least privilege keys only where needed. Validate inputs on every route. Lock down CORS so random origins cannot abuse your APIs. Rate limit sensitive endpoints like login, export, invite creation, and search.
- Database guardrails:
Keep RLS enabled on all customer-facing tables by default. Add automated checks that fail CI if new tables do not have policies attached within one sprint of creation.
- Monitoring guardrails:
Alert on unusual spikes in denied requests, repeated cross-tenant lookups, admin-key usage from unexpected routes, and response bodies containing sensitive fields where they should not appear.
- UX guardrails:
If access fails because of permissions, show a clear "you do not have access" state instead of blank screens or partial data that confuse support teams and trigger repeated retries.
- Performance guardrails:
Authorization checks should be cheap enough not to hurt p95 latency badly. If security logic adds slow joins everywhere, I would index `tenant_id`, profile queries, and move expensive checks into cached membership lookups rather than weakening protection.
When to Use Launch Ready
Use Launch Ready when you need me to make this safe fast instead of spending weeks guessing inside a fragile Cursor build.
This sprint fits best if you have:
- an AI-built SaaS app already working,
- unclear production readiness,
- risky auth or database behavior,
- broken deployment hygiene,
- missing monitoring,
- secrets scattered across tools,
- or launch pressure from investors or paid traffic already running.
- DNS setup
- redirects
- subdomains
- Cloudflare setup
- SSL
- caching strategy
- DDoS protection basics
- SPF/DKIM/DMARC email setup
- production deployment
- environment variables cleanup
- secrets handling review
- uptime monitoring
- handover checklist
What I need from you before starting:
- repo access,
- hosting access,
- database access,
- auth provider access,
- current domain registrar access,
- any error screenshots,
- last known good deploy link,
- list of critical user journeys,
- and confirmation of which data must never leak across accounts.
If your main problem is customer data exposure from bad rules or bad query design, I would pair Launch Ready with an authorization audit so we do not just ship faster; we ship safer too.
References
1. roadmap.sh API Security Best Practices https://roadmap.sh/api-security-best-practices
2. roadmap.sh Code Review Best Practices https://roadmap.sh/code-review-best-practices
3. roadmap.sh Cyber Security https://roadmap.sh/cyber-security
4. Next.js Data Fetching and Caching https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating
5. PostgreSQL Row Level Security https://www.postgresql.org/docs/current/ddl-rowsecurity.html
---
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.