fixes / launch-ready

How I Would Fix database rules leaking customer data in a Cursor-built Next.js internal admin app Using Launch Ready.

If an internal admin app is leaking customer data, I treat it as a production security incident, not a UI bug. The symptom is usually simple: a staff user...

How I Would Fix database rules leaking customer data in a Cursor-built Next.js internal admin app Using Launch Ready

If an internal admin app is leaking customer data, I treat it as a production security incident, not a UI bug. The symptom is usually simple: a staff user opens one record and can see far more than they should, or the app shows rows from other tenants, deleted records, or private fields like emails, notes, invoices, or API keys.

The most likely root cause is broken authorization at the data layer, not just the page layer. In a Cursor-built Next.js app, I would first inspect the database access path: server actions, API routes, ORM queries, and any row-level security or filtering logic that was assumed but never enforced.

Triage in the First Hour

1. Confirm scope in production.

  • Check which customer fields are exposed.
  • Check whether exposure affects one role, all roles, or all users.
  • Check whether this is read-only leakage or if updates and deletes are also possible.

2. Freeze risky changes.

  • Pause deployments from Cursor or CI until the issue is contained.
  • Disable any recent feature flag tied to admin search, export, impersonation, or bulk actions.
  • If needed, temporarily restrict the app to a smaller trusted group.

3. Inspect auth and access logs.

  • Review recent sign-ins for the admin accounts.
  • Look for unusual IPs, repeated requests, high-volume exports, or access outside normal business hours.
  • Check whether leaked data was fetched by a server route or directly by client-side calls.

4. Review the exact query path.

  • Open the Next.js route handler or server action that loads customer records.
  • Inspect ORM queries for missing `where` clauses on tenant_id, org_id, owner_id, or role constraints.
  • Check whether any joins or includes pull private relations by default.

5. Audit database policy enforcement.

  • Confirm whether row-level security exists and is enabled on the affected tables.
  • Verify policies are actually attached to the right roles and connections.
  • Check whether an admin service key bypasses policies in places it should not.

6. Inspect environment and deployment settings.

  • Review production env vars for service-role keys exposed to client bundles.
  • Verify secrets are only used server-side.
  • Confirm Cloudflare and deployment logs do not expose query strings with sensitive identifiers.

7. Compare working and broken builds.

  • Diff the last known safe commit against the current build.
  • Look for Cursor-generated refactors that moved logic from server to client.
  • Review recent changes to caching, ISR, middleware, and data fetching.

8. Capture evidence before patching.

  • Save screenshots of leaked views.
  • Export relevant logs and query traces.
  • Record affected tables and routes so I can verify the fix later.
## Quick checks I would run
grep -R "service_role\|adminKey\|supabase.*anon\|prisma.*findMany\|select:" app src

Root Causes

| Likely cause | What it looks like | How I confirm it | |---|---|---| | Missing tenant filter in queries | Admin sees other customers' rows | Inspect route handlers and ORM calls for absent `orgId` or `tenantId` filters | | Row-level security not enabled | Direct DB reads return too much data | Check table policies and test with a low-privilege account | | Service key used in client code | Browser can fetch privileged data | Search bundled code and network calls for secret-backed endpoints | | Overbroad API response shape | Endpoint returns full customer objects | Inspect JSON payloads and compare against required fields | | Broken role checks in middleware | Non-admin users reach admin screens | Test auth gates on page load and server actions | | Cache serving stale private data | One user's record appears in another session | Review CDN, ISR, React cache usage, and per-user cache keys |

The most dangerous pattern is assuming UI protection equals data protection. If the database will return private rows to a privileged key without strict filtering, then one bad query can expose every customer record behind that table.

The Fix Plan

First, I would stop the leak at the source by moving authorization into the server-side data path. That means every query must be scoped by tenant or organization before it reaches the browser.

Second, I would reduce blast radius by switching responses from "full object" to "minimum necessary fields." Internal apps often over-share because it feels faster during build-out. That trade-off becomes expensive when support tickets start asking why one staff member can see another customer's billing notes.

Third, I would enforce access at two layers:

  • Application layer: verify role and tenant before any fetch runs.
  • Database layer: enable row-level security or equivalent policy enforcement so a bad query cannot bypass controls silently.

Fourth, I would check all admin-only routes for unsafe assumptions:

  • Server actions that trust hidden form fields
  • API routes that accept arbitrary IDs
  • Bulk export endpoints
  • Search endpoints returning too much metadata
  • Impersonation features that do not log activity

Fifth, I would patch secrets handling immediately if any privileged key reached the browser bundle. In Next.js this often happens through sloppy environment usage in client components or shared utility files. A service-role credential in client code is an emergency because it turns a normal bug into full-data exposure.

Sixth, I would add explicit deny-by-default behavior. If a request does not carry a valid org context or role claim, it should fail closed with a 403 instead of trying to be helpful.

