Is my AI-built app secure? A checklist for Lovable, Bolt and Cursor apps
By Obscura Security · Updated · Lovable, Bolt, Cursor, v0, Claude, Supabase, Firebase, Next.js, Stripe
Short answer
Not necessarily. AI tools build apps that work, not apps that are locked down, and the same handful of mistakes turn up again and again: open database rules, keys in the frontend, missing access checks, unverified payments. Work through the 28 checks below on your own app. Most can be tested in a few minutes.
Apps built with Lovable, Bolt, Cursor, v0 or Claude usually look finished long before they are safe. The checks below cover the mistakes that show up most often. Each has a short test you run against your own app, and a link to a full fix guide.
Where to start: items 1 to 5 can expose every user’s data at once. Do those first.
Your data
1. Database access rules are on
Supabase tables need Row Level Security enabled, with policies that say who can read and write. Check: in the Supabase dashboard, open the Security Advisor and look for “RLS Disabled in Public”. See Supabase RLS disabled. Database functions can skip RLS too: see Supabase security definer functions.
2. Firebase rules aren’t open to everyone
Firestore and Realtime Database rules like allow read, write: if true let anyone read and write everything. Check: open the Rules tab for each database and storage bucket and read every line. See Firebase security rules open. The Firebase apiKey in your web config being public is normal: see is the Firebase API key safe to expose.
3. Storage buckets aren’t public by accident
A public bucket serves every file in it to anyone with the URL. Check: list your buckets and look for ones marked public that hold invoices, IDs, uploads or exports. See Supabase storage bucket public.
4. Users can only see their own data
Logging in isn’t the same as being allowed to see a record. Check: sign in as user A, copy a record’s ID from the URL or network request, then request it as user B. See users can see other users’ data.
5. The admin area is protected on the server
Hiding an admin button or page in the interface protects nothing, because the API behind it is still reachable. Check: call an admin API route as a normal user and confirm it is refused. See admin page only hidden in the UI.
6. Users can’t change fields they shouldn’t
An update route that saves the whole request body lets a user set their own role, credits or plan. Check: edit your profile with the Network tab open, then resend the request with an extra field such as "role": "admin". See users can edit fields they shouldn’t.
7. Every server action and API route checks who is calling
Next.js Server Actions and Route Handlers are public endpoints, even when only an admin page shows the button. Check: list every "use server" function and API route and confirm each one checks the user. See Next.js server actions are public.
Keys and secrets
8. No secret keys in the frontend
Anything in your browser code can be read by every visitor, including the Supabase service role key, Stripe secret keys, and OpenAI or Anthropic keys. Check: search your built JavaScript (the Sources tab in developer tools) for sk_, service_role, sk- and secret. See secret API key in frontend and Supabase service role key exposed.
9. No secrets in your Git history
Deleting a committed .env file doesn’t remove it from the repository’s history. Check: look at GitHub’s secret scanning alerts, or run a scanner such as gitleaks on your own repo. See secrets committed to GitHub.
10. .env and .git aren’t downloadable
Request /.env and /.git/HEAD on your live site and check that you don’t get real content back. See env file or git folder public.
Logins and abuse
11. Server code verifies the session
In Supabase, getSession() on the server trusts whatever is in the cookie. Check: search your server code, middleware and server components for getSession(. See Supabase getSession vs getUser.
12. Rate limits on login, resets and AI routes
Without them, someone can guess passwords, spam reset emails or run up your OpenAI bill. Check: call the route 30 times in a loop and see whether it ever answers 429. See no rate limit on login or AI endpoint.
13. Sign-ups need a confirmed email
If anyone can create an account with any address and no confirmation, people can register with emails that aren’t theirs and fill your user list with fakes. Check: sign up with an address you control and see whether you can use the app before confirming. See sign-up without email verification.
14. Password reset links expire and work once
Home-made reset flows often use guessable or never-expiring tokens. Check: request a reset, use the link, then try the same link again. See password reset tokens that don’t expire.
Payments
15. Stripe webhooks verify the signature
Otherwise anyone can send a fake “payment completed” message to your webhook. Check: search your code for constructEvent and send an unsigned test request. See Stripe webhook signature not verified.
16. The server decides the price
If the browser sends the amount or the discount, buyers can change it. Check: in the Network tab, look at what your checkout request sends. See trusting prices from the browser.
Code and input
17. User content is shown as text, not HTML
Comments, profile fields and AI output rendered as HTML can run someone else’s script in your users’ browsers. Check: put <b>test</b> in a text field and see whether it shows up bold. See user content rendered as HTML.
18. Database queries use parameters
SQL built by joining strings lets user input rewrite the query. Check: search your code for $queryRawUnsafe, $executeRawUnsafe and sql.raw. See SQL injection in raw queries.
19. Uploads are checked on the server
An upload that accepts any file lets people host HTML on your domain and fill your storage. Check: try uploading a .html file and a very large file to your own app. See file upload accepts anything.
AI features
20. Your AI feature can’t be talked into misusing its access
If your app has a chatbot or agent, text from users, web pages or documents can instruct it. Check: list what each AI tool call can read and change, and whether it runs with the user’s permissions or yours. See prompt injection in AI features.
21. “Fetch this URL” features can’t reach your internal network
Link previews, imports and AI browsing that fetch any URL can be pointed at addresses only your server can reach. Check: find every place your server fetches a URL a user supplied. See URL fetch features and SSRF.
22. Every package the AI added is real
AI tools sometimes suggest packages that don’t exist, and attackers register those names. Check: look up each unfamiliar dependency on npmjs.com before installing it. See AI-suggested package doesn’t exist.
Your server and site
23. CORS allows only your own origins
Access-Control-Allow-Origin: *, or an origin copied from the request, can let other sites read your API. Check: run curl -i against your API with a fake Origin header. See CORS allows any origin.
24. Security headers are set
Headers like CSP, HSTS and X-Content-Type-Options give you a second layer of protection. Check: run curl -I https://YOUR-SITE.com. See missing security headers.
25. Cron, seed and debug routes are locked
Routes like /api/cron/..., /api/seed or /api/test can be called by anyone unless they check a secret. Check: request each one on your live site with no headers. See unprotected cron and internal endpoints.
26. Preview deployments aren’t open with production data
Every branch on Vercel or Netlify gets its own URL. Check: open a preview link in a private window, see whether it loads, and find out which database it uses. See Vercel preview deployments public.
Habits
27. Dependencies are free of known vulnerabilities
AI tools install packages freely, and some have published security advisories. Check: run npm audit in your project. Update the packages it flags, and be careful with npm audit fix --force, which can make breaking changes. Turning on Dependabot alerts in GitHub keeps this going.
28. Errors don’t reveal your internals, and AI-written code gets reviewed
Stack traces, SQL errors and file paths shown to users hand an attacker a map. Check: trigger an error on purpose, such as a bad ID in a URL, and see what the page and the API response say. Make sure you run production builds in production mode, and return a plain “something went wrong” message.
The bigger habit is reading what the AI changes. Skim every new route and every database change before it goes live, ask the AI to explain what each one allows, and test with two separate accounts before you launch.
Common questions
- Can I just ask the AI to check its own code for security problems?
- It helps, and it will catch some things. But it often misses problems that depend on settings outside the code, such as a database rule set in a dashboard, and it can confidently report that something is fixed when it isn't. Test the result yourself, using the checks on this page.
- Are apps built with Lovable or Bolt less secure than hand-written ones?
- They make the same kinds of mistakes as any app. The difference is that the person shipping the code often hasn't read it, so a mistake can go live without anyone noticing.
- Which problems matter most?
- Database access rules (Supabase RLS or Firebase rules), secret keys in the frontend and missing per-user access checks. These can expose all of your users' data with no hacking skill needed, so check them first.
- How often should I run through this list?
- Before you launch, and again after any large change made by an AI tool, especially a new table, a new API route or a new payment flow.
- Does passing this checklist mean my app is secure?
- It means you've ruled out the common, well-known mistakes. It can't find flaws specific to your app's logic, such as a discount that can be applied twice. If your app holds sensitive data or takes payments, have someone independent test it.
Read next
Written by Obscura Security, an Australian practice that checks web apps for problems like this one. If you want a second pair of eyes on yours, email [email protected].