Skip to content
All guides

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.

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.

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].