AIList
daLæs på dansk12 Vibe Coding Security Issues (and How to Spot Them)
Vibe coding security issues explained for founders: 12 common holes in Lovable, Bolt and Cursor apps, what each one risks, and how to check for it.

Freelance full-stack developer
- Published
- Reading time
- 14 min
In this post9
Most vibe coding security issues come down to three mistakes: secret keys shipped to the browser, data that anyone can read, and a server that believes whatever it's sent. None of them need a skilled attacker to exploit, and you can check several yourself without reading a line of code.
This list is for founders and product owners who built an app with Lovable, Bolt, v0, Replit or Cursor and are about to let real users in. I'm a freelance developer based in Denmark, and reviewing and maintaining apps like these is part of what I do, so weigh my advice on when to bring in help with that in mind.
The short answer
Here are the twelve holes at a glance. Severity is my own judgment of the typical damage once the app has real users and real data.
| What can go wrong | Severity | Can you check it yourself? | |
|---|---|---|---|
| 1. API keys in the frontend | Strangers use your paid accounts, such as OpenAI | High | Yes |
| 2. Database admin key in the browser | Anyone can read, change and delete everything | Critical | Yes |
| 3. Secrets in Git history | Old keys keep working after the file is deleted | High | Partly |
| 4. Open database tables | All data can be downloaded without logging in | Critical | Partly |
| 5. UI-only access control | Admin actions can be called directly | Critical | Yes |
| 6. Other users' data one ID away | Customers see each other's invoices and profiles | Critical | Yes |
| 7. Self-granted roles | Free users make themselves admin or paid | High | No |
| 8. Server trusts the browser | Prices, discounts and payment status can be faked | High | Partly |
| 9. User content that runs as code | Attacker scripts run in your users' browsers | High | No |
| 10. Unrestricted file uploads | Private documents become public | Medium | Partly |
| 11. Hallucinated or outdated packages | Malicious code arrives through dependencies | High | No |
| 12. No limits on abuse | Surprise bills and hijacked accounts | Medium | No |
My rule of thumb: fix everything marked critical first. Those four are all about who can reach your data, and they cause the most harm. If you're getting a prototype ready for launch, my guide to taking a vibe-coded app to production covers the whole path from prototype to a live product. This list is the security part of it.
Leaked keys and secrets
An API key is a password your app uses to talk to other services, such as OpenAI, Stripe or your database. The trouble starts when that password ends up somewhere other people can read it.
1. API keys in the frontend
AI tools take the shortest route to something that works. If the app needs to call OpenAI, the call often goes straight into the frontend code, the part that runs in the visitor's browser. It works immediately, but the key ships to every visitor. Anyone with a few minutes and the browser's developer tools can copy it and use your account on your bill.
How to check: open your live app, right-click and open the developer tools, then search the source for sk- (how OpenAI keys usually start) and sk_live (Stripe secret keys). A key starting with pk_ is Stripe's publishable key and is meant to be public.
The fix is to move the call to your server or an edge function (a small piece of code that runs server-side), issue a new key and set a spending cap with the provider.
2. The database admin key in the browser
Supabase and Firebase both separate a public key, which is fine in the browser, from an admin key that skips every rule. Supabase's documentation on API keys says the public key only reaches what your access rules allow, and that a secret key should never go in a browser, a shipped app or source control.
When an AI assistant hits a "permission denied" error, swapping in the admin key is a tempting shortcut. The error disappears, and so does your protection.
How to check: search the source the same way for sb_secret_ (Supabase's newer secret keys) and service_role (the name of the legacy admin key). If you find the admin key, assume every row could have been read until you can show otherwise.
3. Secrets in Git history
Git stores every version of your code, and a repository remembers everything. If a .env file with keys was committed even once, those keys live on in the history after the file is deleted. If the repository is public on GitHub or shared with anyone else, treat the keys as leaked.
Deleting the file doesn't help. Rotate every key with each provider and disable the old ones, then add .env to .gitignore so it can't happen again. GitHub can flag some known key formats, but don't rely on it to catch everything.
Broken access control: who can see and change what
Access control is the set of rules that decides which data each user can see and change. Broken access control sits at number one on the OWASP Top 10, the standard reference list of critical web app risks. It's also where I start when I assess whether an app is ready for real users.
4. Open database tables
Apps built on Supabase often query the database straight from the browser. That's only safe if every table has Row Level Security (RLS) policies, rules such as "a user can only read their own rows". Without them, or with policies that allow everything, anyone holding the public key can download the whole table.
This has happened at scale. In 2025, researchers found 303 vulnerable endpoints across 170 of the 1,645 Lovable projects they examined, exposing emails, phone numbers, payment details and API keys (the researchers' statement on CVE-2025-48757). They also noted that Lovable's own security scanner only checked whether policies existed, not whether they worked. I've put the Supabase-specific checks in a separate Lovable and Supabase security checklist.
5. Access control that only lives in the UI
A familiar pattern: the admin page only shows up for admins, and the "Delete user" button is hidden from everyone else. But the server or database behind that button never checks who's asking. AI tools build what you ask to see, and what you see is the interface.
How to check: log in as a regular user and type the admin page's address straight into the address bar. If you get in, you have a hole. Unfortunately, the reverse proves nothing: the page can be locked while the requests behind it still answer.
6. Other users' data, one ID away
Many apps load records through addresses like /invoices/1043. If the server only checks that you're logged in, and not that invoice 1043 belongs to you, changing the number to 1042 shows someone else's invoice. Security people call this an IDOR (insecure direct object reference), and it's a common way customer data leaks.
How to check: create two test accounts, A and B. Create something as A, copy its address, log in as B and open it. B should see nothing.
7. Users who can grant themselves roles
This one is hard to spot without reading the code. A user's role or subscription plan often sits in the same table as their name and avatar, in a field like role or plan. If users are allowed to update their own row, they can usually update those fields too, and one request from the browser turns a free account into an admin or a paying one.
The fix is that roles and plans can only be changed by the server, for example when your payment provider confirms a payment, never by the user.
Trusting the browser: input, output and files
Everything a user sends to your app, and everything your app shows to other users, is a possible way in. These three holes are about making the server do its own checking.
8. A server that trusts whatever it's sent
Anything that comes from the browser can be changed by the user. Form validation, like requiring a valid email, helps honest users but stops no one else. Vibe-coded apps often send the price, discount or quantity from the browser to checkout, and the server uses those values as-is.
Payment status is the most dangerous example. If the app marks an order as paid because the user landed on a "thanks for your order" page, anyone can skip paying. Payments should be confirmed by a webhook (a message sent from Stripe, say, straight to your server), and the server must verify the webhook's signature so nobody else can send a fake one.
9. User content that runs as code
When users write text that others see, such as comments, display names or messages, it has to be shown as text. If it's inserted as HTML instead, a user can plant a script that runs in other people's browsers and, for example, steals their session. This is cross-site scripting (XSS). In Veracode's tests across more than 100 language models, the generated code failed to defend against XSS in 86% of relevant samples (Veracode's 2025 GenAI Code Security Report).
The same family includes database queries built by pasting user input into them (SQL injection) and AI features where user text can give the model new instructions (prompt injection). That last one matters most if your AI feature can look up data or take actions on a user's behalf.
10. Unrestricted file uploads
Image and document uploads are often set up with a public storage bucket, because that's the quickest way to make them work. Anyone with the link can then open the files. That's fine for avatars, not for contracts, payslips or photos of passports.
Check for limits on file type and size too. Without them, strangers can fill your storage at your expense or upload file types that can carry code, such as SVG and HTML.
How to check: upload a file as a test user, copy its link and open it in a private browser window while logged out.
Abuse and supply chain risks
The last two holes don't live in any single feature. They're about what happens around your app once it's running.
11. Hallucinated or outdated packages
Modern apps are built on hundreds of ready-made code packages, and AI models sometimes invent package names that sound plausible. A study presented at USENIX Security 2025 found that on average at least 5.2% of packages suggested by commercial models, and 21.7% from open-source models, didn't exist (the package hallucination study). That opens the door for attackers to publish malicious code under those invented names, an attack that has been nicknamed slopsquatting.
The duller version is outdated packages with known vulnerabilities. The AI may pick a version it remembers from its training data, and nobody updates it afterwards. The fix is automated alerts, such as GitHub's Dependabot or npm audit, plus a regular update routine as part of ongoing web application maintenance.
12. No limits on abuse
Rate limiting, capping how many attempts someone gets, usually isn't there unless you ask for it. Without it, a bot can guess passwords on your login page indefinitely, create thousands of accounts, or hammer your AI feature until the model provider's invoice lands in your inbox.
Then there are error messages that say too much. An error page that reveals database details or server paths hands an attacker a map of your system. Users should see a friendly message, and the details belong in logs only you can read.
Why AI tools keep making the same mistakes
These tools are very good at producing something that works. Security is exactly what you can't see when something works: an app with an open database looks identical to one with a locked database. I see three reasons the same holes keep appearing:
- The tool is trying to make the error go away. When a rule blocks a request, the quickest fix is to loosen or delete the rule.
- Your prompt rarely mentions security. You ask for "a page where customers see their invoices", not "only their own invoices".
- Every new prompt can rewrite old code. A rule that was correct yesterday can be quietly changed today.
The same Veracode report found that 45% of code samples from more than 100 models introduced OWASP Top 10 vulnerabilities, and that newer, larger models did no better on security. Waiting for the next model won't solve this for you.
If every fix you prompt seems to break something else, that's one of the signs it's time to hire a developer for your AI-built app.
When you can handle it yourself (and when you can't)
Not every vibe-coded app needs a developer right away. Here's my honest take:
- You can handle it yourself if the app is a prototype with test data, an internal demo, or a tool with no personal data and no payments. Use this list as a checklist, test holes 1, 2, 5 and 6 yourself, and save your budget for later.
- You should get a developer to look once real users, personal data or payments are involved. If your company is in the EU, or you offer the app to people there, GDPR applies as well. The same goes if a business customer starts asking security questions during procurement.
- You need someone other than me if you require a formal penetration test, or evidence for a certification or a customer's security requirements. That's a job for a specialist security firm. A developer like me can close the holes and get the code ready, but the independent test should come from someone who didn't write the code.
If you find many of these holes at once, ask yourself whether the app should be repaired or rebuilt. My guide on whether to rewrite or refactor a vibe-coded app walks through that decision.
Next steps: fix them in the right order
Order matters. Some holes are open doors right now, while others take deliberate effort to exploit.
- Rotate every key that has been in the browser or in Git, and set spending caps with your providers.
- Test access control with two accounts (holes 4 to 7) and close the critical findings first.
- Move prices, roles and payment status to the server (holes 7 and 8).
- Turn on automated alerts for dependencies and errors.
- Re-run your checks after every major change, because the AI tool can rewrite your rules without telling you.
If you want a developer to review the code and keep it secure after launch, see how I handle maintenance and ongoing development for existing apps. If the app is still a prototype that needs to be made ready for real users, the AI app to production service is the better fit.
Frequently asked questions
Can I just ask the AI to fix the security issues?
Partly. An AI tool can fix a specific hole if you describe it precisely, for example that a given table needs policies so users only see their own rows. What it can't do is prove the fix works, and it will often report that everything is fine when it isn't. Use AI to make the change, then verify it yourself with two test accounts or have a developer confirm it.
Do I have to report a security hole under GDPR?
Only if personal data was actually exposed in a way that counts as a breach. In that case, Article 33 of the GDPR requires you to notify your supervisory authority without undue delay and, where feasible, within 72 hours, unless the breach is unlikely to put people at risk. GDPR can also apply to companies outside the EU that serve people in the EU. This isn't legal advice, so check with a qualified adviser.
Is my app too small to be a target?
No. Attacks on small apps are usually not personal. Automated scripts scan the internet for leaked keys, open databases and login pages without rate limits, and they don't care if you have 20 users or 20,000. An exposed database with a handful of users is still a serious problem if it holds personal data.
Are some AI app builders safer than others?
The tool matters less than whether anyone checks the result. Hosted builders such as Lovable add some guardrails, like a built-in security scan, but a scan can confirm that rules exist without knowing whether they fit your app. Editors such as Cursor write code into your own stack, so security depends entirely on how that code is reviewed. Either way, testing with two accounts is the minimum.