How I Would Fix database rules leaking customer data in a Cursor-built Next.js automation-heavy service business Using Launch Ready.
If customer records are leaking through database rules, I treat it as a production security incident, not a bug. In a Cursor-built Next.js...
Opening
If customer records are leaking through database rules, I treat it as a production security incident, not a bug. In a Cursor-built Next.js automation-heavy service business, the usual symptom is simple: one user can see another user's jobs, invoices, messages, or workflow history.
The most likely root cause is weak row-level access control in the database layer, often made worse by AI-generated code that assumes the frontend will "filter" data correctly. The first thing I would inspect is the exact query path from the Next.js server action or API route to the database policy layer, because that is where cross-tenant data leakage usually starts.
I would use that sprint to lock down DNS, redirects, subdomains, Cloudflare, SSL, caching, DDoS protection, SPF/DKIM/DMARC, production deployment, environment variables, secrets, uptime monitoring, and a handover checklist so the same issue does not reappear during launch.
Triage in the First Hour
1. Check whether this is active leakage or historical exposure.
- Open recent support tickets and user reports.
- Confirm which customer objects were exposed: contacts, automations, billing data, files, or internal notes.
- Timebox this to 15 minutes.
2. Freeze risky writes if needed.
- Pause background jobs that sync or fan out customer data.
- Disable any admin automation that can bulk read records.
- If exposure is ongoing, I would prefer a short controlled pause over guessing.
3. Inspect auth and tenant boundaries in the app.
- Review Next.js server actions, route handlers, and API endpoints.
- Look for `userId` passed from the client instead of derived from the session.
- Verify every request resolves tenant identity from trusted auth context.
4. Inspect database rules and policies.
- Review RLS policies or equivalent access rules on every sensitive table.
- Check whether `SELECT`, `INSERT`, `UPDATE`, and `DELETE` policies are all present.
- Confirm policies filter by tenant ID and not by display fields like email alone.
5. Check logs and audit trails.
- Look at database logs for broad queries like `select *` on multi-tenant tables.
- Review application logs for missing session claims or null tenant IDs.
- Confirm whether any admin token was used from the browser.
6. Review recent Cursor-generated changes.
- Diff the last 3 to 5 commits touching auth, data fetching, migrations, or policies.
- Search for copied patterns across tables with different tenant requirements.
- AI code often repeats one correct-looking policy into the wrong table.
7. Validate deployment and environment settings.
- Confirm prod uses separate credentials from staging.
- Check env vars for leaked service keys or shared test databases.
- Verify Cloudflare and SSL are active so traffic is not bypassing your intended edge controls.
-- Quick check pattern for multi-tenant tables select id, tenant_id from customers where tenant_id is null limit 20;
8. Screenshot critical screens before changing anything.
- Capture admin dashboards, record detail pages, workflow views, and export screens.
- You want a before/after record if support asks what changed.
Root Causes
| Likely cause | What it looks like | How I confirm it | |---|---|---| | Missing row-level security | Any authenticated user can query all rows | Check table policies and test with two different accounts | | Policy uses wrong claim | Tenant ID exists but policy reads stale email or role field | Decode session claims and compare them to policy predicates | | Client-side filtering only | Data loads broadly then UI hides some rows | Inspect network calls and server queries for unscoped reads | | Shared service key in browser/server mix | One powerful key can read everything | Search env usage and ensure secret-only access stays server-side | | Bad migration copied to new table | One secure table exists but related tables are open | Audit all new tables added by Cursor in recent commits | | Admin bypass too broad | Internal tools can read customer data without guardrails | Review admin routes and ensure least privilege plus audit logging |
The pattern I see most often is this: someone built fast with Cursor, got a working flow in staging, then assumed "authenticated" meant "authorized". It does not. Authentication proves who is there; authorization proves what they can see.
The Fix Plan
1. Stop relying on frontend trust.
- Move all sensitive reads behind server-only code paths in Next.js.
- Derive tenant identity from verified session context only.
- Remove any request body field that sets `tenantId`, `accountId`, or `orgId`.
2. Lock down every sensitive table with explicit access rules.
- Add row-level access control per table if your database supports it.
- Write separate policies for read and write operations.
- Include deny-by-default behavior where possible.
3. Normalize tenant scoping everywhere.
- Ensure each customer-owned record has one canonical tenant key.
- Backfill missing tenant IDs before enforcing strict rules.
- Add foreign keys so orphaned rows do not float outside ownership boundaries.
4. Remove broad queries from automation paths.
- Replace unfiltered list fetches with scoped lookups by tenant plus status plus date range when needed.
- Avoid returning entire datasets to power dashboards "for convenience".
- Use pagination everywhere.
5. Split privileged operations from normal app traffic.
- Put admin tasks behind separate routes or services with tighter access checks.
- Require stronger authentication for exports and bulk actions.
- Log every privileged access event with actor ID and target scope.
6. Rotate secrets if any exposure touched credentials.
- Rotate database passwords if connection strings were exposed anywhere risky.
- Rotate API keys used by automations if they were accessible from leaked records or logs.
- Keep secrets only in environment variables or secret managers; never in client bundles.
7. Patch deployment safety at the same time with Launch Ready scope.
- Verify DNS points to the correct production host only after policy fixes land.
- Confirm SSL is valid end-to-end through Cloudflare and origin certs if used correctly.
- Turn on uptime monitoring so you catch auth failures after redeploy instead of hearing about them from users.
My rule here is simple: fix authorization at the data layer first, then clean up app code second. If you reverse that order, you get a prettier app that still leaks data.
Regression Tests Before Redeploy
I would not redeploy until these pass:
1. Tenant isolation tests
- User A cannot read User B's records through any endpoint or server action.
- User A cannot update or delete User B's records either directly or indirectly through an automation step.
2. Role-based tests
- Normal users see only their own workspace data.
- Admins can access only explicitly allowed scopes with audit logs enabled.
3. Negative tests
- Requests without a session fail closed with 401 or 403 as appropriate.
- Requests with forged tenant IDs are ignored or rejected.
4. Query safety checks
- No endpoint returns unbounded customer lists in production paths.
- Pagination defaults exist and cap large responses.
5. Data exposure checks
- Export CSVs contain only authorized rows for that actor's scope.
- Sensitive fields like tokens, internal notes, and payment details are excluded unless explicitly required.
6. Edge-case QA
- Empty tenants do not crash dashboards.
- Deleted users do not resurrect old permissions through cached sessions.
- Background jobs respect current authorization rules after retries.
7. Acceptance criteria
- Zero cross-tenant reads across a minimum of 20 test cases per major table。
- 100 percent of sensitive tables have explicit access rules reviewed before merge。
- No critical auth/security errors in logs for 24 hours after deploy。
- p95 page load remains under 2 seconds on core dashboard pages after fixes。
If you want one practical gate before shipping: run two browser sessions side by side with two different tenants and prove there is no shared object visibility anywhere in navigation flow, exports, or background sync results.
Prevention
I would put four guardrails in place so this does not come back:
1. Security-focused code review
- Every change touching queries, server actions,
or migrations gets reviewed for behavior first, style second。
- I would block merges that add new tables without explicit ownership rules。
2. Database observability
- Alert on suspicious broad reads,
policy denials, and repeated cross-tenant lookup failures。
- Watch p95 latency too,
because broken policies sometimes create expensive retry storms。
3. Safer AI-assisted development
- Treat Cursor output as draft code,
not trusted logic。
- Add a checklist for prompt-injected comments,
copied policies, and missing deny paths。
- Require human review on anything involving auth,
billing, or customer data。
4. Deployment hygiene
- Keep staging isolated from production credentials。
- Use Cloudflare protections,
strict CORS, and least privilege secrets handling。
- Monitor uptime plus error rate so you catch broken auth flows within minutes。
A good target here is boring reliability: 99.9 percent uptime, under 300 ms p95 API latency on common reads, and zero known cross-tenant exposure paths before launch.
When to Use Launch Ready
Use Launch Ready when you already have a working product but need it made safe enough to ship without betting your business on guesswork. If your Next.js app has domain issues, email deliverability problems, broken SSL, messy environment variables, or no real monitoring while handling customer data, I would start here before adding more features.
What I need from you before kickoff:
- Access to GitHub or your repo export
- Database admin access or a staging clone
- Hosting provider access
- Cloudflare access if DNS sits there
- A short list of exposed tables or workflows
- Any support screenshots showing what leaked
The sprint outcome should be clear: your domain resolves correctly, email works reliably through SPF/DKIM/DMARC, deployment is clean, secrets are out of reach of client-side code, and monitoring tells us fast if something breaks again।
For an automation-heavy service business, that usually saves days of support churn and prevents lost trust during launch week。
References
- https://roadmap.sh/cyber-security
- https://roadmap.sh/api-security-best-practices
- https://roadmap.sh/code-review-best-practices
- 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.