Vibe coding is not secure by default, and it is not insecure by nature either: an AI-generated web app is as safe as the checks someone ran on it. The same handful of failures keeps appearing in apps built with AI builders: data readable by anyone, secret keys in the browser, rules enforced only in the interface. Each one can be tested by a non-developer in a few minutes, and each one has a follow-up prompt that fixes it.
This checklist is for founders, freelancers and small teams about to give a first real user access to an app built with an AI builder. If you are new to the method, start with our vibe coding guide. If your app uses Supabase, the details of the database side are in our guide to Supabase row level security.
Why AI-built apps fail the same way
An AI builder optimizes for an app that works on screen, and a screen that works says nothing about who else can reach the data behind it. When you ask for "a dashboard where each client sees their invoices", the model builds the screen first. Whether the database refuses to return other clients' invoices is a separate piece of work, and it only gets done if the prompt or the builder's defaults ask for it.
A public example shows the shape of the problem. In 2025, a vulnerability record (CVE-2025-48757) was published describing insufficient row level security policies in sites generated with Lovable, through 2025-04-15, which let unauthenticated visitors read or write tables. The National Vulnerability Database entry also notes that the vendor disputes the finding and states that each customer is responsible for protecting their own application data. Both statements point to the same practical conclusion: whoever publishes the app has to verify its access rules, whichever tool wrote it.
The seven failures at a glance
Most of these failures map to the OWASP Top 10, the reference list of web application risks, in its 2025 edition. The mapping below uses the category names from that list.
| Failure | What an attacker does | OWASP 2025 category | Your 10-minute test |
|---|---|---|---|
| 1. Missing row level security | Reads or edits any table through the public API | A01 Broken Access Control | Two accounts, compare what each sees |
| 2. Secret keys in the frontend | Copies the key from the browser and spends your money | A02 Security Misconfiguration | Network tab and page source |
| 3. Rules only in the interface | Calls the API directly and skips the hidden button | A01 Broken Access Control | Replay a request as the wrong role |
| 4. Admin pages reachable by URL | Types /admin in the address bar | A01 Broken Access Control | Open admin URLs logged out and as a normal user |
| 5. Unvalidated file uploads | Uploads huge, wrong or public files | A06 Insecure Design | Upload a wrong file, open its link logged out |
| 6. Outdated dependencies | Uses a known flaw in an old package | A03 Software Supply Chain Failures | Run the package audit command |
| 7. No rate limits | Floods sign-up or drains your AI credits | A06 Insecure Design | Repeat a request 50 times and see what happens |
1. Missing row level security
With a Supabase backend, the browser talks to the database through a public API, so the only thing standing between a visitor and your tables is row level security (RLS): rules attached to each table that say which rows a user may read or write. A table without RLS enabled can be read by anyone who has the public key, and that key is in the browser by design. Supabase explains the mechanism in its row level security documentation.
The test takes five minutes. Create two accounts, A and B, with different emails. As A, create a record (an invoice, a note, a project). Sign out, sign in as B, and look for A's record in every list and search. Then open the browser's devtools, go to the Network tab, reload the page, click a request to the database and read the response: it must contain only B's rows. Finally, sign out and reload the same page: the requests should return nothing or an error.
Follow-up prompt: "List every table in the database with whether row level security is enabled and which policies apply. Enable RLS on every table that has none, and write policies so a user can only select, insert, update and delete rows where user_id equals their own id. Show me the policies and explain any table you left public on purpose."
2. Secret keys in the frontend
Anything shipped to the browser is public, including every key you can find in the page source. Some keys are meant to be public (Supabase's public key, protected by RLS). Others are not: payment secret keys, AI provider keys, email service keys, the database's service role key. A visitor who copies one can send emails as you or run AI requests on your bill.
To test it, open the published app, open devtools, go to the Network tab and the Sources tab, and search for the first characters of each secret you use (for example "sk_" for a payment key) and for the name of any provider. Also search the project's code and its GitHub repository if you export it. A secret in a public repository counts as leaked even after you delete it.
Follow-up prompt: "Search the whole project for API keys, tokens and secrets in client code. Move every call that needs a secret into a server-side edge function, read the key from environment variables, and never use the service role key in the browser. List what you moved." If a secret was exposed, rotate it at the provider: deleting the code does not cancel the key.
3. Authorization checked only in the interface
Hiding a button is a design choice, not a permission. If the delete button is hidden for a "viewer" role but the database accepts the delete request anyway, any viewer with devtools can send it. Rules must be enforced where the data lives, with the interface only reflecting them.
To test it, sign in as the lowest role, perform an allowed action such as reading, and in the Network tab copy that request (right click, "Copy as fetch"). Edit it into something forbidden, such as changing another record's id or a role field, and run it in the console. If it succeeds, the rule lives only in the interface. Also check that a user cannot edit their own role or plan field.
Follow-up prompt: "For each role, list the actions it may perform on each table. Enforce them with database policies, not only in the UI. Block users from updating their own role, plan or user_id columns."
4. Admin pages reachable by URL
An admin page that is simply not linked in the menu is still reachable by anyone who guesses the address. The page must check the role before it shows anything, and the data behind it must be protected by the policies from point 1.
To test it, list your admin routes (/admin, /settings/users, /dashboard/all). Open each one in a private window while logged out, then while logged in as a normal user. You should land on a login page or an "access denied" page, and the Network tab should show no admin data in any response.
Follow-up prompt: "Protect every admin route so only users with the admin role can open it, redirect everyone else, and make sure the data queries on those pages also fail for non-admins."
5. File uploads without validation
An upload field that accepts any file, of any size, into a public folder lets strangers host content on your domain and fill your storage. Private documents (contracts, CVs, invoices) must not be readable through a guessable link.
To test it, upload a file of the wrong type (an executable renamed to .jpg) and a very large file. Then copy the link of an uploaded private document and open it in a private window. If it opens, the bucket is public.
Follow-up prompt: "Restrict uploads to these file types: [list] and a maximum size of [x MB], checked on upload. Make the bucket private, add storage policies so a user can only read their own files, and serve files through signed links that expire."
6. Outdated dependencies
An app is made of hundreds of open-source packages, and a package with a known flaw is a ready-made way in. OWASP groups this under software supply chain failures. The AI may also pick an old version of a package it learned from older examples.
The test is one command in the exported project: "npm audit" lists known vulnerabilities in the installed packages. If you cannot run it, ask the AI to do the equivalent review. Schedule it again before every major release.
Follow-up prompt: "Check the dependencies for known vulnerabilities and outdated major versions. Update the ones that have a fix, avoid breaking changes, and list what you could not update and why. Remove packages the app does not use."
7. No rate limits on sign-up and AI endpoints
Without limits, a script can create thousands of fake accounts or call your AI endpoint until your provider bill, not your user count, grows. Two places matter most: sign-up (and password reset, which sends emails) and any function that calls a paid API.
To test it, send the same sign-up or AI request many times in a row from devtools or a simple script and watch whether anything slows or blocks it. Also check that AI endpoints require a logged-in user and that a user cannot send a 10 MB prompt.
Follow-up prompt: "Require login on every edge function that calls a paid API, limit each user to [n] calls per hour, cap the input size, and return a clear error when the limit is reached. Enable email confirmation and a captcha on sign-up."
Personal data and GDPR
If your users are in Europe, the app holds personal data and you are responsible for it, whatever tool built the app. The security failures above are also the usual way personal data leaks. Beyond them, check the basics: a privacy policy that matches what the app really collects, a way to delete a user's account and data, and no personal data in logs or analytics that you did not intend to send. Our guide to GDPR and AI no-code apps covers the legal side in more detail.
Follow-up prompt: "Add an account deletion feature that removes the user's rows and files, list every personal data field we store and why, and make sure no personal data is written to logs."
What Cadrant does by default, and what it does not
Cadrant, our builder, generates React apps with a Supabase backend in your own Supabase account. By default, the AI writes the database migrations with row level security policies on per-user tables, including storage policies, and it can inspect the current schema and policies before changing them. Secret keys are not put in client code: integrations such as Stripe, OpenAI or email providers run through Supabase edge functions, with secrets stored as environment variables. Our documentation describes this in the edge functions and environment variables pages.
Defaults reduce the number of mistakes; they do not replace the test. A prompt can still ask for a public table, a model can still miss a policy on a table you add later, and no builder reviews your business rules for you. Run the two-account test after every large change, whichever tool you use.
A pre-launch checklist
When the app handles payments, health data or other regulated information, a one-time review by a security professional is worth its cost: this checklist catches the common failures, not every failure. For the build side, see our AI app builder page.