Is my Lovable app secure enough to launch?
Probably not yet, and the gap is usually in four specific places rather than everywhere. Apps generated by Lovable and similar tools most often ship with database rules that were never enabled, keys that are visible in the browser, permission checks that live only in the interface, and dependencies nobody has looked at. All four are checkable in under an hour, and this guide shows you how.
Lovable is genuinely good at turning a description into a working application. What it does not do, and does not claim to do, is take responsibility for what happens when strangers start using that application. That gap is not a criticism of the tool. It is the same gap that exists between a framework generating a project and that project being ready for production.
The useful question is therefore not whether AI wrote your code. It is whether the four things that actually get small applications breached have been dealt with. In our experience reviewing AI-built projects, the same four keep appearing, and they appear together because they share a root cause: the generator produced something that works when you are the only user.
1. Row level security that was never switched on
Most Lovable applications use Supabase, and Supabase tables are readable by anyone holding the public key unless row level security is enabled and a policy is written. During development this is invisible, because you are logged in as yourself and the data looks correctly scoped. In production it means any visitor can read every row in the table.
This is the single most common serious finding in AI-built applications, and it is not subtle when it goes wrong. It is the difference between a private customer list and a public one.
How to check it yourself
- Open your Supabase dashboard and look at the table list. Any table without row level security enabled is readable by the public key.
- For each table that does have it enabled, read the policies. A policy of true, or one that only checks that a user is authenticated, does not scope data to its owner.
- Test it the way an attacker would: open a private browser window, and with only your public key, request a table you believe is protected. If rows come back that are not yours, the policy is wrong.
The fix is a policy that compares the row's owner column to the requesting user, applied per table and per operation. Enabling row level security without writing a policy denies everything, which is safe but breaks the app, so both steps belong together.
2. Keys that are sitting in the browser
There are two kinds of key in a typical Supabase project. The public one is designed to be in the browser and is safe there, provided the previous section is done properly. The service role key is designed to bypass every rule you have written, and it must never leave your server.
The failure happens when a generated integration, or a later change made to fix something quickly, puts the service role key into client code. It is then in the JavaScript bundle that every visitor downloads, and holding it grants complete read and write access to the database regardless of any policy.
How to check it yourself
- Open your deployed site, open developer tools, and search the loaded sources for the word service_role.
- Search your repository for the same term, including files that are committed but not currently imported.
- Check your environment variable names. In most frameworks a prefix such as NEXT_PUBLIC or VITE means the value is deliberately exposed to the browser, so a secret carrying that prefix is a secret no longer.
If a service role key was ever exposed, rotate it. Removing it from the code does not undo the exposure, because anyone who fetched the bundle already has the old value.
3. Permission checks that only exist in the interface
Generated applications tend to enforce rules where the rules are visible: the admin button renders only for admins, the edit form appears only for the owner. This looks correct because the interface behaves correctly. It is not a control, because the interface is not where requests come from.
Anyone can send a request directly to your API without ever loading your interface. If the only thing standing between a normal user and someone else's record is a hidden button, there is nothing standing there at all. This is what the OWASP API Security list calls broken object level authorization, and it remains the most exploited API weakness in practice.
How to check it yourself
Take an identifier belonging to another account. Sign in as an ordinary user, and send a direct request for that record, then an update to it. If either succeeds, ownership is not being enforced on the server. Repeat for anything that changes state: deletes, role changes, payment-related endpoints.
4. Dependencies nobody has looked at
A generated project can pull in a large dependency tree in a single step, and known vulnerabilities in those packages are public by definition. This is the least interesting failure mode and the easiest to fix, which is exactly why it is worth doing before launch rather than after.
Run your package manager's audit command, and treat anything critical or high as a launch blocker. Most will be resolved by a version bump. The ones that are not are worth understanding, because a vulnerability with no fix available is a decision, not an oversight.
The things this check does not cover
Being honest about the boundary matters more than sounding thorough. The four checks above address the failures we see most often, and they are not a complete security review. They do not cover business logic that is wrong in a way only you would recognise, they do not cover your hosting and account security, and they do not tell you whether your application stays up under load.
If you are about to take payments, handle health or financial data, or sign an enterprise contract, you need more than a pre-launch check. What this gives you is the floor, and most applications that get breached were below it.
Turning the answer into something you can show people
Working through this list yourself is genuinely the right move, and if you stop here you will still be in better shape than most. The reason people ask us to do it instead is usually not the checking. It is that a claim about your own security carries no weight with an investor, a customer's procurement team, or an accelerator reviewing its batch.
StackAttest tests these controls against named standards and produces a Passport: a page you can share, showing what was tested, what the evidence was, and how strong that evidence is. The score is deterministic, so it does not change based on who is asking.
Frequently asked questions
Is Lovable itself insecure?
No. Lovable generates working applications quickly, which is what it is for. The gap is between working and production ready, and it exists for hand-written code too. What is different about generated code is that the defaults are accepted more often, because nobody chose them deliberately.
Does Lovable's own security scan cover this?
It catches some of it, and it is worth running. Two things it does not do are test the way an attacker would from outside your session, and give you evidence you can share with someone who does not trust your word for it.
How long does this check take?
Under an hour for a typical application if nothing is wrong, and rather longer if the row level security section turns up problems, because writing correct policies per table is real work.
I am not technical. Can I still do this?
The key exposure check and the dependency audit, yes. The row level security policies and the ownership testing genuinely need someone who can read the code, and this is the point where an automated validation earns its cost.