How I Would Fix database rules leaking customer data in a Bolt plus Vercel AI-built SaaS app Using Launch Ready.
If customer records are showing up in the wrong account, or a user can query data they should never see, I treat that as a production security incident,...
Opening
If customer records are showing up in the wrong account, or a user can query data they should never see, I treat that as a production security incident, not a bug. In a Bolt plus Vercel AI-built SaaS app, the most likely root cause is bad database authorization logic: missing row-level rules, weak tenant scoping, or server routes that trust client-supplied IDs.
The first thing I would inspect is the exact path from browser to database. I want to know whether the leak comes from a direct client query, a Vercel API route, or an AI-generated backend helper that skipped auth checks and returned too much data.
Triage in the First Hour
1. Freeze risky changes.
- Pause deployments in Vercel.
- Stop any Bolt-generated auto-fixes until the leak path is understood.
- If the issue is active, temporarily disable the affected endpoint or feature flag.
2. Confirm blast radius.
- Check which tables, tenants, and user roles are affected.
- Look at recent support tickets, screenshots, and error reports.
- Estimate exposure in business terms: number of customers impacted, records exposed, and whether PII or billing data was visible.
3. Inspect Vercel logs and function traces.
- Find the route returning customer data.
- Check request headers, session claims, and query parameters.
- Verify whether the handler is using authenticated user context or trusting `customerId`, `orgId`, or `tenantId` from the client.
4. Review database access rules.
- Inspect row-level security policies if you use Postgres with RLS.
- Check whether policies exist on every sensitive table.
- Look for permissive fallback rules like "allow all authenticated users."
5. Audit the latest Bolt changes.
- Review generated code for query builders, server actions, and admin helpers.
- Look for direct table reads without ownership checks.
- Search for hardcoded service-role keys or admin SDK usage in shared code paths.
6. Check secrets and environment variables in Vercel.
- Confirm no production service key is exposed to browser code.
- Verify preview and production env vars are separated correctly.
- Make sure secrets were not copied into a client bundle by mistake.
7. Validate auth session flow end to end.
- Sign in as two different users from different tenants.
- Compare what each user can see in dashboard screens and API responses.
- Confirm that cached responses are not being served across accounts.
8. Capture evidence before changing anything else.
- Save logs, policy snapshots, and failing requests.
- Note timestamps so you can correlate with deployment history.
## Quick checks I would run vercel logs <project-name> --since 24h psql "$DATABASE_URL" -c "\d+ customer_records" psql "$DATABASE_URL" -c "select * from pg_policies where tablename = 'customer_records';"
Root Causes
| Likely cause | What it looks like | How I confirm it | |---|---|---| | Missing row-level security | Any authenticated user can read rows from another tenant | Check table policies and test with two separate accounts | | Wrong tenant filter in queries | Queries use `user.id` when they should use `org_id`, or forget the filter entirely | Inspect server routes and compare SQL/query builder conditions | | Client-trusted identifiers | Frontend sends `tenantId` or `customerId` and backend uses it without verifying ownership | Review request payloads and auth middleware | | Service role key used too broadly | Admin credentials are used in routes that serve normal users | Search codebase for privileged DB clients outside protected admin paths | | Caching leaks between users | One user's response is cached and reused by another user | Check Vercel caching headers, ISR behavior, edge caching, and shared keys | | AI-generated shortcut logic | Bolt created "quick" data access code that skipped auth checks to make the app work fast | Diff recent generated files and inspect any helper functions around data fetching |
The one I see most often in AI-built SaaS apps is this: the app works during testing because everything runs under one developer account, then leaks data once real multi-tenant users sign in. That is not just a bug. It becomes support load, trust damage, refund risk, and possible compliance exposure.
The Fix Plan
My approach is to repair this safely in layers: stop the leak first, then make it hard to reintroduce.
1. Lock down access at the database layer first.
- Turn on row-level security for every customer-facing table that stores tenant data.
- Write deny-by-default policies.
- Allow reads only when the row belongs to the authenticated tenant or user.
2. Move authorization into server-side code only where needed.
- Client code should never decide what rows can be returned.
- Server routes should derive tenant identity from session claims or verified tokens only.
- Remove any reliance on client-provided `tenantId` unless it is cross-checked against session state.
3. Split public and privileged data paths.
- Keep admin queries separate from normal user queries.
- Use a dedicated admin route with strict access control for support staff only if absolutely necessary.
- Never reuse a service-role key inside general app routes.
4. Remove unsafe caching on personalized responses.
- Disable shared caching for account-specific API responses unless cache keys include tenant identity safely.
- Make sure response headers do not allow one user's data to be reused by another user through CDN or edge caching.
5. Patch all affected queries together.
- Do not fix one screen while leaving another screen exposed to the same table.
- Search for every read path into the affected tables: dashboard pages, exports, search endpoints, webhooks, admin views, scheduled jobs.
6. Add defensive logging without leaking sensitive data.
- Log auth decisions and denied access attempts.
- Do not log full personal records or secrets into application logs.
- Include request IDs so you can trace future incidents quickly.
7. Deploy as a controlled release.
- Ship behind a feature flag if possible.
- Test with one internal tenant first if your setup allows it.
- Watch error rates and access-denied counts immediately after rollout.
A simple rule I follow here: if the database can enforce ownership safely, let it do that work first. Application code should add checks too, but it should not be your only line of defense.
Regression Tests Before Redeploy
Before I let this back into production, I want proof that it cannot leak again under normal use or obvious misuse.
1. Cross-tenant access test
- User A cannot read User B's records through UI or API calls.
- Acceptance criteria: 0 unauthorized rows returned across 10 repeated attempts.
2. Direct API tampering test
- Change `customerId`, `orgId`, or similar identifiers in requests from an authenticated session.
- Acceptance criteria: request returns 403 or empty result set; no record disclosure.
3. Privileged route isolation test
- Confirm admin-only endpoints reject normal users consistently.
- Acceptance criteria: non-admins receive 403 on all protected routes.
4. Cache isolation test ```http Cache-Control: private, no-store ```
5. Session expiry test
- Expired sessions must not continue returning data after logout or token expiry.
6. Empty state and error state check
- Users should see clear "no access" or "no records" states instead of stale data from another account.
7. Smoke test on deployment preview
- Verify login flow,
- verify dashboard load,
- verify one create/read/update/delete path,
- verify no cross-account bleed in network responses.
8. Security review gate
- Review database policies,
- inspect recent diffs,
- confirm no secrets moved into client-side bundles,
- confirm logs do not expose PII.
I would also keep one manual acceptance criterion very simple: two fresh accounts created minutes apart must never see each other's dashboards, exports, notifications, invoices, or search results.
Prevention
To stop this class of issue coming back, I would add guardrails at four levels.
1. Database guardrails
- Deny by default on sensitive tables,
- require explicit policies per table,
- review policy drift every time schema changes.
2. Code review guardrails
- Any change touching auth or queries gets senior review,
- reviewers check behavior first,
- no merge if tests do not cover multi-tenant boundaries.
3. Monitoring guardrails
- Alert on spikes in 403s,
- alert on unusual broad queries,
- track p95 API latency so added auth logic does not slow onboarding past 300 ms on core reads.
4. UX guardrails
- Show clear loading states while permissions resolve,
- show honest empty states instead of generic failures,
- avoid UI patterns that encourage users to guess other tenants' IDs.
For performance safety, I would also watch query plans after adding stricter filters. A bad fix can create slow dashboards if indexes do not match tenant-scoped lookups; that turns a security patch into a conversion problem because pages start timing out at p95 over 800 ms.
When to Use Launch Ready
Launch Ready fits when you have fixed the leak but still need to get the product back into a safe launch state fast.
I would recommend Launch Ready when:
- your fix is done but deployment still feels risky,
- you need clean DNS and SSL before re-opening signups,
- you want monitoring in place before traffic returns,
- you need someone senior to verify secrets were moved correctly out of Bolt-generated code paths.
What you should prepare: 1. Vercel project access with deploy permissions. 2. Database console access for policy updates if applicable. 3. Domain registrar access for DNS changes. 4. A list of current env vars and which ones are production-only. 5. One internal tester account plus one standard customer account for validation.
My opinionated take: do not keep iterating inside Bolt while production is leaking data. Fix authorization first with a narrow scope change , then deploy through a controlled handover process so you reduce downtime risk instead of multiplying it.
Delivery Map
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 .Vercel Environment Variables docs: https://vercel.com/docs/environment-variables
---
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.