9 min read
Vibe coding security: issues we check in AI-generated apps
AI-generated apps can look finished while any signed-in user can read everyone's data. The specific security issues we check before an AI-built app goes in front of customers.
Vibe coding security is mostly about what AI-generated apps leave out: server-side authorization on every record, secrets kept off the client, locked-down database rules, validated input, safe payment webhooks, and rate limits. Review those trust boundaries before launch, because the app can look finished while any logged-in user can read everyone's data.
This is the checklist level of our prototype-to-production work. For the overall hardening order, see how to turn a Lovable or v0 prototype into a production application. For agents specifically, see the limits of building agents only from vibe-code tools. Here we list the concrete issues we look for in AI-built web and mobile apps, drawn from implementation reviews and from building the ImadDhin portal. Nothing here is a claim about breach statistics.
Why AI-generated apps have predictable gaps
AI coding tools are optimized to produce something that works when you try it. Security properties are mostly invisible when you try something. A page that shows your own orders looks identical whether the server checks ownership or simply returns whatever order ID the browser asks for.
The builder also tests as a single user, usually the owner, with full access. Every flow works because nothing is ever denied. When a permission error does appear, the fastest prompt to make it go away is often to loosen a rule, and the tool obliges.
Generated code also inherits patterns from tutorials and starter templates, where keys sit in client configuration and database rules are left open for convenience. Those patterns are fine for a demo and wrong for customers.
What teams often get wrong
- Assuming a hosted platform or backend-as-a-service makes the app secure by default
- Hiding buttons in the interface and treating that as authorization
- Asking the same AI tool whether its code is secure and accepting the answer as a review
- Loosening database rules to fix a permission error instead of fixing the query
- Planning the security review for after launch
The platforms provide good building blocks, but the configuration and the authorization logic are yours. The review has to be done by someone looking for failure, not by the tool that wrote the code.
The checklist
We group the review by trust boundary. Each item is something we have seen missing in generated code often enough to check every time.
Authentication and accounts
- Password reset and email verification flows cannot be replayed or skipped
- OAuth redirect URLs are restricted to known domains
- Roles such as admin come from a server-controlled source, never from a profile field the user can edit
- Sessions expire and sign-out invalidates what it should
Authorization on every record
- Every read and write checks ownership or tenancy on the server or in database rules
- Changing an ID in a request cannot return or modify another user's record
- Admin routes and API endpoints are protected server-side, not just hidden in navigation
- List endpoints filter by the current user or tenant, not only by what the page displays
Database and storage rules
- Row-level security or equivalent rules are enabled on every exposed table or collection
- No rule allows unrestricted reads or writes for convenience
- Create rules validate the shape and ownership of new records
- File storage buckets have their own rules and do not allow public listing
For Supabase projects this is its own topic, covered in Supabase row-level security mistakes in AI-built apps.
Secrets and configuration
- No service-role, admin or payment secret keys in client code or public environment variables
- No secrets in committed files, including history
- Production source maps do not expose server logic or keys
- CORS is restricted and security headers are set
Input, output and AI features
- Server-side validation on every input, not only in the form
- User content rendered as Markdown or HTML is sanitized
- File uploads check type and size and are stored outside the web root
- Features that fetch URLs cannot reach internal network addresses
- Model output is treated as untrusted and cannot trigger actions without checks
- Model provider keys are used only on the server
Payments and entitlements
- Prices and plan IDs are set on the server, never taken from the client
- Webhook signatures are verified before any processing
- Webhook handlers are idempotent, so a retried event does not grant access twice
- Access is granted from the verified payment event, not from reaching a success page
Abuse and cost
- Rate limits on sign-in, sign-up, password reset and AI endpoints
- Quota and credit counters are updated atomically
- Public forms have bot protection proportional to their risk
Dependencies and operations
- Every added package exists, is the intended one, and is maintained
- Debug routes, seed endpoints and test accounts are removed
- Errors shown to users do not include stack traces or queries
- Security-relevant events, such as sign-ins, role changes and admin actions, are logged without secrets or unnecessary personal data
Implementation considerations
Start with a short threat model: what data the app holds, who should see it, what an attacker would want, and which actions cost money. An hour of that focuses the rest of the review.
Then test like an outsider. Create two ordinary accounts and try to read, change and delete each other's data through the interface and directly through the API. Query the database with the public client key while signed out. Search the built client bundle for anything that looks like a key. Replay a payment webhook. Send the same request many times quickly. These tests find more real issues than reading code alone.
Fix in order of blast radius: exposed secrets and open database rules first, because they affect every user at once; then authorization gaps; then payments; then abuse limits and hardening. Rotate any secret that was ever exposed, even briefly, rather than only removing it from the code.
Finally, make the fixes stick. Add the cross-account tests to your automated suite, put rule files under review, and add secret scanning to the repository, so the next AI-generated change cannot quietly reopen the same holes.
Trade-offs
Tight database rules sometimes break features that depended on open access. That breakage is useful: it shows which queries were relying on the hole. Fixing the query is more work than restoring the open rule, and it is the only fix that holds.
A thorough review slows the launch by days, not months, for most prototypes. The alternative is discovering the same issues after customers have trusted the app with their data, when fixes also require notification, rotation and cleanup.
Not every finding justifies the same effort. A prototype used internally with test data needs less than a public app that takes payments. Match the depth of the review to the data and money involved.
Lessons from ImadDhin work
These are code-level observations from the ImadDhin portal's configuration and from codebases we review, not claims about incidents.
The portal's database rules deny access by default, with no catch-all rule. Chat sessions, messages, saved memory and usage records are denied to clients entirely and handled only by server routes. Several public intake collections allow create but not read, update or delete, with validation functions checking the shape of each new record.
The most instructive pattern we see in reviews is an intake rule that accepts any create because a server writes to that collection through the client SDK. Database rules cannot tell a server using the client SDK apart from a browser, so a rule written to let the server in lets everyone in. Treat that pattern as a finding: write from the server with administrative credentials and deny client writes, or add the same shape validation the other collections use.
Usage counters are the second pattern. A counter that reads the current count and then writes the incremented value in a separate step, outside a transaction, lets two concurrent requests both pass the limit check. For a small free allowance the exposure is minor, but the same pattern guarding a paid credit balance is a real vulnerability, and it is exactly the kind of code that passes a quick review.
Server secrets are resolved at runtime on the server, first from environment configuration and then from a secret manager, and none use a public prefix. Deploy ignore rules are a useful backstop, but the stronger habit is keeping credential files outside the repository entirely, so a single misconfigured rule cannot expose them.
Common mistakes to test for
- Sign in as user B and request user A's records by ID through the API
- Query each table or collection with the public client key while signed out
- Search the production bundle and source maps for secret-looking strings
- Replay a payment webhook and confirm access is granted only once
- Edit your own profile to add an admin role and check it has no effect
- Send parallel requests at a quota limit and confirm it holds
- Paste a script tag into a text field that is rendered elsewhere
When a simpler solution is better
If the app is an internal prototype with fake data, the simplest security measure is not to expose it publicly: keep it behind authentication or a private network until it is ready for real data.
If the app needs only sign-in and a few pages, use your platform's managed authentication and the most restrictive default rules, and avoid custom roles until you need them. Fewer features mean fewer trust boundaries to review. Sometimes the best security fix is removing a feature nobody uses.
Get the review done before customers find the gaps
Run the checklist, test with two accounts and a signed-out client, and fix by blast radius. If you would rather have the review and fixes done for you, see ImadDhin's vibe-code rescue practice, send your repository details through the project brief, or book a 30-minute call.
Frequently asked questions
Are apps built with Lovable, Bolt, v0 or Cursor insecure?
Not inherently. The tools produce working code quickly, but authorization, database rules, secrets handling and payment verification often need deliberate configuration and review before real users and data arrive.
What is the most common security issue in AI-generated apps?
In our reviews, missing server-side authorization and overly open database rules are the issues to check first, because they can let any signed-in or even anonymous user read or change other users' data.
Can we ask the AI tool to review its own code for security?
It can help find obvious issues, but it is not a substitute for testing like an attacker: using two accounts, querying with the public key while signed out, replaying webhooks and inspecting the built bundle.
How long does a security review of a vibe-coded app take?
It depends on the number of features, data types and integrations. A focused review of a typical prototype is measured in days, with fixes planned by blast radius. Payments and multi-tenant data add time.
Should we rewrite an AI-built app to make it secure?
Usually not. Most issues are fixable in place: rules, authorization checks, secret handling and webhook logic. A rewrite makes sense when the data model or architecture cannot support proper authorization.
Secure your AI-built app before launch
Bring the repository and the features that touch data or money.
Book a 30-minute callSecurity review and production hardening for AI-built apps.
Vibe-code rescueChoose fix an existing prototype.
Send a project briefKeep reading
How to turn a Lovable/v0 prototype into a production application
AI builders ship demos fast. Production needs auth, payments, secrets hygiene, observability, and an architecture that survives real users — here is the hardening path I use.
The limits of building agents only from vibe-code tools — and what it costs you at scale
Vibe-coded agents demo brilliantly and stall in production. Here is the exact wall they hit, what has to be rebuilt, and how to keep the speed without paying for it twice.
Supabase row-level security mistakes in AI-built apps
In a Supabase app, row-level security is often the only thing between the public key and your data. The policy mistakes AI builders make most often, and how to test for them.
Secrets in AI-generated apps: moving keys out of the client bundle
AI app builders often wire provider keys straight into browser code. Here is how to classify credentials, move secret-bearing calls server-side, rotate what shipped and stop it from coming back.