A safe repair sequence looks like this:

1. Patch server-side authorization first. 2. Add database policy enforcement second. 3. Narrow response payloads third. 4. Remove any client-side privileged access fourth. 5. Add tests fifth. 6. Redeploy only after verification on staging mirrors production permissions.

If there is active leakage right now and you cannot verify containment quickly enough, I would rotate affected secrets immediately and temporarily block high-risk endpoints like exports and bulk search until confidence is restored.

Regression Tests Before Redeploy

I will not ship this kind of fix without tests that prove both safety and usability.

Acceptance criteria:

  • A user can only see customers within their own org or assigned scope.
  • A non-admin cannot access admin-only routes even if they guess URLs directly.
  • No API response includes fields outside the approved allowlist.
  • Export endpoints return only permitted records and columns.
  • Cached pages do not show another user's private data after logout/login switching.

Test plan: 1. Role-based access tests

  • Admin user can view allowed records.
  • Staff user receives 403 on restricted routes.
  • Anonymous user cannot reach protected APIs.

2. Tenant isolation tests

  • User from Org A cannot fetch Org B's customers by ID search or list view.
  • Cross-org IDs return empty results or 404/403 depending on design.

3. Payload tests

  • Response JSON contains only approved fields like name and status if that is all the UI needs.
  • Private fields like notes, billing details, tokens, or internal flags are excluded unless explicitly required.

4. Cache tests

  • Log out as one user and log in as another on the same browser session.
  • Confirm no stale customer data appears from SSR cache, browser cache, or CDN cache.

5. Negative-path tests

  • Invalid org context fails closed.
  • Missing auth token fails closed.
  • Expired session redirects correctly without exposing partial data.

6. Manual exploratory checks

  • Search by partial email fragments if search exists.
  • Open detail pages directly by URL guesswork.
  • Try bulk actions with mixed authorized and unauthorized IDs.

I want at least 90 percent coverage on authorization-critical paths before redeploying this class of fix. More important than raw coverage is making sure every route that touches customer data has an explicit test around identity and scope.

Prevention

The best prevention is architectural discipline plus boring operational controls.

I would put these guardrails in place:

  • Code review rule: no customer-data query ships without an explicit scope filter reviewable in diff form.
  • Security rule: no service-role credentials in client components or shared browser utilities ever again.
  • Database rule: row-level security stays enabled on sensitive tables by default unless there is a documented exception.
  • Logging rule: log access decisions without logging sensitive payloads themselves.
  • Monitoring rule: alert on unusual export volume, repeated 403s from one account, cross-org lookup failures, and spikes in admin reads after hours.

For UX safety inside internal tools:

  • Show clear permission errors instead of silent failures that encourage workarounds.
  • Make scoped filters visible so staff know what they are seeing belongs to their team only.
  • Add confirmation steps for exports because export abuse is how many internal leaks become external incidents.

For performance safety:

  • Avoid fixing security by adding expensive per-request joins everywhere without measuring p95 latency first.
  • Use targeted indexes on tenant_id plus commonly filtered columns so authorization does not create slow dashboards at scale.
  • Watch p95 response time after adding stricter filters; if it jumps above 300 ms on core admin views, profile query plans before shipping more logic into middleware.

For AI-built codebases specifically:

  • Treat Cursor output as untrusted until reviewed line by line for auth assumptions.
  • Require one human pass on every route handler touching sensitive records.
  • Keep a small red-team checklist for prompt-influenced changes:

1. Can this change widen access? 2. Can it leak secrets into logs? 3. Can cached output outlive its permission context?

When to Use Launch Ready

I use Launch Ready when the issue is bigger than a quick patch but smaller than a full rebuild.

This sprint fits best when you already have:

  • A working Next.js app built in Cursor
  • Access to GitHub/Git provider
  • Production hosting access
  • Database console access
  • Cloudflare access if your domain sits there
  • A short list of who should see what data

What I need from you before starting: 1. Repo access with current branch history intact 2. Production error logs or monitoring screenshots 3. Database schema or migration files 4. A list of roles such as owner/admin/staff/support 5. The exact pages where leakage happens 6. Any recent Cursor prompts or generated files if you have them

My recommendation: do not keep iterating blindly inside Cursor while customer data is exposed. Stop development noise first, fix authorization at the server/database layer second through Launch Ready style triage if needed quickly then redeploy with tests third once verified safe.

References

1. Roadmap.sh Cyber Security Best Practices: https://roadmap.sh/cyber-security 2. Roadmap.sh API Security Best Practices: https://roadmap.sh/api-security-best-practices 3. Roadmap.sh Code Review Best Practices: https://roadmap.sh/code-review-best-practices 4. Next.js Security Docs: https://nextjs.org/docs/app/building-your-application/authentication 5. OWASP Authorization Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.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.*

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.