howsafeismyapp

Blog · 2026-08-06

Shipping a vibe-coded app safely — the security basics AI builders skip

By the howsafeismyapp team

Vibe coding works. You describe, the model builds, and an afternoon later there is a real app with real users on a real domain. We are not here to talk you out of that — we scan these apps all day, and the interesting pattern is not that they are uniquely broken. It is that they break in *predictable places*: the handful of concerns that never show up as a failing feature, so no prompt ever asks for them.

An app with an open database works. An app with an admin key in the bundle works *great*. That is precisely why these things survive into production. The fix is not to stop vibe coding — it is a short, repeatable pass over the five places AI-generated apps actually fail. (Practical guidance, not legal advice.)

1. Data access rules — is the database actually behind the login?

AI builders wire the browser directly to a backend-as-a-service — usually Supabase, sometimes Firebase. In that architecture your login form protects nothing by itself: the database answers API requests, and the only gatekeeper is the access-rules layer — Row Level Security on Supabase, security rules on Firebase. Generated code routinely ships with that layer off, because everything works without it.

The test takes two minutes: from a logged-out private window, find your app's API request in DevTools → Network and replay it. If a table with user data answers, it answers to everyone: database readable without a login. The full walkthrough and fix is in our Row Level Security guide.

This is the most consequential item on the list. Do it first.

2. Secrets in the bundle — what did the AI paste into client code?

Everything in your frontend JavaScript is public — every visitor downloads it. AI generation steps blur the server/client line, and secrets follow: a Supabase service_role key used to "fix" a blocked query, an API secret in a NEXT_PUBLIC_-prefixed variable, credentials in comments that shipped via source maps.

Search your deployed bundle for service_role, for sk_live, for eyJ (JWTs — decode them and read the role claim). Findings to know:

3. What loads before consent — the network tab doesn't lie

Trackers arrive innocently: a template included Google Analytics, a prompt added a Meta Pixel, a YouTube embed came with the hero section. Under § 25 TDDDG and Art. 6 GDPR, tracking needs consent *before* it fires — and most vibe-coded apps either have no banner or a decorative one painted over scripts that already ran.

Open a private window, DevTools → Network, load your app, and touch nothing. Every third-party request you see happened before anyone could consent. Sort them: fonts from Google are their own classic finding (Google Fonts loaded directly from Google — self-hosting is a 15-minute fix). Trackers are the bigger problem: tracking loads before anyone could consent, third-party tracking service embedded. For each one: remove it, replace it with a cookieless EU-friendly alternative, or gate it behind a consent tool that actually blocks it.

If nothing tracks, you may not need a banner at all — needing consent and needing a banner are different questions.

4. Legal pages — the part no prompt generates

Models generate code; the legal pages are on you, and their absence is visible to anyone who scrolls to your footer — competitors and their lawyers included.

Both linked in the footer, on every page, including the app itself.

5. Browser defenses — the headers nobody asked for

Security headers are one-line instructions that switch on protections browsers already contain — against MIME-sniffing, clickjacking, protocol downgrade and injected scripts. No prompt asks for them, so generated apps ship without:

They live in a config file (vercel.json, netlify.toml, next.config.js) — the walkthrough is in what security headers actually do.

The launch ritual

The five areas compress into a pass you can rerun before every meaningful release:

  1. Private window, logged out, DevTools open.
  2. Network tab on load: any third-party request before interaction? Justify or remove each.
  3. Replay the data request: does the database answer without auth?
  4. Search the bundle: service_role, sk_live, unfamiliar JWTs, .map files.
  5. Footer: privacy policy and imprint linked?
  6. Response headers on the document request: nosniff, frame protection, HSTS, CSP?

Fifteen minutes, and it catches the failure modes that actually occur — the platform-specific versions live in our Lovable, Bolt and v0 checklists.

Common questions

Is vibe-coded software inherently less secure?

The generated code itself is often fine. What differs is the *process*: no security-minded reviewer is in the loop, and the failure modes above never surface as broken features. Add the review pass and the gap mostly closes.

I'm pre-launch with no real users. When does this start mattering?

The moment real personal data arrives — which is usually the first waitlist signup, well before "launch." The database and secrets checks are worth doing even earlier, since fixing access rules is easiest before there is data behind them.

Can I just ask the AI to make my app secure and compliant?

Asking helps — prompts like "enable RLS with least-privilege policies" or "add a strict CSP" produce real improvements. But generation isn't verification: the model can claim anything, while the Network tab, your bundle and your response headers show what actually ships. Trust the outside view.

What's the single highest-priority check?

The logged-out database test. An open users table is the finding with the worst blast radius — under Art. 32/33 GDPR it is a breach waiting to be discovered — and it takes two minutes to run.

Or check all five areas at once

Everything on this list is visible from outside — which is the point of our free passive scan. It loads your app the way any anonymous visitor would and reports open databases, exposed keys, pre-consent tracking, missing legal pages and header gaps in about a minute. No login, nothing installed, nothing stored. Run it, fix what it finds, ship the next feature.

Check your app against all of this — free

passive · no login · we store nothing

Related posts

← All posts