A founder forwards a Lovable repo on Monday morning. They have users, a Stripe account, a Supabase project, and one urgent question: should we rescue this codebase or rebuild it before the next launch?
We do not start with code style. We do not start with folder structure. A serious vibe-coded app security audit starts with the things that can leak data, grant free subscriptions, or make the database impossible to save.
The order is the opinion. Lovable is useful for getting a product into users' hands, but a Lovable security checklist for production has to triage like production engineering, not like a prettier lint pass.
Lovable has added security scanners for common mistakes, and that helps. Its own docs still make the important point: automated checks support secure development; they do not replace a real security review when the app handles sensitive data or critical flows.
Why audit order decides the rescue vs. rebuild call
The first 2 hours matter because every check either stops active damage, gates the rebuild decision, or buys the founder credible runway. NVD lists CVE-2025-48757 as supplier-disputed, but the failure mode is still the first thing we audit: Lovable-generated apps relying on direct browser-to-Supabase requests where RLS has to carry the security model. Matt Palmer's statement put the scan at 303 endpoints across 170 projects, about 10.3% of 1,645 analyzed; Semafor separately reported that Palmer and Kody Low's deeper analysis turned up 170 vulnerable Lovable sites.
That is why we check secrets, Supabase RLS, and auth before anything else. Supabase is clear that anon and publishable keys can live in public clients, while service_role and secret keys bypass RLS and must not be exposed. If your elevated key is in the browser bundle or a public table has RLS off, the next step is not "technical debt grooming." The next step is rotate keys, patch policies, and stop shipping features until the blast radius is understood.
Imagine you're a technical founder two days before sending a demo link to an enterprise buyer. The UI looks finished, but a logged-out request to /rest/v1/users?select=* returns customer rows. That is not a refactor ticket. That is the audit taking control of the roadmap.
What do we check in 5, 30, and 120 minutes?
We split the audit into 3 gates: a 5-minute smell test, a 30-minute security triage, and a 120-minute keep-or-burn pass. Anything beyond that is no longer an audit. It is the rebuild estimate.
In the first 5 minutes, we run a secrets scan, open package.json, look at the largest files, and read the README. In the next 30 minutes, we inspect Supabase policies, auth paths, and Stripe webhooks. In the final 120 minutes, we walk schema, transactions, validation, dependency graph, observability, and platform lock-in.
The point is not to find every defect. The point is to find the defect that changes the recommendation from "patch this week" to "rebuild the foundation before you add one more customer."
The first 3 checks stop active bleeding
1. Secrets in the client bundle: We run gitleaks detect, search history for sk-, and inspect public environment variables such as VITE_ and NEXT_PUBLIC_. SupaExplorer's 20,052-URL scan reported 2,325 critical Supabase exposures across vibe-coded apps. The useful distinction is not "Supabase key equals leak." The useful distinction is anon keys are expected in clients; service_role keys are not. In @tgoldi's 50-app Lovable spot check, 34% exposed a service_role key in client code.
This comes first because a leaked service_role key bypasses RLS entirely. If it is exposed, the order is simple: rotate keys, invalidate sessions where needed, review logs, then continue.
2. Row-Level Security on every public table: We check for public tables with RLS disabled and policies that resolve to USING (true). The same @tgoldi 50-app spot check reported that 89% of deployed Lovable apps had at least one table with RLS off entirely.
RLS is the last line of defense when frontend auth, direct browser queries, or leaked anon keys fail. If it is missing, you do not have a production Supabase setup. You have a public database with a nicer UI.
**3. Auth logic and direct auth.users access:** We look for client-side "authenticated" flags, protected pages that render before redirect, no rate limiting, and direct queries against auth.users instead of a profiles table. The Register reported that Taimur Khan found 16 vulnerabilities in a Lovable-hosted edtech app, including inverted auth logic that exposed 18,697 user records and 4,538 student accounts.
Auth bugs are dangerous because they turn anonymous traffic into authenticated traffic. Once that happens, every weak RLS policy becomes reachable.
Payments come before architecture arguments
4. Stripe webhook signature verification: We grep for constructEvent, stripe.webhooks, and STRIPE_WEBHOOK_SECRET. Stripe expects signature verification with the raw request body, the Stripe-Signature header, and the endpoint secret. If the handler trusts raw JSON, anyone who knows the endpoint can fake checkout.session.completed and grant themselves a paid plan.
This is usually a 10-minute finding with a serious business impact. It does not matter whether the app uses the right component library if the paid tier can be spoofed with a POST request.
5. Idempotency on side-effecting webhooks: We look for a webhook_events or processed_events table keyed on Stripe event.id, plus a unique constraint. Stripe can redeliver the same webhook event for up to 3 days, and its docs tell you to log processed event IDs before acting.
If the handler does not do that, retries can duplicate entitlement changes, emails, local records, and any non-idempotent outbound Stripe POST calls. The duplicate-charge risk appears when your app creates or mutates Stripe objects again without idempotency keys, not because Stripe redelivered the original event. That is why payment idempotency sits before schema elegance. Money paths fail loudly and quickly.
Schema is the keep-or-burn gate
6. Schema sanity: This is where we decide whether rescue is real. We count foreign keys, inspect naming drift, look for JSON blobs where relations should exist, check missing indexes, and compare soft-delete patterns across tables.
Inigra's single-codebase audit found more than 600,000 lines of AI-generated code, 100+ database tables, 100+ serverless functions, 500+ database migrations, and only 2 test files. It also found functions over 3,000 and 1,500 lines. At that point, "clean up the repo" is not a plan.
The schema tells us whether the app has a core model or just accumulated prompts. If user_id, userId, and userid all exist in the same product, every future feature inherits that confusion.
Operational checks tell us whether rescue is real
7. Transactions on multi-step writes: We search for BEGIN, supabase.rpc, and transaction wrappers around onboarding, order creation, and subscription activation. A signup flow that inserts into users, then profiles, then subscriptions without rollback creates ghost accounts on the first network failure. If the critical path cannot move into one RPC, one transaction, or one server-side unit before launch, we price that as stabilization, not cleanup.
8. Server-side input validation: We check for Zod or Valibot schemas on routes, edge functions, and RPC calls. Client-side validation is useful for UX, but production security lives on the server. If schemas exist only in React form components, the API is trusting the browser, and the browser is the least trustworthy part of the stack.
9. Dependency graph and dead code: We run tools such as knip, madge, and npm audit --production to see whether the codebase can be moved without excavation. Our smell-test threshold is simple: 80 to 120 dependencies is a typical Lovable handoff, 150+ dependencies slows every migration, and knip reporting more than 25% unused exports means the repo has structural rot, not clutter.
10. Error boundaries and observability: We count error boundaries, inspect try/catch blocks, and look for Sentry, PostHog, structured logs, or anything that tells you what broke in production. A React app with 50 useEffect calls for every error boundary is not ready for real traffic; it is ready to surprise you at 2 a.m. If this cluster fails, the app might still be rescuable, but the founder should stop calling it a launch checklist and start calling it a stabilization sprint.
These checks do not always force a rebuild. They tell us the cost of keeping the current codebase alive while we stabilize it through engineering and Growth & Scale work.
The last 2 checks predict the next 6 months
11. The smell test: We look at largest files, README quality, dependency count, commit history, and test count. Our thresholds are plain: under 500 lines for the largest file is healthy, 800 to 1,500 is typical for a Lovable handoff, and 3,000+ is a burn-it signal.
A useful README explains setup, environment variables, and scripts. An empty README, one commit, 150+ dependencies, and zero tests do not prove the product is doomed. They do prove that every handoff will be slower than the founder expects.
12. Vendor lock-in: We search for Lovable-specific cloud paths, hidden AI gateways, and platform-hosted functions that cannot be moved without rewriting the feature. The same Inigra audit describes a codebase with 30+ AI serverless functions routed through a proprietary gateway and zero direct OpenAI or Anthropic API calls.
That changes the commercial recommendation. If the AI logic is outside the repo, we usually budget 2 to 4 extra weeks just to extract prompts, provider calls, and failure handling into code you own.
What should you hear after hour 2?
At the end of 2 hours, we do not hand you a 90-page PDF. We give you one of 3 answers: green means patch now and keep shipping, amber means stabilize for a short migration window, and red means rebuild the core before the product earns more traffic.
The horror stories explain why we keep the answer that blunt. Semafor reported that Danial Asaria said he found personal debt amounts, home addresses, API keys, and prompts in 47 minutes. The Register later reported a separate BOLA flaw where a researcher said another user's profile, projects, source code, and database credentials were reachable in as few as 5 API calls from a free account, after a report filed 48 days earlier.
Lovable is right for launch. It is not a substitute for production ownership. At appssemble, we use this checklist in this order. If your next launch depends on a repo you do not trust, book a call and we will give you the keep-or-burn read before you spend another sprint.