AI Generated Code Security: 6 Mistakes and How to Fix Them
· 6 min read · Guides
AI-generated code is not secure by default, and the app working on screen tells you nothing about whether it is safe. The same few mistakes keep showing up: open databases, secrets in the browser, and access checks that only exist in the UI. You can catch most of them in about an hour if you know where to look.
Why AI-built apps keep making the same mistakes
The model optimizes for "it runs". Security is mostly about what the code doesn't do: the check nobody asked for, the table nobody locked. The Cloud Security Alliance puts it this way: an assistant will happily ship an endpoint that accepts input without validating or authorizing it, because the prompt never said it needed to.
This isn't one tool's problem. IOActive's April 2026 whitepaper reports that no model they tested produced fully secure output, and its advice is to treat AI output as untrusted input. A separate cross-model study found that risk tracked the task type more than the model. Switching editors won't save you. Reviewing will.
Mistake 1: The database is open to anyone
This is the big one for Lovable, Bolt and any Supabase-backed app. Your front end ships a public API key. That's normal. The thing that makes it safe is Row Level Security (RLS) on every table. Without it, anyone who copies the key from your browser can read, and sometimes write, your tables.
It has caused real incidents. CVE-2025-48757 covered insufficient RLS in Lovable-generated projects. Lovable disputes the framing and says each customer is responsible for their own app's data, so the practical point stands either way: it's your job to check.
How to fix it
- In Supabase, open the Security Advisor and fix everything it flags about RLS.
- Turn RLS on for every table in an exposed schema.
- Write policies that tie rows to the signed-in user.
- Test with a second account. Zero rows returned proves nothing until another user's data exists to be leaked.
alter table public.notes enable row level security;
create policy "own rows only" on public.notes
for all to authenticated
using (user_id = (select auth.uid()))
with check (user_id = (select auth.uid()));
Watch for policies written as using (true). The lock is on, but the door is propped open. Also don't build admin checks on user-editable metadata, since users can change it themselves.
A Supabase change that lands today
Per Supabase's changelog, on October 30, 2026 the new default reaches all existing projects. New tables in public are no longer exposed to the Data API automatically, and they need an explicit grant first. Existing tables keep their current grants.
The trap: your AI agent hits a "permission denied" error and "fixes" it by granting access to anon on everything. That reopens the hole. Grant only what each role needs, and keep RLS on.
Mistake 2: Secrets in the client or the repo
Models love pasting an API key straight into the file where it's used. If that file runs in the browser, the key is public. Anything prefixed NEXT_PUBLIC_ or VITE_ ends up in your bundle.
Never put these in client code: a Supabase service_role or secret key (it bypasses RLS), OpenAI or Anthropic keys, Stripe secret keys, and database connection strings.
The ecosystem numbers are not great. A Cloud Security Alliance research note, citing GitGuardian's 2026 report, says AI-assisted commits leaked secrets at 3.2%, against a 1.5% baseline for all public commits. Treat that as one data point, but the direction matches what most of us see.
How to fix it
- Move secret calls into a server route, edge function or backend. The browser calls your endpoint, not the provider.
- Keep keys in environment variables, and add
.env*to.gitignorebefore the first commit. - Rotate any key that has ever been in the client or in git. Deleting it from the file doesn't remove it from commit history.
- Add a secrets scanner as a pre-commit hook.
Mistake 3: Access control that lives only in the UI
The dashboard only shows your projects, so it feels private. But the endpoint behind it often returns everything to anyone who asks. If the server doesn't check who is calling and whether they own the thing, hiding a button protects nothing.
The subtler version is a logic slip. The CSA gives an example where a role check gets swapped for a single-role comparison. It works for simple users and quietly over-grants for others.
The fix: check on the server, every time, that the user is logged in and owns the resource. Never trust a user ID or role sent from the client.
Mistake 4: No input validation
Generated code often trusts whatever arrives. That opens the door to injection and cross-site scripting. Security Journey lists raw string-built SQL and skipped validation among the common failures.
- Use parameterized queries or your ORM. Never build SQL with string concatenation.
- Validate request bodies with a schema library (Zod, for example) on the server.
- Don't render user input as raw HTML.
Mistake 5: Thin authentication
Hand-rolled login is where models get creative. Common problems include fast hashes like MD5 or plain SHA-256 for passwords, Math.random() for reset tokens, and no limit on login attempts.
- Use your platform's auth (Supabase Auth, Clerk, Auth.js) instead of letting the AI write its own.
- If you must store passwords, use bcrypt, scrypt or Argon2.
- Add rate limiting to login, signup and password reset.
Mistake 6: Unvetted dependencies and open previews
Agents add packages freely, sometimes outdated ones. Run your package manager's audit command and read what got added to package.json. Also check that staging and preview URLs aren't open to the public with real data behind them.
Quick reference
| Mistake | How to spot it | Fix |
|---|---|---|
| Open database | Security Advisor flags; a second account sees the first one's rows | RLS on, per-user policies |
| Leaked secrets | Keys in the bundle, VITE_/NEXT_PUBLIC_ vars, git history | Server-side calls, rotate keys |
| UI-only access control | Call the API directly with another user's ID | Server-side ownership checks |
| No validation | Weird input breaks or alters behavior | Schema validation, parameterized queries |
| Weak auth | Custom login code, no rate limits | Managed auth, rate limiting |
A one-hour audit you can run today
- Search the codebase for secrets. Look for
sk-,sk_live_,service_roleand connection strings. - Open DevTools on your live site. Check the network tab and bundled JS for anything that shouldn't be there.
- Check every table for RLS. Then test with two accounts.
- Hit your API without logging in. If data comes back, you have a problem.
- Run a dependency audit and a secrets scan.
- Ask a second model to review. Use the prompt below, then verify its findings yourself.
Paste-ready review prompt
Act as a security reviewer, not a code author. Audit this project for:
1) tables without RLS or with policies like using (true),
2) secrets or service-role keys reachable from client code,
3) API routes missing server-side auth or ownership checks,
4) unvalidated input and string-built SQL,
5) custom auth code that a managed provider should handle.
For each finding give: file and line, how an attacker would use it,
and the smallest fix. Do not change any code yet. List findings only.
The "list findings only" part matters. Review and fixing are separate steps, and you want to read the list before an agent starts editing.
Bake it into the project
Add a short security section to your rules file (CLAUDE.md, Cursor rules, or similar): RLS on every new table, no secrets in client code, validate all input on the server, use managed auth. It won't be perfect, but it moves the defaults in your favor.
A scanner is a useful second opinion, not a pass. One security write-up on Lovable said the platform's own scan confirmed policies existed without checking they were correct. "Passed the scan" and "is safe" are different things.
FAQ
Is AI-generated code less secure than human-written code?
Studies disagree on the exact rates, and I wouldn't trust any single percentage. What the research does agree on is that AI output regularly contains vulnerabilities and needs review. Humans also write insecure code. The difference is speed: an agent can ship a lot of unreviewed code in an afternoon.
Do I need to be a security expert to ship safely?
No, but you need to check the basics. Database access rules, secret handling, server-side auth checks and input validation cover most of the damage you're likely to see. Use managed auth and a managed database, and run the audit above before launch.
Can I just ask the AI to "make it secure"?
That helps a little, but it isn't enough. Vague prompts get vague fixes. Ask for specific checks, make it list findings before changing anything, and then test the results yourself with a second account and a direct API call.