The pre-launch security checklist for AI-built apps
Before opening sign-ups on an app built with AI, verify 20 things yourself, in roughly 3 hours and without buying anything: that your database cannot be read from the browser, that one account cannot fetch another account's records, that no credentials ship in your client bundle, that login attempts are rate-limited, that sign-out invalidates the session, that admin endpoints check the role server-side, that old preview deployments are not public, that errors do not leak stack traces, that security headers and a non-wildcard CORS policy are in place, and that anything which costs you money cannot be called in a loop by a stranger.
Free, no account, nothing to install. Your progress is remembered in this browser only — we never see it.
0 of 20 doneabout 3 h 28 m in total
0 of 20 checks complete.
Your data
Start here. This group contains the two items most likely to be the reason you get breached.
Confirm your database is not readable from the browser
10 min- How
- Open your app, open the browser network tab, and find the request your frontend makes to your database. Copy the public key it sends. Now use it to request a table your account should have no business reading — another user's records, an orders table, anything.
- You pass if
- You get an empty result or a permission error. If rows come back, you have found a critical problem.
- Why it matters
- In stacks where the browser talks to the database directly, the public key is not a secret and was never meant to be. The only thing protecting your data is row-level access rules, and code generators very often do not write them.
Try to read another account's data
15 min- How
- Create two accounts. Sign in as the first. Take a record id that belongs to the second — a project, an order, a profile — and request it directly, by putting it in the URL or the request body.
- You pass if
- You get a 404 or a 403. Anything that returns the record is broken access control.
- Why it matters
- This is the most common serious flaw in AI-built applications. The interface only ever shows you your own data, so it looks correct right up until somebody edits a number.
Read what your API actually returns
10 min- How
- Open the network tab on a page that shows a few fields, and look at the whole response body.
- You pass if
- The response contains what the page displays, and not much else.
- Why it matters
- Generated endpoints usually return the whole database row because that was the shortest thing to write. The page shows three fields; the response ships thirty, including internal ids, billing references and email addresses.
Check your file storage permissions
10 min- How
- Take the URL of a file you uploaded — an avatar, an invoice, an export. Open it in a private window with no session. Then try changing the filename or id in it to something else plausible.
- You pass if
- Private files require a signed or authenticated URL, and guessing a neighbouring name gets you nothing.
- Why it matters
- Buckets default to convenient rather than private, and predictable filenames turn one leaked link into everyone's documents.
Secrets and keys
Anything that reaches the browser is public. The only question is whether you know which things reach the browser.
Search your client bundle for credentials
8 min- How
- Open your deployed site, view source, and search the loaded JavaScript for: secret, token, api_key, password, sk_, service_role.
- You pass if
- Only keys explicitly designed to be public appear. Nothing else.
- Why it matters
- A secret in a client-side environment variable is not hidden, it is published. The prefix that separates public from private variables is easy to get wrong and there is no warning when you do.
Confirm privileged keys are only used server-side
10 min- How
- Search your codebase for the admin or service key of every provider you use, and check every file that imports it runs on the server.
- You pass if
- No privileged key is imported by anything that ends up in a page component or a client bundle.
- Why it matters
- A service key bypasses every access rule you wrote. One accidental client import undoes the whole data layer.
Rotate anything that was ever pasted into a chat or a commit
20 min- How
- Check your git history for keys, and rotate every credential you have pasted into an AI tool, a screenshot or a support thread.
- You pass if
- Nothing currently live has ever left a secure place.
- Why it matters
- Git history is forever and public repositories are scanned continuously by people who are not you.
Accounts and sessions
The flows that were generated early, work fine, and have not been looked at since.
Try twenty wrong passwords in a row
5 min- How
- On your own test account, submit the wrong password twenty times as fast as you can.
- You pass if
- Something slows you down, locks the account, or challenges you before you get to twenty.
- Why it matters
- Without a limit, weak and reused passwords get guessed automatically. This is cheap to add and almost never present by default.
Test the password reset flow properly
10 min- How
- Request a reset. Use the link. Then try the same link a second time, and try one from a request you made an hour ago.
- You pass if
- A used link fails, and an old link expires.
- Why it matters
- Password reset is the flow that gets least attention and gives away the most. A reusable or long-lived token is an account takeover.
Check that signing out actually signs you out
8 min- How
- Copy your session cookie or token. Sign out. Then replay a request with the old value.
- You pass if
- The replayed request is rejected.
- Why it matters
- If sign-out only clears the browser, a stolen token stays valid for as long as it was issued for.
Inspect your session cookie flags
5 min- How
- In the browser's application tab, look at your session cookie.
- You pass if
- HttpOnly and Secure are set, and SameSite is Lax or Strict.
- Why it matters
- These three flags block whole classes of theft for free, and they are a configuration line, not a project.
Call an admin endpoint as a normal user
12 min- How
- Find an admin action in your app. Sign in as an ordinary account and call that endpoint directly, without going through the interface.
- You pass if
- You get a 401 or 403 from the server.
- Why it matters
- Hiding the button is not access control. If the role check lives in the frontend, it does not exist.
What you are exposing
Things that are reachable and that you have probably forgotten about.
Find your old preview and staging deployments
12 min- How
- List every deployment URL your host has ever generated, and open a few of the old ones.
- You pass if
- Nothing old is publicly reachable, or what is reachable holds no real data.
- Why it matters
- Preview environments frequently point at production data, skip authentication, and stay online for years.
Try to fetch configuration and version-control files
5 min- How
- Request /.env, /.git/config, /config.json and /backup.sql on your live domain.
- You pass if
- All of them 404.
- Why it matters
- Automated scanners try exactly these paths on every new domain, within hours of it appearing in certificate logs.
Trigger an error on purpose
5 min- How
- Request a URL with an obviously invalid id, and submit a form with malformed data.
- You pass if
- You get a generic message. No stack trace, no file paths, no framework version, no database driver.
- Why it matters
- A stack trace tells an attacker exactly which published exploits to look up. It is a settings change, not a code change.
Check your security headers
8 min- How
- Look at the response headers on your homepage, in the browser network tab or with any online header checker.
- You pass if
- Content-Security-Policy, Strict-Transport-Security, X-Content-Type-Options and Referrer-Policy are all present.
- Why it matters
- These tell the browser to block a range of injection and data-leak attacks on your behalf. Adding them is usually one config block.
Check your CORS policy is not a wildcard
5 min- How
- Look for Access-Control-Allow-Origin in your API responses.
- You pass if
- It names your own origins, not *. And if credentials are allowed, it definitely does not say *.
- Why it matters
- A permissive CORS policy lets any website on the internet make authenticated requests to your API using your users' sessions.
Cost and abuse
The failures that arrive as a bill rather than as a breach.
Rate-limit anything that costs you money
15 min- How
- List every endpoint that sends an email or SMS, generates a file, or calls a paid API. Try calling one of them twenty times in a row, signed out.
- You pass if
- You are stopped, either by authentication or by a rate limit.
- Why it matters
- An unauthenticated endpoint that calls a paid model is a bill with a public URL in front of it, and it can be found by anyone reading your JavaScript.
Try to hijack your own AI feature
15 min- How
- If your app has a chat, assistant or summarisation feature, put an instruction inside the content it reads — a document, a profile field, a page it fetches — telling it to ignore its rules and reveal its instructions.
- You pass if
- The feature carries on as normal and does not follow the embedded instruction.
- Why it matters
- Models follow instructions they find in data. If your feature reads user content, users can give it orders.
Try to complete a purchase without paying properly
20 min- How
- Walk through checkout while watching the network tab. Try changing an amount, a currency or a plan id in a request. Try navigating straight to the success page.
- You pass if
- The server rejects the tampered value and refuses to grant access without confirmed payment.
- Why it matters
- Prices and entitlements resolved on the client are suggestions. This is the one failure that hits revenue directly.
What this checklist cannot do
It covers the failures that are common, serious and testable by hand. It does not cover the ones that need someone to chain three requests together, to notice that a workflow can be replayed, or to spend two hours mapping every endpoint your app exposes including the ones you forgot about. Those are the findings that need an assessment rather than a list.
Work through this first. It is free and it removes the most likely reason you would get breached. Then, if the app takes payments, holds personal data or has customers who would be upset, have the running application tested properly.
Stop reading. Start checking.
An independent security check of your live app, from €59, with fix prompts for the coding tool you already use.
Keep reading
- What is a vibe coding security check?The definition, what it covers, and how it differs from your coding agent.7 min read
- Everything a VibeGuard check looks forThe full coverage list, area by area, with what is actually tried in each.9 min read
- Vibe coding security risks: what the research actually showsPublished figures on how often AI-written code ships vulnerable, and what it costs.8 min read