How I Would Fix database rules leaking customer data in a Bolt plus Vercel 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 most likely root cause is simple: the...
How I Would Fix database rules leaking customer data in a Bolt plus Vercel 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 most likely root cause is simple: the app is trusting the frontend too much and the database rules are either missing, too broad, or bypassed by an API route that returns more than it should.
The first thing I would inspect is the data access path, not the design. I want to see exactly how Bolt generated the app, what Vercel routes are calling the database, and whether any service role key or privileged token is exposed in environment variables, serverless functions, or client-side code.
Triage in the First Hour
1. Check what data is exposed.
- Open the admin screens that show customer records.
- Confirm which fields are leaking: email, phone, notes, invoices, addresses, tokens, or internal flags.
- Capture 3 to 5 examples so I can trace them back to the source.
2. Inspect Vercel logs and function traces.
- Look for API routes that return full rows instead of a filtered projection.
- Check for repeated 200 responses where there should be 403 or 401.
- Note any routes with unusually large payloads.
3. Review the database rules or policies.
- Find row-level security policies, table permissions, or equivalent access rules.
- Verify whether read access is restricted by user role, org ID, or session claims.
- Look for "allow all" defaults or missing deny rules.
4. Check secrets and environment variables.
- Review Vercel project env vars for service keys, admin tokens, or database credentials.
- Confirm nothing sensitive was copied into a client-exposed variable prefix.
- Rotate anything that may have been exposed.
5. Audit Bolt-generated code paths.
- Identify every place where customer data is fetched.
- Check if Bolt created direct client-to-database calls instead of server-side access control.
- Look for helper functions that bypass normal auth checks.
6. Verify auth behavior in the browser.
- Test logged-out access to admin pages and endpoints.
- Test with a low-privilege account and confirm it cannot see other customers.
- Check whether caching is serving one user's data to another user.
7. Review deployment history.
- Find the last known good deployment on Vercel.
- Compare recent changes to schema migrations, API routes, and env var updates.
- If needed, roll back fast before doing deeper surgery.
## Quick diagnosis for leaked fields in API responses curl -s https://your-app.vercel.app/api/customers | jq '.[0]'
Root Causes
| Likely cause | How to confirm | Business risk | |---|---|---| | Missing row-level security | Database tables return records across users or orgs | Customer data exposure and compliance risk | | Overbroad policy | Policy uses `true`, `authenticated`, or wrong org filter | Internal users see records they should not | | Service key used on client side | Secret appears in browser bundle or network requests reveal privileged access | Full database compromise if abused | | Unsafe API route | Vercel function queries with admin credentials and skips authorization checks | One endpoint leaks everything | | Bad cache behavior | Cached response from one user shows up for another user | Cross-user data leakage and trust loss | | Broken tenant scoping | Queries do not filter by `org_id`, `account_id`, or workspace ID | Users see other customers' records |
1. Missing row-level security
This is common when founders move fast with generated apps. The database works fine in testing because everyone has access, then production exposes every row because no policy blocks it.
I confirm this by checking whether tables have explicit read restrictions and whether anonymous or authenticated roles can select from them without constraints.
2. Overbroad policy
Sometimes there is a policy, but it is too wide. A rule like "authenticated users can read customers" sounds safe until you realize every staff member can see every tenant's records.
I confirm this by reviewing each policy condition against real roles and test accounts. If there is no org boundary in the rule, it is probably wrong.
3. Service key used on client side
If Bolt wired a privileged key into frontend code, the app may be bypassing all database protections. That turns a normal web app into a direct admin console with no guardrails.
I confirm this by searching the repo and built output for secret names and checking whether any privileged credential appears in browser requests.
4. Unsafe API route
Vercel serverless functions often become "god mode" endpoints when rushed. The route may fetch all customer rows using elevated credentials but forget to verify who asked for them.
I confirm this by tracing each endpoint from request to query and checking whether authorization happens before the database call.
5. Bad cache behavior
Caching can leak data if a response varies by user but the cache key does not. This happens with CDN caching, edge caching, or custom memoization inside serverless functions.
I confirm this by comparing responses across two different accounts and checking cache headers plus any shared storage layer.
6. Broken tenant scoping
Many internal apps need org-level isolation even if they only have "staff" users today. If every query lacks `org_id`, then any future expansion becomes dangerous immediately.
I confirm this by reviewing schema design and query filters for every list view, export flow, search endpoint, and detail page.
The Fix Plan
My goal is to stop exposure first, then repair access control without breaking legitimate admin workflows.
1. Freeze risky changes.
- Pause deployments until I know where leakage occurs.
- Disable any export feature if it returns bulk customer records.
- If exposure is active now, roll back to the last safe build immediately.
2. Remove privileged secrets from the client path.
- Move database admin credentials into server-only environment variables.
- Ensure no service role key ships to the browser bundle.
- Rotate any credential that may have been exposed already.
3. Enforce server-side authorization on every read path.
- Add auth checks before each query in Vercel routes or server actions.
- Require role validation plus tenant scoping for all customer reads.
- Deny by default if identity or org context is missing.
4. Tighten database rules.
- Turn on row-level security where supported.
- Write explicit allow rules for each role and each table used by admins.
- Add deny-by-default behavior so new tables do not inherit unsafe access patterns.
5. Limit returned fields.
- Only select columns needed for each screen.
- Do not return notes, tokens, payment details, or hidden metadata unless required.
- Build separate endpoints for list views versus detail views if needed.
6. Fix cache boundaries.
- Mark personalized responses as non-cacheable unless carefully keyed per user/org.
- Review CDN settings on Vercel and any custom caching logic in code.
- Purge stale cached responses after patching permissions.
7. Add audit logging without logging secrets.
- Log who accessed what record type and when.
- Avoid storing full PII payloads in logs.
- Keep logs useful for incident review without creating another privacy problem.
8. Deploy in a controlled order.
- Apply schema/policy fixes first in staging.
- Run regression tests against staging with real auth contexts.
Then deploy backend fixes before frontend updates so broken UI does not hide unsafe access paths.
A safe fix usually takes one focused sprint if scope stays tight: 1 backend pass, 1 policy pass, 1 QA pass. For most Bolt plus Vercel apps I would expect 4 to 8 hours of hands-on repair time plus another few hours of validation.
Regression Tests Before Redeploy
Before I ship this again, I want proof that unauthorized users cannot see protected data even when they try normal app flows hard enough to break them.
- Logged-out user cannot access any customer endpoint
- Expected: 401 or redirect to sign-in
- Low-privilege staff user can only see their own org
- Expected: no cross-org rows returned
- Admin user sees only allowed fields
- Expected: sensitive columns are absent unless explicitly required
- Direct API request without UI context fails
- Expected: 403 from protected routes
- Cached pages do not leak between sessions
- Expected: different users get different results where appropriate
- Export endpoint respects same authorization as list view
- Expected: no bulk dump beyond scope
- Database policy tests pass
- Expected: allowed reads succeed; disallowed reads fail
- Secret scan passes before deploy
- Expected: no privileged keys in client bundle or repo history
Acceptance criteria I would use:
- Zero unauthorized customer records visible from test accounts outside their org.
- All protected endpoints return correct auth failures within p95 under 200 ms on staging for normal requests and under 300 ms with auth checks enabled.
- No secrets found in frontend code bundles or public logs.
- At least one negative test per sensitive endpoint passes before release.
Prevention
This kind of leak comes back when teams ship faster than they review access control. I would put guardrails around security reviews so the next change does not reopen the same hole.
- Code review gate
- Any change touching customer reads needs a second pair of eyes focused on auth logic first and UI second.
- Reviewers should check behavior changes, query scope, secrets handling, and failure modes.
- Security checklist
- Verify authentication before authorization-sensitive reads Verify tenant scoping on every query Verify least privilege on env vars and DB roles Verify logging excludes PII and tokens
- Monitoring
- Alert on spikes in customer list exports Alert on unexpected anonymous requests to protected routes Alert on repeated forbidden responses from staff accounts that may indicate broken permissions
- UX guardrails
- Show clear permission errors instead of silent empty states when access is denied Make role boundaries visible inside the admin UI so staff know what they are allowed to do Add confirmation steps before bulk export actions
- Performance guardrails
- Keep queries narrow with indexed filters like `org_id` and `created_at` Watch p95 latency after adding auth checks so security does not create slow admin screens Avoid overfetching large customer payloads into lists
When to Use Launch Ready
Use Launch Ready when you need me to stop a risky release from turning into support tickets or a privacy incident fast.
It includes DNS setup, redirects, subdomains if needed beneath your main launch path under Cloudflare protection setup SSL production deployment environment variables secrets uptime monitoring caching DDoS protection SPF DKIM DMARC alignment plus a handover checklist so your team knows what changed and why it matters.
What you should prepare before booking: - Vercel project access - Database access with permission to review policies - Repo link from Bolt export or GitHub sync - Current domain registrar access if DNS changes are needed - A list of staff roles plus who should see which records - Any existing incident notes screenshots or error reports
If your issue includes leaked customer data I would start with containment then move into Launch Ready once access control is verified because shipping infrastructure polish before fixing permissions just makes the blast radius bigger.
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://vercel.com/docs/security/overview
---
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.