The short answer
Before an AI-built app takes real users, real data or real money, check that every request is authorized on the server for the specific record it touches; that database access rules (row-level security, if you use Supabase) are on for every exposed table; that no secret key reaches the browser; that payment webhooks are verified and processed once; and that you can restore a backup. Those five cover the failures that cost the most. The full checklist below has eighteen items. For each one it says what to check, why it matters, how to verify it safely, the warning sign to look for and the official documentation behind it.
Nothing here assumes your app is insecure because an AI tool wrote it. Many generated apps get several of these right. The aim is to know which ones, before a customer or a stranger finds out for you.
Prototype, test environment or production: which one do you have?
Most of the risk sits in the gap between three different things that can all look like “the app works”. A prototype that appears to work does what it should for the person who built it, on sample data. A working test environment has its own database, test keys and several test accounts, so you can try things without touching anything real. A production system holds real people's data and money, and has to keep working when someone uses it in a way nobody planned. This checklist is written for the step from the second to the third. If you only have the first, set up the second before you work through it.
- Prototype that appears to work: it works for the person who built it, with sample data or none, keys and settings left from the builder, and rules mostly enforced in the interface.
- Working test environment: its own database and test API keys, several test accounts with different roles, payments in test mode, and schema changes applied from migration files.
- Production system with real users, data and money: access enforced on the server and in the database, secrets kept server-side with live keys only here, backups with a tested restore, and errors reported to someone who acts on them.
What the AI tools say about their own checks
The platforms are clear that their checks are a starting point. Lovable's documentation describes a Quick scan that runs automatically every time you publish and looks, among other things, for tables without row-level security. The same page says these tools do not replace a thorough security review and that you are responsible for ensuring that your app meets the security requirements appropriate for its use case. Replit publishes its own security checklist for vibe-coded apps, covering secrets, authorization, rate limiting and dependencies. Supabase puts it the same way for the database: running it is a shared responsibility between you and Supabase, and access management, data and security controls stay with you.
For context only: a 2026 preprint by Deng, Fan and Meng (arXiv:2606.23130, not peer reviewed as far as the arXiv page shows) audited 200 publicly deployed applications built with Claude Code and Lovable and reported that 91.0% of audited applications contain at least one vulnerability, concentrated in broken access control, injection and authentication failures. That is one research team's sample of 200 apps. It does not establish a rate for AI-built apps in general and says nothing about yours, which is why the rest of this guide is about checking.
How to use this checklist safely
- Run every check on an app you own, in your test environment, with test accounts and test-mode payment keys.
- None of the self-checks needs a security tool. Two test accounts, the browser's developer tools (the Network tab) and your providers' dashboards are enough.
- Do not load-test or probe production yourself. If a check suggests real data is exposed in production, treat it as an incident: close the gap, rotate any exposed keys and have an engineer check the logs for access.
- The references are to official documentation. OWASP's Application Security Verification Standard is the long-form version of most of these items if you want the full requirement list.
Who can get in, and what they can see
1. Authentication
- What to check
- Sign-up, login, password reset and logout use the platform's auth service or an established library, not hand-written code. Logging out ends the session. Your own admin accounts on the hosting, database and code platforms use multi-factor authentication.
- Why it matters
- OWASP's Top 10 lists missing or ineffective multi-factor authentication and sessions that are not invalidated during logout or a period of inactivity among authentication failures. Replit's checklist says that if you build custom auth, use established libraries.
- How to verify safely
- Log in, copy the URL of a private page, log out and paste it back: you should land on the login page. Request a password reset for an address that has an account and for one that does not; OWASP recommends using the same messages for all outcomes. Supabase's production checklist asks you to protect your Supabase Account with multi-factor authentication (MFA); do the same for GitHub and your host.
- Warning sign
- Passwords checked in front-end code, an “admin” flag stored where the browser can change it, or different reset messages that reveal who has an account.
- Reference
- OWASP Top 10:2025 A07 Authentication Failures; Replit security checklist.
2. Authorization on the server, per record
- What to check
- Every request that reads or changes a record checks, on the server, that this user may do this to this record. Hiding a button or a menu item is not a check.
- Why it matters
- OWASP: access control is only effective when implemented in trusted server-side code or serverless APIs. Its first listed failure pattern includes viewing or editing someone else's account by providing its unique identifier. In Next.js, a Server Action is reachable via a direct POST request, not just through your application's UI, and the docs say to verify authentication and authorization inside each one.
- How to verify safely
- Create two test accounts, A and B. As A, create a record and note its ID in the address bar or the Network tab. Log in as B and open the same URL. You should get “not found” or “access denied”. Repeat for edit pages and downloads.
- Warning sign
- B can open A's record, or an API response in the Network tab contains other users' records that the interface filters out before display.
- Reference
- OWASP Top 10:2025 A01 Broken Access Control; OWASP API Security API1:2023; Next.js data security guide.
3. Supabase row-level security and the secret key
- What to check
- If the app uses Supabase: row-level security (RLS) is enabled on every table in an exposed schema, each table has policies that match what each role may do, and the secret key (or the older service_role key) is only ever used on a server.
- Why it matters
- Supabase: a table in an exposed schema without RLS is readable and writable by any role with a grant on it. The publishable key is meant for the browser: anyone can read it, so it only reaches what Row Level Security allows. A secret key bypasses every Row Level Security policy you have; Supabase says never to put one in a browser, a shipped application or source control. In PostgreSQL itself, RLS with no policy is a default-deny policy, so a table that suddenly returns nothing usually means RLS is on and a policy is missing.
- How to verify safely
- In the Supabase dashboard, run the Security Advisor and check the RLS status of each table. In the browser, open the developer tools on your deployed app and search the loaded scripts for
sb_secret_andservice_role; neither should appear. Then repeat the two-account test from item 2. - Warning sign
- A policy to anon using (true) on a table that holds private data. A view over a protected table: Supabase notes that views bypass RLS by default. A secret key in any file the browser downloads.
- Reference
- Supabase: Row Level Security, API keys, production checklist.
4. Tenant isolation
- What to check
- If the app serves several companies or teams (tenants), every query, file path, cache key and search index is scoped to the tenant, and the tenant comes from the signed-in user's verified membership, not from a value the browser sends.
- Why it matters
- OWASP's multi-tenant cheat sheet: a single vulnerability can expose all tenants' data. It advises to treat client-supplied tenant identifiers as selectors only and to verify that the user is authorized to act in that tenant.
- How to verify safely
- Create two test tenants with one user each and repeat the record-swap test across them, including file downloads, exports, search results and emails sent by the system. Thorough isolation tests are engineering work: OWASP asks to test isolation through the same role, connection path, and pooling mode used by the application.
- Warning sign
- A tenant ID in a header, body or URL that the server trusts; admin screens that can reach every tenant without a record of who looked. For a full treatment, see building a multi-tenant SaaS platform.
- Reference
- OWASP Multi-Tenant Application Security Cheat Sheet.
5. API access control
- What to check
- There is a list of every API route, Server Action, edge function and database function the app exposes, each marked public or protected, and each protected one checks the caller itself.
- Why it matters
- OWASP: permission should be validated correctly on every request, whatever started the request. Next.js warns that a page-level authentication check does not extend to the Server Actions defined within it. Replit's checklist says to add authentication to sensitive endpoints.
- How to verify safely
- While logged in, note the API calls the app makes in the Network tab. Open a private browser window (logged out) and request one of those URLs that only reads data. Expect a 401 or 403 response, not data. Leave anything that writes or deletes to an engineer in the test environment.
- Warning sign
- Nobody can produce the list of routes, or an admin route answers a logged-out request.
- Reference
- OWASP Authorization Cheat Sheet; Next.js data security.
Secrets, input and limits
6. Secrets and environment variables
- What to check
- No secret is in the source code, the repository history or the browser bundle. Variables with a client-exposed prefix hold only values you would publish. Test and production use different keys. Any key that was ever exposed has been rotated.
- Why it matters
- Next.js copies any variable prefixed
NEXT_PUBLIC_into the js bundle that is delivered to the client. Vite: VITE_* variables should not contain sensitive information such as API keys. On Vercel, environment variable changes only apply to new deployments, so a rotated key needs a redeploy. GitHub's advice for a leaked secret is to rotate the affected credential immediately. - How to verify safely
- List every variable in the hosting dashboard and in
.env.example. For each one with aNEXT_PUBLIC_orVITE_prefix, ask: would I put this on a public web page? If the repository is on GitHub, turn on secret scanning. - Warning sign
- A payment, email or AI provider secret with a public prefix; a committed
.envfile; the same key in test and production.
7. Input validation
- What to check
- The server checks the type, length, format and allowed values of every input, including uploaded files, whatever the form already checks.
- Why it matters
- OWASP: validate on the server even when the browser checks the same fields; browser checks are for quick feedback. The API Security Top 10 asks for a maximum size on all incoming parameters and payloads.
- How to verify safely
- In the test environment, paste a very long block of text into a form field and upload a file of a type the app should not accept. Expect a clear rejection, not a crash, a stack trace or the value saved as-is. Ask an engineer where the server-side validation for each endpoint lives.
- Warning sign
- Validation only inside form components, or database queries built by joining user input into a string.
- Reference
- OWASP Input Validation Cheat Sheet; API4:2023 Unrestricted Resource Consumption.
8. Rate limiting and spending limits
- What to check
- Login, sign-up, password reset and every feature that costs money per call (AI model calls, SMS, email) are limited per user or per client. Each paid provider has a spending cap or a billing alert.
- Why it matters
- OWASP asks you to implement a limit on how often a client can interact with the API within a defined timeframe and to configure spending limits for all service providers/API integrations. For logins it says to limit or increasingly delay failed login attempts without locking real users out.
- How to verify safely
- Open the billing settings of each paid provider and confirm a limit or alert is set. Ask where rate limits are configured and what a user sees when one is hit. Do not try to trigger them against production.
- Warning sign
- An AI feature that calls a paid model on every request with no per-user cap and no billing alert.
- Reference
- OWASP API4:2023; A07:2025; Replit security checklist.
Payments, events and background work
9. Payment and webhook signature verification
- What to check
- Every webhook endpoint (payments, auth, third-party events) verifies the sender's signature on the raw request body before it does anything.
- Why it matters
- Stripe: without verification, an attacker could send fake webhook events to your endpoint to trigger actions like fulfilling orders, granting account access or modifying records. Verification needs the untouched body: Stripe requires the raw body of the request to perform signature verification.
- How to verify safely
- Find the webhook handler and ask to be shown the line that verifies the signature with the signing secret. In the provider dashboard (in test mode), look at recent deliveries and their responses. Sending a deliberately unsigned request belongs in the test environment, done by an engineer.
- Warning sign
- The handler parses the JSON first and never verifies; or paid access is granted when the browser reaches a “success” page, not when a verified payment event arrives. Our guide to reliable webhook integrations covers the full receive path.
- Reference
- Stripe: verify events are sent from Stripe, receive events in your webhook endpoint.
10. Duplicate webhook delivery and idempotency
- What to check
- Processing the same event twice has the same effect as processing it once: one order, one email, one credit. Event IDs are recorded and repeats skipped. Nothing assumes events arrive in order.
- Why it matters
- Stripe: webhook endpoints might occasionally receive the same event more than once, and it recommends logging processed event IDs and skipping them. Stripe also doesn't guarantee the delivery of events in the order that they're generated, and in live mode retries delivery for up to three days with an exponential backoff.
- How to verify safely
- Ask an engineer to deliver the same test event twice in the test environment and confirm one order, one email and one credit. During your own testing, note any duplicate emails or double charges.
- Warning sign
- Duplicate emails or credits in testing; a handler that expects “created” to arrive before “updated”.
- Reference
- Stripe webhooks: handle duplicate events, event ordering.
11. Background jobs
- What to check
- Work that runs outside a web request (emails, imports, AI processing, scheduled tasks) has a retry limit, is safe to run twice, re-checks which user or tenant it acts for, and leaves a visible record when it fails.
- Why it matters
- OWASP: a shared queue or topic is not itself a tenant-isolation boundary; a job must not trust an unverified tenant field in its message. Jobs that retry also run twice, so the idempotency rule from item 10 applies to them too.
- How to verify safely
- Ask where jobs run, what happens when one fails and where you can see failed jobs. In test, cause a known failure (for example an invalid email address) and check that it shows up somewhere a person will see it.
- Warning sign
- No record of failed jobs anywhere, or jobs that use the database secret key with no tenant check. Our guide to reliable background jobs with Redis and BullMQ covers retries, stalled jobs and idempotent handlers.
- Reference
- OWASP Multi-Tenant Application Security Cheat Sheet (tenant-aware asynchronous work).
Data you cannot afford to lose
12. Database migrations
- What to check
- Every schema change, including access policies, is a migration file in the repository, applied to the test environment before production. Nobody edits the production schema by hand in a dashboard.
- Why it matters
- Supabase describes migrations as SQL statements that change your schema and a common way of tracking changes to your database over time. When policies live in migration files, they are reviewed like code and can be rebuilt in a fresh environment.
- How to verify safely
- Ask whether a fresh database can be built from the repository alone, then compare its tables and policies with production.
- Warning sign
- Production has tables or policies that exist in no file; a migration drops or renames a column with no plan for the data already in it.
- Reference
- Supabase database migrations.
13. Backups and restore tests
- What to check
- Automatic backups are on, you know how far back they go, and someone has restored one into a separate database and checked the data.
- Why it matters
- PostgreSQL's manual: PostgreSQL databases should be backed up regularly. On Supabase, Pro plan projects can access the last 7 days of daily backups; database backups are not available for download for Free Plan projects, and Supabase recommends that free projects regularly export their data using the Supabase CLI db dump command and maintain off-site backups. A backup nobody has restored is an assumption, not a plan.
- How to verify safely
- Check your plan and the backups page. Write down the date of the last restore test and how long it took. If there has never been one, schedule it before launch.
- Warning sign
- A free-plan project holding customer data with no exports, or no one able to say how a restore would work. Backups also go with the project: when you delete one, Supabase says we permanently remove all associated data, including any backups.
- Reference
- PostgreSQL Backup and Restore; Supabase database backups.
Running it in production
14. Dependency management
- What to check
- The lockfile is committed, dependency alerts are on, every package is one you meant to install, and updates are reviewed rather than skipped.
- Why it matters
- GitHub: Dependabot alerts help you find and fix vulnerable dependencies.
npm auditasks for a report of known vulnerabilities in your dependencies, though some vulnerabilities cannot be fixed automatically. OWASP notes a risk specific to AI tools: they frequently suggest package names that do not exist on public registries, which attackers can then register. - How to verify safely
- Turn on Dependabot alerts for the repository and run
npm auditlocally to see the report. Look up any package name you do not recognize on the public registry. - Warning sign
- Hundreds of open alerts nobody reads; a package with an unfamiliar name, few downloads and a recent creation date.
- Reference
- GitHub Dependabot alerts; npm npm audit; OWASP Secure Coding with AI Cheat Sheet.
15. Error monitoring
- What to check
- Errors in production are reported to a tool that alerts a named person. Users see a plain error message, not a stack trace or a database error.
- Why it matters
- OWASP lists as a failure that the application cannot detect, escalate, or alert for active attacks in real-time or near real-time. Detailed error output helps an attacker: unhandled errors can assist an attacker in the reconnaissance phase.
- How to verify safely
- In the test environment, open a page with an ID that does not exist and submit a form with a missing field. Check what the user sees and whether the error reached your monitoring and someone was told.
- Warning sign
- Errors visible only in the browser console; stack traces or SQL in responses; alerts going to an inbox nobody reads.
- Reference
- OWASP Top 10:2025 A09; OWASP Error Handling Cheat Sheet.
16. Logging hygiene
- What to check
- Logs record security events (logins, failed logins, denied requests, admin actions) and leave out secrets and personal data.
- Why it matters
- OWASP's logging guidance lists access tokens, authentication passwords, database connection strings, encryption keys, payment card data and sensitive personal data among things that should usually not be recorded directly in the logs. The Top 10 names logging sensitive information that should not be logged (such as PII or PHI) as a failure.
- How to verify safely
- Sign up in the test environment with a distinctive test password, then search your log viewer for that password and for the names of your secret keys. Neither should appear.
- Warning sign
- Whole request bodies or headers logged; debugging statements that print the full user object; logs anyone on the project can read.
- Reference
- OWASP Logging Cheat Sheet; A09:2025.
17. Automated tests
- What to check
- Tests cover the rules that matter: user B cannot read user A's records, each role sees what it should, RLS policies hold, a repeated webhook is processed once. They run on every change.
- Why it matters
- OWASP's API guidance: write tests to evaluate the vulnerability of the authorization mechanism. Do not deploy changes that make the tests fail. Supabase documents running database tests in CI so you run your tests when you or your team make changes. For RLS, OWASP asks to prove that expected cross-tenant operations are denied and expected same-tenant operations succeed.
- How to verify safely
- Ask to see the access-control tests and when they last ran. A quick way for an engineer to prove a test is real: remove one check on a branch and confirm the test fails.
- Warning sign
- No tests, tests that only cover the happy path, or tests switched off to get a deploy through.
- Reference
- OWASP API1:2023; Supabase automated testing.
18. CI/CD and production configuration
- What to check
- The code is in a repository your company controls. Production deploys from the main branch through a pipeline that runs the tests. Production, preview and development have separate settings and keys. Admin access is limited to current people, with MFA.
- Why it matters
- Next.js's production checklist: ensure your .env.* files are added to .gitignore and only public variables are prefixed with NEXT_PUBLIC_. On Vercel you can select one or more Environments to apply the Variable to (Production, Preview, Development), so preview deployments can use test keys. Supabase reminds owners that they are always responsible for account and access management.
- How to verify safely
- List who has admin access to the repository, hosting, database, payment provider and domain registrar, and remove anyone who has left. Confirm MFA on each. Confirm preview deployments do not point at the production database or live payment keys.
- Warning sign
- Production deployed from someone's laptop or a builder's publish button with no tests; a former contractor still an admin; preview builds writing to production data.
Summary: what you can check, and what needs an engineer
| Item | Self-check | Needs an engineer |
|---|---|---|
| Authentication | Log out and reuse an old URL; compare reset messages for known and unknown emails; MFA on your own admin accounts | Review of session handling, reset flow and any custom auth code |
| Authorization per record | Open user A’s record while logged in as user B | Trace every read and write path to a server-side check |
| Supabase RLS and keys | Security Advisor; RLS status per table; search the browser bundle for a secret key | Write and review policies per role; check views and security definer functions |
| Tenant isolation | Repeat the A/B test across two test tenants, including files and exports | Isolation tests on the real connection path and role |
| API access control | Open a protected API URL while logged out | Inventory of routes, Server Actions and functions with their checks |
| Secrets and env vars | List every NEXT_PUBLIC_ / VITE_ value and ask if you would publish it | Rotate exposed keys; clean repository history if needed |
| Input validation | Very long text and an unexpected file type in a test form | Server-side schemas for every endpoint |
| Rate and spending limits | Spending caps and billing alerts at each paid provider | Rate limits on login, reset and paid endpoints |
| Webhook signatures | Look for failed deliveries in the provider dashboard | Confirm verification on the raw body before any action |
| Duplicate webhooks | Watch for double emails or credits during testing | Idempotent handlers keyed on event ID; ordering assumptions |
| Background jobs | Find where failed jobs are visible | Retries, idempotency and tenant context in each job |
| Migrations | Ask whether a fresh database can be built from the repository | Reconcile dashboard changes into migrations; plan data changes |
| Backups and restore | Check plan, retention and the date of the last restore | Run and time a restore into a separate database |
| Dependencies | Turn on Dependabot alerts; run npm audit | Triage alerts; check unfamiliar packages; plan upgrades |
| Error monitoring | Trigger an error in test and see who is told | Set up reporting, alerts and generic error pages |
| Logging hygiene | Search logs for a unique test password | Remove secrets and personal data from log statements |
| Automated tests | Ask to see the access tests and their last run | Write tests for access rules, RLS and webhooks; gate deploys |
| CI/CD and production config | List who has admin access; check MFA and which keys each environment uses | Pipeline from the main branch with tests; separate environments |
When the self-checks are not enough
The self-checks tell you whether a problem is likely. They do not prove its absence. If the record-swap test fails, if nobody can list the API routes, if the webhook handler has never been tested twice, or if a restore has never been tried, the next step is an engineer reading the code paths behind those items. The work is usually specific: policies written per role, checks moved to the server, keys rotated, handlers made idempotent, migrations reconciled, a restore run and timed. That is the scope of our AI app to production service, which takes an existing AI-built codebase and makes it ready for real users.
If you want a second opinion on your own results, the first step is a 30-minute technical discovery call with an engineer. Bring the summary table with your answers filled in. We will tell you which items look fine, which need work, and whether that is a small fix or a larger piece of work. More on how that works is on the AI app to production page.
Frequently Asked Questions
Are apps built with Lovable, Replit, Bolt or Cursor insecure?
Not as a rule. The tool does not decide whether an app is secure; what was built and what was checked decides that. The tools say so themselves: Lovable’s documentation says its scans cannot guarantee complete security and that the app owner is responsible for meeting the security requirements of the use case. An AI-built app can pass every item on this checklist; the point is to check rather than assume.
Is it safe to have the Supabase publishable (anon) key in the browser?
Yes, that is what it is for, as long as Row Level Security is on. Supabase’s key guide says of the publishable key that anyone can read it, so it only reaches what Row Level Security allows. The secret key (and the older service_role key) is different: it bypasses every policy and must stay on servers you control.
Is the built-in security scan in my AI app builder enough?
It is a useful first pass, not a review. Lovable describes its Quick scan as a fixed set of checks that runs on every publish, and says its tools do not replace a thorough security review. Scans catch configuration problems such as a table without row-level security; they cannot tell whether your business rules (who may approve, refund or export) are enforced on the server.
Which checks can I do myself, and which need an engineer?
Anything you can test with two test accounts, the browser’s developer tools and your providers’ dashboards is a fair self-check: the record-swap test, a logged-out request to a protected URL, spending limits, Dependabot alerts and a search of the logs for a test password. Reading authorization code, writing RLS policies, making webhook handlers idempotent, cleaning up migrations and running a restore drill are engineering work. The summary table in this guide splits each item that way.
Do I need to rebuild an AI-built app before launch?
Not by default. Most items on this checklist are fixed in place: a policy added, a key moved to the server, a handler made idempotent. A rebuild is worth discussing only when a review shows that access rules, data model and tenancy would all have to change at once. Decide after the review, not before.

