Skip to content
Angelo LamonacaBook a call

Free checklist

The vibe-coded app security checklist

25 checks to run before real users and real money reach your app. Each one says what to look for and how to check it yourself. No security background needed.

By Angelo Lamonaca, full-stack engineer · Updated

Why this list exists. AI tools build what you ask for, and nobody asks for row-level security. In 2025 a researcher found 170 apps from Lovable's showcase that let anyone read or change their database (CVE-2025-48757), and Veracode found security flaws in 45% of coding tasks done by AI models. It works for apps built with Lovable, Bolt, v0, Replit, Cursor or Claude Code; the database checks assume Supabase, the most common backend for these apps.

Your ticks are saved in this browser only. Building with Lovable? There's a page about fixing Lovable apps too.

0 of 25 checked

01 · 5 checks

Database and data access

Where most AI-built apps leak. Start here.

  • In Supabase's SQL editor, this lists every table in the public schema that has it switched off. It should return nothing. On Firebase, your Firestore and Storage rules shouldn't contain if true or a test-mode expiry date.

    select tablename
    from pg_tables
    where schemaname = 'public' and not rowsecurity;
  • This lists the policies whose rule is simply true. Each one it returns needs a good reason, like a public product catalogue.

    select tablename, policyname, cmd, roles
    from pg_policies
    where schemaname = 'public'
      and (qual = 'true' or with_check = 'true');
  • Log in as one test user, take the ID of a record that belongs to another account, and ask for it: change the ID in the URL, or edit the request in your browser's DevTools. You should get nothing back.

  • Copy the anon or publishable key from your site's source and ask your Supabase API for each table. Whatever comes back, anyone on the internet can read. It's how a researcher found 170 leaking Lovable apps in 2025.

    curl 'https://<project>.supabase.co/rest/v1/<table>?select=*' \
      -H 'apikey: <public key>'
  • While logged out, open the link to a file another user uploaded. A public bucket serves its files to anyone who has the URL.

02 · 4 checks

Secret keys

A key in the browser is a key anyone can use, on your bill.

  • Open your live site, then View Source and the Sources tab in DevTools, and search the JavaScript for sk_live, sk-, service_role, sb_secret_ and secret. Supabase's anon or publishable key and Stripe's pk_ keys are meant to be public. Nothing else is.

  • If a secret ever reached GitHub, a chat, a screenshot or a prompt, removing it isn't enough: it's still in the history. Create a new key and revoke the old one. GitHub's secret scanning flags many key formats for you.

  • The browser should call your own edge function, never OpenAI or a similar API directly. Give each user a rate limit, and set a monthly spending cap in the provider's dashboard.

  • Not in the code, and not in a committed .env file. Use separate keys for testing and for production, so a leaked test key can't touch real data.

03 · 4 checks

Login and permissions

Hiding a button is not access control.

  • As a normal user, find a request only an admin should be able to make (DevTools, Network tab, Copy as fetch) and run it in the console. It should fail.

  • Logged in as a normal user, open your admin page and watch the Network tab. If any admin data loads, the check only lives in the frontend.

  • Rate limits and a CAPTCHA on sign-up, login and password reset. Supabase Auth supports hCaptcha and Cloudflare Turnstile out of the box.

  • Sign up with a fresh address and check you can't use the app before clicking the confirmation link. Otherwise anyone can create accounts in someone else's name.

04 · 5 checks

Payments

Test mode is forgiving. Real money isn't.

  • Your webhook handler uses the endpoint's signing secret (constructEvent, or constructEventAsync in edge functions) and rejects any request without a valid Stripe-Signature header.

  • Open your checkout success URL directly, without paying. If it unlocks anything, the check is in the wrong place: only the webhook should change a user's plan.

  • Stripe can deliver an event more than once. Record each event ID and skip the ones you've already handled, then test it by resending an event from the Stripe dashboard.

  • Handle invoice.payment_failed, customer.subscription.deleted and charge.refunded, and test each one with stripe trigger from the Stripe CLI.

  • Buy your own product in live mode with a real card. Check the database, the confirmation email and the receipt, then refund yourself.

05 · 4 checks

Reliability and recovery

Things will break. What matters is how fast you notice, and how fast you're back.

  • Check your database plan's backup schedule, then restore a backup into a separate project. A backup nobody has ever restored is a hope, not a backup.

  • Add error monitoring (Sentry or similar) to the frontend and your server functions, with alerts to email or Slack. Break something on staging and see whether you hear about it.

  • Your repo has a supabase/migrations folder, or your stack's equivalent, and nobody edits the production schema by hand in the dashboard.

  • Changes reach a preview or staging environment with its own database before production, and your host can put the previous version back in one click.

06 · 3 checks

Privacy and the basics

What a customer, a lawyer or an investor will ask about.

  • List every service that receives user data (database host, email, analytics, AI providers, payments) and check each one is named. In the EU, non-essential cookies wait for consent.

  • Under the GDPR and Swiss data protection law, people can ask for both. Make sure you can do it without hand-editing the database, ideally from their account settings.

  • Run your domain through securityheaders.com and npm audit on your code. Fix anything critical, and turn on Dependabot so new problems reach you.

25 left to check?

The Launch Audit checks every item on this list for you, plus what's specific to your app, and you get a written report with every issue ranked by risk and the fix. Five working days, €900CHF 900$990 fixed, and credited in full if you then book a sprint.