By the SurfaceCheckr team. Every finding maps to a check we run in production - see our methodology.
Vibe coding security: what your AI-built app hands the public
You prompted a model, it wrote the app, the app worked, you shipped it. That part is real: the models are good at producing code that runs. What they are not doing, unless you ask, is thinking about what the running app shows a stranger who never logs in.
That gap has a name now. Vibe coding security is the practice of finding and closing the holes an AI coding assistant leaves behind. The model optimized for one thing, make this work, and "don't leak anything" was a separate job you didn't put in the prompt. So the feature shipped and the exposure shipped with it. The useful question is not "is AI code insecure" in the abstract. It is which specific things end up readable from outside, because they cluster in a short, predictable list.
Why this is a real category and not a scare word
The clearest number comes from a controlled study, not a headline. In its 2025 GenAI Code Security Report, Veracode ran 80 curated coding tasks across more than 100 large language models. When a task could be completed a secure way or an insecure way, the models picked the insecure option 45% of the time. For cross-site scripting specifically, the generated code failed to defend against it in 86% of the relevant samples.
Read that carefully, because the honest framing matters. It does not mean half of all shipped apps are broken. It means the code an assistant writes is not secure by default, so the review pass you skipped is the one that would have caught the hole. The tool gives you a working app. The parts it left open are still yours.
The exposures that show up from outside
An attacker on the open internet cannot see your prompt history or your source file. They see what your live URL serves. From that vantage point, vibe-coded apps leak in four recurring ways.
A key that shipped to the browser
Ask a model to "connect to Stripe" or "set up the Supabase client" and the fastest working answer is a key sitting right in the code. The model has no idea whether that file ends up on your server or in the JavaScript bundle sent to every visitor, and it often picks the client-side spot because that is where the example in its training data lived. Prefix it with NEXT_PUBLIC_ or VITE_, which a model will happily suggest, and your framework inlines that secret into the public bundle.
Some keys are fine there. A Supabase anon key or a Stripe publishable key is public by design. The problem is the secret ones: a sk_live_ Stripe key, an AKIA… AWS key, a Supabase service_role JWT. That last one is the worst case, because it ignores every access rule you wrote.
A database table with no access rule
Modern stacks let the browser talk to the database directly, with a policy as the only gate. Ask an assistant to "add a waitlist table" and it writes a CREATE TABLE migration. In Supabase, a table made that way does not get row-level security turned on automatically, so the public anon key reads every row. In Firebase, an app left in Test mode lets anyone who guesses your project ID read and write the data. Nobody wrote the rule, because nobody was thinking about the gate, and the demo looked fine. This is the literal version of the vibe-coder's lament: your entire waitlist visible in the frontend.
An API that returns more than it should
The page loads, fires a quiet fetch, and the response carries more than the logged-in user's own data: other people's names, emails, roles, sometimes a password hash the serializer dumped along with everything else. That is broken object-level authorization, the first item on the OWASP API Security Top 10, and it is readable straight from the browser's network tab.
A file that rode along on deploy
Generated projects accumulate scaffolding: a .env the model created, a backup.sql from when you seeded test data, a .git directory. Deploy the whole folder and those go up too. Then a bot requests /.env directly, and if your host serves it, the model's helpful setup file is a public download.
Why the next prompt won't catch it
It is tempting to think you can just ask the model to review its own work. Sometimes that helps. But the model reasons about the code in front of it, not about your deployed surface. It cannot see what your host actually serves, whether the bundle shipped the key, or whether debug got toggled in the environment you deployed to. Those are facts about the running system, and the model works at edit time. The exposure is a deploy-time fact.
// the source it wrote const key = process.env.SUPABASE_KEY // looks fine in the editor
How to check your own app
You need a browser and a few minutes, no tools.
- Open your live site in an incognito window with no login. Open devtools, go to the network tab, and reload.
- Watch every request the page makes to your backend or database. Each one is a request a stranger can make too. For each response carrying data, ask: should a logged-out visitor see this?
- Request the files that should not be there:
/.env,/.git/config,/backup.sql. If any returns content instead of a 404, it is a public download. - Search the JavaScript bundle for key shapes:
service_role,sk_live_,AKIA. A secret key in there is a fire.
A passive external scan does all of this at once, without logging in or sending an attack. It reads what your site already serves and grades it, which is the same view the attacker has. That is what SurfaceCheckr does, and the four exposures above are exactly what it looks for. One honest limit: a scan from outside catches the leaked key, the open table, and the reachable .env, but it cannot tell you whether the logic the AI wrote is correct, and it will not log in. For that you need a pentest, and we don't pretend to be one.
If you want the deeper cut on where AI code specifically goes wrong, the rundown of AI-generated code risks walks through the failure modes one by one. If you are on Supabase, locking down row-level security is the single fix that closes the most at once. Keep prompting. Just check what the model shipped alongside the feature.
Read next
- You shipped your MVP this weekend. What did you leave exposed?Shipping fast without shipping holes
- Does "vibe coding" leave security holes? (Yes, here's where)Shipping fast without shipping holes
- The website security checklist: what to lock down, in order of what gets hit firstShipping fast without shipping holes
- Vibe coding best practices: shipping fast without shipping holesShipping fast without shipping holes
Find it before someone else does.
Paste your domain. The grade and issue count are free, and you'll see in a couple of minutes exactly what's reachable from outside.
External, read-only scan. We only request public URLs - never log in, never send attack traffic.