- Published on
Is Your AI-Generated App Secure? A Founder's Checklist

- Beka Makharoblishvili
- Founder and Product Engineer
Most security advice for AI-built apps is written for people who already know what a security review looks like. That is not much use if you built the thing with prompts and the word "middleware" is doing no work in your head.
So this is the version you can actually run. Every item below is something you can check yourself, in a browser, in under an hour total. Where a check needs a developer, it says so.
The framing that matters: you are not looking for bad code. The code is probably fine. You are looking for things that were never specified, because an app builder implements what you asked for and has no opinion about what you forgot.
Check 1: can one user read another user's data
This is the check that matters most, and it takes five minutes.
Create two accounts in your own app. Log in as the first, do something that creates a record, and look at the URL. If you see an id anywhere, something like /orders/1041 or ?project=88, note it.
Now log in as the second account and put the first account's id in the URL.
If you see the first user's data, stop reading and go fix it. That is a live data leak, and it is the single most common serious flaw in generated applications. It happens because "show the record with this id" is a complete and correct implementation of the request, and "but only if it belongs to them" was never part of the request.
Try the same trick on anything that lists records, edits them, or deletes them. Reading someone else's invoice is bad. Deleting it is worse.
Check 2: is your database enforcing anything
If your app uses Supabase, and most Lovable and Bolt apps do, your browser talks to the database more or less directly. That is a deliberate design and it works, but it only works if the database itself decides who may read what. That mechanism is called row level security, RLS.
RLS is off by default when a table is created. With it off, the protection you think you have from your app's screens does not exist, because the screens are not the only way in.
In the Supabase dashboard, open the table editor and look at each table. You want to see RLS enabled everywhere that holds user data. Then open the policies for each table and look at what they actually say.
Three things to look for, and you can eyeball these without being a developer:
- A table with RLS enabled and no policies blocks everything. Safe, but your app will be broken, so this usually is not what you have.
- A policy whose condition is
trueallows everything. This is the dangerous one, because the table looks configured. If you seeusing (true)on a table with user data, that table is readable by any logged in user. - A policy that compares something like
auth.uid()to a user id column is the shape you want.
You also want separate policies for reading, creating, updating and deleting. A common pattern is a correct read policy and nothing restricting deletes.
If your app is not on Supabase, the equivalent question is: does the server check ownership on every query, or does it trust the id it was given? Ask whoever can read the code.
Check 3: read your own JavaScript
Everything your browser downloads, anyone can read. There is no such thing as a secret in frontend code.
Open your app, open developer tools, go to the network tab, reload, and look at the JavaScript files. Search them for the word key, and for sk_, and for service_role.
What you may safely find there:
- A Supabase anon key. This one is designed to be public, and it is only safe because RLS is doing the real work, which is why check 2 comes first.
- A Stripe publishable key, which starts with
pk_.
What must never be there:
- A Supabase service role key. It ignores RLS completely. If it has ever been in client code, rotate it now and assume it leaked.
- A Stripe secret key, starting with
sk_. - An OpenAI, Anthropic, or other AI provider key. Someone will find it and spend your budget. This happens routinely and the bills are not small.
- Email, SMS, or cloud storage credentials.
If any of those are present, the fix is not to hide them better. The fix is to move the call that uses them to a server route, and rotate the key, because the old one is public forever.
Check 4: does logging out actually stop anything
Log out. Then paste a URL that only a logged in user should reach.
Better, if you can see them: find one of the addresses your app calls for data, usually visible in the network tab as something under /api/, and open it directly while logged out.
If data comes back, your protection is in the interface rather than in the server, which means it is decorative. Hiding a page from someone is not the same as refusing to serve it to them.
The same applies to anything role based. If your app has admin features, make an ordinary account and try to reach the admin endpoints directly. A hidden button is not a lock.
Check 5: if you take money, three specific things
Skip this if you are not charging yet.
Are webhooks verified? Stripe signs the messages it sends your app. If your code does not check that signature, anyone can send a fake "payment succeeded" message and be granted whatever paying gets them. Ask your developer, or search the code for constructEvent or webhook secret. If neither appears, it is probably not verified.
Does the server decide the price? If the amount being charged is sent from the browser, customers can change it. The browser should say which product, and the server should say how much it costs.
Does a repeated message cause a repeated grant? Stripe retries delivery. If a retry grants the credits again, people will notice.
Check 6: what happens when things go wrong
Not strictly security, but the same root cause: nobody specified it.
Turn off your wifi mid action, or let an AI feature run on nonsense input. Does the app show a sensible message, or does the screen go blank? A white screen means there is no error boundary, and it means users hit a dead end with no path forward.
Then ask a question that has an uncomfortable answer for most prototypes: if the app broke right now, how would you find out? If the honest answer is "a user would tell me," you have no error tracking and no uptime monitoring. Both take about an hour to add and both change the situation from hoping to knowing.
Check 7: the AI features themselves
If your app calls a language model, two additional risks apply that traditional apps do not have.
Cost. If any visitor can trigger a model call, your spending is set by whoever is most persistent. You want a per user limit and an overall ceiling that shuts things off rather than scaling into a bill you cannot pay.
Input. If user text is passed into a prompt, users can write instructions to the model rather than content for it. What that can achieve depends entirely on what the model is then allowed to do. If it only writes text back to the same user, the blast radius is small. If it can query your database or call other services, the blast radius is your database.
The mitigation is not clever prompt wording. It is limiting what the model is permitted to touch.
Scoring yourself honestly
Count the checks where you found a problem.
Zero. Genuinely unusual. Get a second pair of eyes anyway, because check 2 in particular is easy to believe you have passed.
One or two, none of them checks 1 to 3. You are in reasonable shape. Fix them in order and launch.
Anything in checks 1, 2, or 3. Do not launch yet. Those three are the ones that expose data or leak credentials, and they are not the sort of thing you can patch after an incident, because the data is already gone and the key is already public.
Four or more. This is normal and not a judgment on you. It is the expected state of an app built by describing what it should do rather than what it should refuse to do. It is a few weeks of work, not a rebuild.
What to do next
The important thing is that you now know which situation you are in, which is more than most people launching a prototype can say.
If you want a second opinion in writing, we run a free teardown: send us the app and we send back what is genuinely wrong with it, at no cost. If you want the full verdict, the Production-Readiness Audit returns a written report in 48 hours covering security, auth, payments, and data handling, with a fixed quote to fix what is broken and a 90 day plan. It is credited back if you book a sprint within a week.
For the wider picture beyond security, including performance, failure handling, and deployment, see taking your Lovable app to production.