How to use this checklist
Run the checks in order, and only against your own app and your own accounts. The order matters: secrets and data access come first because a failure there is usually silent and total, while a missing header is loud only to a scanner.
A check is done when you have seen the pass condition with your own eyes. 'The AI said it added row security' is not a pass. A logged-out request that returns nothing is.
Do not try to be perfect. The aim is to close the failures that let a stranger read or change your users' data without any special skill. Everything else can be scheduled.
Group 1: Secrets and keys
AI tools copy keys into files because it makes the demo work. The fix is cheap if you catch it now and expensive after a key has been used by someone else.
- 1Search your repository and its history for keys. Run a secret scanner such as gitleaks over the full git history, not just the current files. Pass: zero findings, or every finding has been rotated and removed from use.
- 2Check what ships to the browser. Open your deployed site, view the built JavaScript in developer tools, and search for the names of your service keys. Pass: only keys that are designed to be public, such as a database public key, appear.
- 3Confirm that .env files are ignored by git. Run git check-ignore .env. Pass: the file path is printed back.
- 4Confirm that production and development use different keys. Pass: revoking the development key would not break production.
- 5Rotate anything that has ever been pasted into a chat, an issue or a screenshot. Pass: the old value no longer works when you try it against your own service.
Group 2: Data access
If your app talks to a hosted database straight from the browser, the database rules are your real backend. This is the group that decides whether a stranger can read your users' rows.
- 1Turn on row-level access rules for every table the browser can reach. Pass: your provider's dashboard or a SQL query shows the rules enabled on every such table.
- 2Test as a logged-out visitor. Use a private window, and the public key from your own front end, to request each table. Pass: you get an empty result or a permission error.
- 3Test as user A against user B. Create two test accounts and try to fetch B's record by ID while signed in as A. Pass: the request is refused or returns nothing.
- 4Test writes, not just reads. Try to update or delete another test user's row. Pass: refused.
- 5Check file storage. Open a file URL from a private bucket in a logged-out window. Pass: it does not load without a signed link.
Group 3: Authentication and sessions
- 1Try to reach a protected page while logged out. Pass: you are redirected, and no protected data flashes before the redirect.
- 2Call your API directly, with no cookie or token. Pass: a 401 or 403, never data.
- 3Check that admin actions are enforced on the server. Log in as a normal user and replay an admin request using your browser's network tab. Pass: refused.
- 4Try a password reset for an email that does not exist. Pass: the response looks identical to a real one, so accounts cannot be enumerated.
- 5Confirm that sessions end. Log out, then replay a request with the old token. Pass: refused.
Group 4: The edges of your app
These checks are quick and catch a surprising number of leftovers from the prototype stage.
- 1Look for debug routes and test pages. Search your routes for names like debug, test, seed and admin. Pass: none reachable in production.
- 2Read your error pages. Trigger a failed request. Pass: no stack traces, file paths or SQL in the response.
- 3Check response headers with a header checker or your browser's network tab. Pass: HTTPS enforced and a Content-Security-Policy present, even if it starts in report-only mode.
- 4Rate limit sign-in and any endpoint that sends email or SMS. Pass: the sixtieth rapid request from your own script is slowed or refused.
- 5Check third-party scripts. List every script your pages load. Pass: you can name the reason for each.
Group 5: Recovery
- 1Confirm backups exist and know how far back they go. Pass: you can state the restore window without looking it up.
- 2Restore one backup into a scratch project once. Pass: it works, and you know how long it took.
- 3Write down the three steps you take if a key leaks: revoke, rotate, check logs. Pass: the steps are in a file, not in your head.
The whole checklist on one page
Print this table or paste it into your tracker. Each row is one test you can run today.
| Check | How to test on your own app | Pass condition |
|---|---|---|
| No secrets in git history | Run a secret scanner over the full history | Zero live findings |
| No service keys in the browser bundle | Search built JavaScript for key names | Only public-by-design keys found |
| Row rules on every exposed table | Query as a logged-out visitor with the public key | Empty result or permission error |
| Users cannot read each other's data | Fetch user B's record while signed in as user A | Refused or empty |
| Server enforces roles | Replay an admin request as a normal user | 403 |
| API rejects anonymous calls | Call the endpoint with no token | 401 or 403 |
| Private files stay private | Open a private file URL logged out | Does not load |
| No leaky errors | Trigger a failure and read the response | No stack trace or SQL |
| Abuse limits exist | Script rapid sign-in attempts against yourself | Slowed or refused |
| Restore works | Restore a backup into a scratch project | Data present, time noted |
What to do when a check fails
Fix in this order: anything that exposes data, then anything that lets a stranger act as someone else, then everything else. Do not ask the AI tool to 'make it secure'. Give it the specific failed test, for example 'requests from a logged-out user to the orders table return rows, add a policy so users only read rows where user_id matches their own ID', then run the same test again.
Keep the tests. A failing check you turn into a repeatable script becomes a regression test, which matters because the next AI-assisted change can quietly undo a fix.
Frequently asked questions
How long does a vibe coding security check take?
For a small app with one database and a few dozen routes, most of the checks above take one focused afternoon. The data access group takes longest because you need two test accounts. Once you have scripted the tests, rerunning them before each release takes minutes.
Can I ask the AI tool to run this checklist for me?
It can help write the tests and fix what fails, but it should not be the judge of its own work. Run the pass conditions yourself against the deployed app. A tool that wrote a flawed policy will often describe it as correct.
Which check matters most?
The logged-out data access test. If a stranger with your public key can read or write your tables, every other control is moot. It is fast, needs no special tools, and catches the most damaging class of failure in apps built on hosted databases.
Do I need a penetration test before launch?
Not for a first small launch, provided you finish a checklist like this one. A professional review becomes worth paying for once you store sensitive personal, health or payment data, or when a customer's procurement team asks for one.