Security audit for AI-generated code
Code generated by AI usually works. It is vulnerable not because it is broken, but because the model was solving for functionality, and security was never part of that task.
In short
Most holes in AI-written code share the same cause: permissions are enforced in the interface, not on the server. That is where keys in browser code, Supabase tables without RLS policies, and other people's records exposed by changing a number in the URL come from. The audit starts with four questions: where the secrets are stored, who can read each table, whether the server checks who owns a record, and whether every installed package actually exists. The answers produce a list you can close in days, not a rewrite.
Why AI writes vulnerable code
A model answers the prompt. If the prompt says to build a login and an orders list, it builds a login and an orders list. The requirement that one user must not see someone else's orders was not in the prompt, and often is not in the code.
This has been measured. Veracode tested more than a hundred models on the same tasks: in 45% of cases, the model chose the insecure option when a secure one was available, and newer models were no better than older ones. In a Stanford study, participants using an AI assistant wrote less secure code than those without one, and were more likely to believe it was secure. The second half of that finding matters more than the first.
A vulnerability does not show in a demo. An app where anyone can read every record looks exactly like an app where nobody can. The difference is visible only to someone who deliberately tries.
Four places to look first
1. Secrets in browser code
Anything in the frontend bundle is available to every visitor through the developer tools. A payment key, an OpenAI key, or a database service key in that code means the key is already public. GitGuardian counts millions of secrets leaking to public GitHub every year, and most leaked keys keep working for years because nobody revokes them.
2. Tables without access policies
In Supabase the public key (anon, now called publishable) is designed to be visible in the browser. It is safe only if Row Level Security is enabled on every table and policies are written. The Supabase documentation says it plainly: a table in an exposed schema without RLS is readable and writable by any role that has a grant on it. The service key (service_role, now secret) bypasses every policy and must never reach the browser or the repository.
This is not theoretical. Under CVE-2025-48757, a researcher checked 1,645 projects built with Lovable and found inadequate RLS policies in 170 of them, exposing personal data, third-party API keys, and payment details. Lovable disputes the CVE, arguing that data protection is each project owner's responsibility. For the owner, the conclusion is the same whichever side you take: you have to check it yourself.
3. Permissions checked only in the interface
The delete button is hidden from users who may not delete, but the server executes the delete request for anyone. Or an order opens at /orders/1043, and changing it to 1044 opens someone else's order. This is called IDOR, and OWASP keeps broken access control at the top of its risk list: in tested applications, some form of it shows up practically every time.
4. Packages that do not exist
Models sometimes import a library with a plausible name that does not exist. Research presented at USENIX Security counted more than two hundred thousand unique hallucinated package names; for open models, the share of such references reached a fifth. An attacker only has to register the name and publish a malicious package under it. The technique is called slopsquatting.
What the audit covers
| What we check | How | What you get |
| Secrets | Search for keys in frontend code, repository history, and the build | Keys to revoke and where to move them |
| Data access | Per table: whether RLS is on, what the policies allow, and who can actually read and write | A table-by-table risk rating |
| Server-side authorization | Attempts to open and change other users' records as an ordinary user | Exact URLs and requests that let the wrong person through |
| Dependencies | Whether each package exists, known vulnerabilities, abandoned libraries | A list of replacements |
| Input | File uploads, forms, parameters that reach database queries | Places where user input reaches the database unchecked |
The audit runs on a copy or with an ordinary user's rights. Production database keys, passwords, and connection strings are not needed and will not be requested.
What to check yourself first
- Open the site, press F12, and search the sources for
key, secret, token. Treat everything you find, except the Supabase public key, as leaked.
- In the Supabase dashboard, open the table list and note which tables lack the RLS mark. Each of them is open.
- Sign in as a second user and try to open another user's record by changing the number in the URL.
- If any item worked, do not ship new features until it is closed: every new table inherits the same mistake.
Frequently asked questions
Is the Supabase public key safe in the browser?
Yes, if Row Level Security is enabled on every table and the policies let users see only their own rows. Without RLS, the same key gives read and write access to the whole table. The service_role key is never safe in the browser.
Doesn't the AI assistant check security itself?
It checks what it was asked to check. Research shows that without an explicit requirement, a model often chooses the insecure option, and a developer working with an assistant tends to trust the result more. So the audit has to be separate and have a different goal: not to build, but to break.
What is IDOR in plain words?
A record opens by its number, and the server does not check whether it is yours. Change the number and you see someone else's order, invoice, or profile. It is common in AI-written code because the interface simply does not show links to other people's records, and that feels like enough.
Do you need access to the production database?
No. We work on a copy or with an ordinary user's rights, the way an outsider would. Production keys, passwords, and connection strings are not needed for the audit.
How long does fixing take?
Usually, it is not a rewrite. RLS policies, moving keys to the server, and checking record ownership in the API are targeted fixes. The time depends on the number of tables and the places where the server trusts the interface; after the audit, it is known item by item.
SLAtech LTD