The First Hour: Where Vibe-Coded Apps Usually Fail First

June 30, 2026engineering9 min read

In the rescue audits we run on vibe-coded apps, the first finding rarely takes an hour. It usually lands in under five minutes, somewhere between opening DevTools and searching the JavaScript bundle for supabase, service_role, NEXT_PUBLIC_, or VITE_.

That does not make Lovable, Bolt, v0, Cursor, or Replit useless. It means their output is usually day-one code, and day-one code becomes dangerous when it is treated as production code. The common failure is not that AI cannot build features; it is that "secure by default" was never part of the prompt.

The median app fails before the audit gets interesting

The public numbers point in the same direction, but each one measures a different slice of the problem. Wiz Research reported that one in five organizations it observed were exposed to systemic risks in vibe-coded applications. Veracode found that 45% of AI-generated code samples failed security tests with OWASP Top 10 issues. In a self-published scan of 50 deployed Lovable apps, Tomer Goldstein reported that 89% lacked proper Supabase row-level security.

Those numbers sound dramatic until you run the first pass yourself. Open the deployed app, filter Network requests for /rest/v1, inspect the headers, and look at which key the browser is carrying. If the frontend bundle contains a Supabase service_role key, a Stripe secret, an OpenAI key, or a database URL, the app is already past "needs cleanup."

Imagine you're a CTO inheriting a vibe-coded internal tool after a founder weekend build turned into 4,000 signups in a quarter. The dashboard works, customers can log in, and billing seems to charge cards correctly. Your first security review is not about component naming or test coverage; it is about whether one free account can read every other customer's rows.

Why does the same bug keep showing up?

AI coding tools optimize for the visible path: the form submits, the dashboard renders, the demo account logs in. Security lives in the invisible path: authorization boundaries, RLS policy shape, secret handling, input validation, dev/prod separation, and the boring refusal to trust client state.

That gap matters because most vibe-coded prompts describe product behavior, not threat models. "Build a SaaS with a subscription paywall" often becomes a React boolean, a localStorage flag, or a hidden button. The UI says "Pro" is locked, but the server still accepts the request.

The fix is not to stop using AI. We use AI inside our own workflow, but we keep senior engineers in the loop where the blast radius is real. At appssemble, our engineering practice treats generated code as a draft until the auth model, database policy, deployment path, and observability all survive review.

Minute 0-5 should start in the browser, not the repo

The fastest audit starts with the shipped app. We open DevTools, read the loaded JavaScript, and search for apiKey, serviceRoleKey, STRIPE_SECRET, OPENAI_API_KEY, GEMINI_API_KEY, Bearer , and eyJ, the common JWT prefix.

This sounds crude because it is. Escape.tech found 400+ exposed secrets across 5,600 publicly available vibe-coded apps. SupaExplorer's self-published summary reported an 11.04% exposure rate across 20,052 launch URLs, including 2,325 critical exposures. Treat that as a directional field scan, not a benchmark study.

Not every Supabase key in a browser is a breach; anon and publishable keys are designed to be public. The critical failures are service_role or secret keys in the bundle, or public keys sitting in front of tables whose RLS policies do not actually restrict rows.

The common mistake is misunderstanding public prefixes. NEXT_PUBLIC_* and VITE_* are not private environment variables with nicer names; they are intentionally shipped to the browser. One typo, such as NEXT_PUBLIC_OPENAI_API_KEY instead of OPENAI_API_KEY, turns a server secret into customer-side JavaScript.

Minute 5-15 is where Supabase RLS tells the truth

Supabase is not the problem. Misconfigured Supabase is the problem, and vibe-coded apps keep producing the same broken shape: a table exists, the frontend can query it, and row-level security either is off or says any authenticated user can read every row.

The dangerous policy looks innocent: authenticated users can view data. In practice, that can mean user A signs up, gets a valid JWT, and reads user B's records because the policy checks auth.role() = 'authenticated' instead of auth.uid() = user_id. Matt Palmer's scan found 303 exposed endpoints across 170 Lovable projects. MITRE assigned CVE-2025-48757 a 9.3 critical score; NVD lists the record as disputed by Lovable.

We do not accept "RLS is enabled" as proof. We check whether each policy matches the application model: own rows, team rows, tenant rows, admin-only rows, public read rows, and service-only writes. A policy existing is not the same as a policy being correct, and a Supabase service-role key bypasses RLS entirely if it reaches the browser.

Minute 15-45 is where IDs, paywalls, and auth logic break

After secrets and RLS, we walk the application like an attacker with a normal account. Any URL with /users/123, /orders/456, /files/789, or an app_id gets changed. If the next ID returns another user's data, you have IDOR, not a routing problem.

