The security holes vibe coding keeps shipping
A demo can look finished while every row is public. Three recent failures show where AI-built apps break, plus a two-account test you can run before launch.

AI builders are very good at producing the visible path: sign in, create a record, show it in a dashboard. The invisible path is harder. Who owns that record? Can another user request it directly? What happens when the browser ignores the interface and calls the API itself?
Recent disclosures involving Base44, Lovable and Moltbook were different incidents, but they shared one pattern: the product worked while a trust boundary did not. That does not prove every AI-built app is insecure. It does mean generated code should be treated as a draft until the access rules have been tested.
Secrets do not belong in the browser
Anything shipped to the browser can be read by the person using it. In Next.js, a variable prefixed with NEXT_PUBLIC_ is inlined into the client bundle. That is correct for analytics IDs and publishable client keys. It is a leak for database passwords, model-provider secret keys and admin tokens.
Concrete check: build the app, open its JavaScript in DevTools and search for the prefixes used by your providers. Then list every match. For each one, be able to explain why it is safe to be public. Move every privileged credential behind a server-only boundary and rotate it if it has already shipped.
Sources
Your app trusts whoever is asking
Logging a user in answers one question: who are you? Authorization answers another: are you allowed to touch this specific object? An endpoint can get the first right and still expose another customer’s data.
Take GET /api/invoices/inv_1042. User A owns that invoice and should receive 200. User B can see the same ID in a shared screenshot, a URL or a network log. If User B repeats the request and also receives 200, the app has broken object-level authorization. The correct response is 403 or 404, enforced on the server for that object.
Wiz found a Base44 registration and OTP flow that let an outsider join private, SSO-only apps using an app ID visible in the URL. Lovable later confirmed that a backend regression exposed chat history and source code from public projects to other authenticated users. Both products fixed the reported issues. The useful lesson is the boundary: knowing an app ID or being signed in is not proof of permission.
GET /api/invoices/inv_1042User A owns the invoice
User B does not
Same endpoint, different identity. The server checks permission for this invoice.
The database will not protect itself
There is no honest one-line claim that Supabase or Firebase is secure or insecure by default. Supabase enables row-level security for tables created in its dashboard, but tables created through raw SQL need it enabled explicitly. Firestore production mode denies client reads and writes, while test mode allows them. Your actual policies decide the result.
Moltbook is the cleanest recent example. Wiz found a Supabase publishable key in the frontend and a backend without the required row-level policies. The publishable key was not the vulnerability by itself. The missing database boundary turned it into unauthenticated read and write access, exposing 1.5 million API authentication tokens, 35,000 email addresses and private messages. The team secured the database after disclosure.
The check is simple: test the data layer directly as an anonymous user, as the owner and as a different authenticated user. A hidden button proves nothing. The database or server must reject the forbidden request.
Why AI gets authorization wrong in particular
Authorization is business logic. A model can see a table called invoices, but it cannot infer whether an accountant may read every invoice in a company, only invoices they created, or none after leaving the team. Those rules live across routes, queries, roles and lifecycle events. They have to be stated and tested as a system.
Veracode tested more than 100 models across 80 focused coding tasks and found a known flaw in 45% of generated solutions. The test covered SQL injection, cross-site scripting, log injection and weak cryptography. It did not test whole applications or object authorization, so it is evidence that generated code needs review, not proof that 45% of AI-built apps are insecure.
How to test your own app
You can catch the most obvious access failures with two test accounts and one focused pass through the product.
- Try to read someone else’s data: Log in as user A, note the ID in a request (a URL like /api/items/57, or a network call), then repeat it while logged in as user B. If B sees A’s data, you have broken access control.
- Log out and knock anyway: Open the network tab, sign out, and replay your API calls with no session. Anything that still returns real data is unprotected.
- Grep your bundle for secrets: Search your shipped JavaScript for your provider’s key prefixes and words like "secret" or "key". A live credential in the browser is already leaked.
- Make the database say no: Run allowed and forbidden reads, writes and deletes against your real policies. Assert the expected result instead of treating an empty response as proof.
- Name the guard for every route: For each endpoint, answer who is allowed to call it and where that is enforced on the server. If the answer is "the frontend hides the button," it is not protected.
The takeaway
Vibe coding is useful for reaching a working product quickly. Working is not the same as defended. Before real data goes live, write the access rule for every sensitive object, enforce it on the server or database, then prove the forbidden path fails.