These are the ten problems we find most often in apps built with Lovable, Bolt, Cursor, Replit and Claude Code. You can check all ten yourself in about an hour. Our audit runs 49 checks and reads every file, but these ten catch the worst.
Is row-level security on for every table?
How to check
In Supabase, open the Table Editor and look for a table marked as unrestricted, or run select tablename, rowsecurity from pg_tables where schemaname = 'public';. Every row should say true.
Why it matters
Any table with it off can be read and changed by anyone holding your public key, which ships in your site.
Do any policies simply say "true"?
How to check
Search your migrations for using (true) and with check (true). In Firebase rules, search for if true and for rules that only check request.auth != null.
Why it matters
A policy that is always true is the same as no policy. "Any signed-in user" is not much better for private data.
Can users edit their own plan, role or credits?
How to check
Find the table that holds plan, role, credits or is_admin. If users can update their own row there, check that a policy or trigger stops them changing those columns.
Why it matters
If not, any user can make themselves an admin or a paying customer with one request.
Is a secret key reachable from the browser?
How to check
Search your code for VITE_, NEXT_PUBLIC_ and EXPO_PUBLIC_ next to words like SECRET, SERVICE_ROLE, OPENAI or STRIPE. Anything behind those prefixes is sent to every visitor.
Why it matters
A service-role key in the browser gives a visitor your whole database. A paid API key gives them your bill.
Are secrets in your repository or its history?
How to check
Check that .env is in .gitignore, then search history with git log -p --all -S "sk_live_" and the same for your other key prefixes.
Why it matters
A key removed from the code is still in every old commit. It needs rotating, not deleting.
Does your payment webhook verify the signature?
How to check
Open your Stripe webhook handler and look for constructEvent or constructEventAsync with your webhook secret.
Why it matters
Without it, anyone can post a fake "payment completed" message and get paid features for free.
Does the price come from your server?
How to check
Look at the code that creates a checkout session. The amount, price and credit count must come from a list on the server, never from the request.
Why it matters
If the browser sends the amount, a buyer can set it to one cent, or ask for a million credits.
Can a stranger call your paid APIs?
How to check
List every endpoint or function that calls a model, sends email or sends SMS. Each one needs a signed-in user and a limit per user. In Supabase, check verify_jwt for each function.
Why it matters
An open endpoint that calls a paid API is a bill that someone else controls.
Are admin pages protected on the server?
How to check
Find how your admin pages decide who is an admin. If the check is only in the page's own code, call the same data request while signed in as a normal user and see what comes back.
Why it matters
Hiding a page is not protection. The data behind it needs its own rule.
Do you have backups, and have you restored one?
How to check
In your database provider's dashboard, check that backups are on, how long they are kept, and whether your plan includes point-in-time recovery. Then restore one to a test project.
Why it matters
A backup you have never restored is a hope, not a backup.
Found something?
Most of these are an hour or two to fix once you know where they are. If you would rather have an engineer check everything and fix what matters, that is what our audit is for.