Then we check the paywall. We toggle client flags, patch localStorage, change React state in DevTools, and hit the underlying API directly. If the backend trusts isPro from the browser, the paid feature is not paid; it is hidden.

The research receipts are ugly, but they need attribution. The Register reported researcher Taimur Khan's claim that a Lovable EdTech showcase app exposed 18,697 records, including 4,538 students and 870 full PII profiles, with authentication logic described as backwards. Lovable told the publication it contacted the app owner and acted within minutes after receiving a proper disclosure report.

Wiz disclosed a Base44 issue on July 9, 2025 where a public app_id could be enough to register a verified account on private apps, bypassing SSO. Wix fixed it in under 24 hours and said it found no evidence of past abuse. The lesson is not that every Base44 app was compromised. The lesson is that shared platform auth bugs can cross tenant boundaries fast.

This is the part of the audit where a CTO usually stops asking whether the UI can be kept and starts asking which layer must be replaced. Sometimes the UI is fine. The danger sits behind it, in the place a demo never exercises.

Minute 45-60 is where destructive primitives show up

The last quarter-hour is about damage. We look for client-callable mutations that do not derive user_id from the authenticated session, admin routes without middleware, public storage buckets, eval(, dangerouslySetInnerHTML on user input, and any AI agent that can write to production data.

The Replit/SaaStr incident is the cleanest warning here. During a 12-day experiment, the agent wiped 1,206 executive records and 1,196+ company records during an explicit code freeze, according to Jason Lemkin's public account as reported by Business Insider. That is not a styling bug; that is a missing boundary between suggestion, execution, development, and production.

The Tea app breach is not proven to be a vibe-coded failure, so it should not be used that way. It still belongs in the checklist as the storage version of the same class of mistake: a public Firebase bucket exposed 72,000 images, including 13,000 photos of users holding government IDs, followed by reporting of 1.1M private messages in a second leak. Public-by-accident is still public.

A scanner helps, but ownership fixes the app

Scanners are useful because they catch repeatable failures. They can flag exposed keys, missing headers, known dependency advisories, suspicious routes, and some broken RLS patterns. They cannot fully understand whether your tenant model allows a regional manager to view one branch but not another.

Fixing the app means making the security model explicit. RLS is written from auth.uid() and tenant membership outward. Paid features are enforced server-side. Secrets stay server-side. API routes re-check the session. Input is validated before it reaches the database. Production write access is not available to a chat agent in a dev loop.

That work sits next to release discipline. Feature flags, staged rollouts, monitoring, rollback paths, and error tracking are part of security because they reduce the time between "something broke" and "we contained it." That is why our Growth & Scale work includes release management and monitoring, analytics after launch alone.

The first-hour script should fit in one screen

If you are the CTO inheriting the app on Monday, the audit note should be short enough to run before lunch:

  • Search browser-delivered bundles for service_role, sb_secret_, sk_live_, STRIPE_SECRET, OPENAI_API_KEY, DATABASE_URL, NEXT_PUBLIC_, and VITE_.
  • Confirm Supabase client code uses only anon or publishable keys, then check sensitive tables for user- or tenant-scoped RLS policies.
  • Copy the browser key and call the Supabase REST endpoint directly against a disposable account. A private table should not return another user's rows.
  • Change one user, order, file, or app_id value and hit the API directly, not through the UI.
  • Toggle paid-feature flags in DevTools, then call the backend route without trusting client state.
  • Check storage buckets, admin routes, and agent permissions for unauthenticated read or write paths.

Our pass/fail threshold is blunt. If we find client-side secrets, broken RLS on private data, or unauthenticated admin routes, launch pauses. The UI might stay. The auth and data-access layer might need to be replaced before another feature ships.

The first hour is not the full audit, but it is enough to decide

A first-hour review will not prove your application is secure. It will prove whether the codebase understands the basics: secrets, RLS, authorization, input validation, storage permissions, and production boundaries. If it fails there, deeper architecture review can wait until the obvious risk is removed.

The point is not "AI bad." The point is that generated code needs the same production gate as human code, and often a stricter one because it arrives faster than the review process around it. Apiiro's Fortune 50 research found AI-assisted developers producing 3-4x more commits and teams shipping 10x more security findings. That does not mean every AI-assisted team is unsafe. It means your review system has to scale with the code generator.

If you have shipped or inherited a vibe-coded app, start with the first hour. If you want our team to run the review with you, book a call. We will tell you which parts can stay, which parts need hardening, and which parts should never have reached production.