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 trueor 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_andsecret. Supabase's anon or publishable key and Stripe'spk_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
.envfile. 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, orconstructEventAsyncin edge functions) and rejects any request without a validStripe-Signatureheader.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.deletedandcharge.refunded, and test each one withstripe triggerfrom 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/migrationsfolder, 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 auditon 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.