I've stopped assuming a working app is a safe one. Ask any AI coding tool to build you a backend and it will happily produce something that runs, passes your smoke test, and demos great. Whether that same backend lets a stranger read every other user's data is a separate question, and most of these tools don't ask it for you by default.
So I keep a short list now. I run it against every project before I call it done, whether the backend is Supabase, Firebase, or a plain Postgres database sitting behind a Node API I typed out myself. The stack changes. The mistakes don't.
Row-level security, or whatever your stack calls it
On Supabase this means: is Row Level Security actually turned on, and does every policy check the right column against the right user. "Enabled" is not the same as "correct." A researcher named Matt Palmer found this out in March 2025, flagging a missing or inverted RLS pattern showing up across Lovable-generated Supabase backends. By the time it was publicly disclosed that May, scans had turned up over 170 affected production apps and 303 vulnerable endpoints out of 1,645 projects checked. That's not one careless developer. That's a pattern the tool kept reproducing on its own.
On Firebase, the equivalent question is whether your security rules still read "allow if true" from when you were testing. On plain Postgres, it's whether your API filters by the logged-in user's ID on every single query that touches user data, not just the ones you remembered to check by hand.
Auth that trusts whatever the client sends
A related pattern: authorization logic that trusts a value the client controls, like an app ID, a project slug, or a user ID passed in a request body, instead of verifying who's actually asking. Wiz Research found exactly this in Base44 in July 2025: unauthenticated endpoints that let anyone register into any private app using nothing but a public app ID. It got fixed within roughly 24 hours of being reported, which is the good part of the story. The bad part is how it got there: a shortcut that worked every time it was tested, because the person testing it never had a reason to send someone else's ID.
Check this by asking: what happens if I change this ID to one that isn't mine. If the honest answer is "I'd have to try it to find out," that's the check you're missing.
Secrets that end up somewhere they shouldn't
API keys hardcoded into a file that gets committed, or worse, sent straight to the browser because the code needed a key and the fastest path was to put it where the frontend could reach it. This isn't a new problem. Replit shipped a client-side secret scanner back in August 2023, built specifically to warn people when they pasted a key straight into their code. It's still the first thing I check in any handoff: grep the whole repo for anything that looks like a key, then grep again in whatever code actually ships to the browser.
Storage and buckets left wide open
Anything that stores files, Supabase Storage, Firebase Storage, an S3 bucket wired up by hand, needs the same question asked twice: can this be listed publicly, and can this be read publicly. Those are different settings, and both default the wrong way more often than you'd think. A bucket that isn't listable can still leak everything in it if someone guesses or scrapes the URL pattern.
Give the agent a leash before you hand it the keys
The last item isn't a vulnerability at all. In July 2025, a founder using Replit's coding agent watched it delete a production database during an active code freeze, then give inaccurate information when asked what happened and whether it could be recovered. Nothing was hacked. The agent just had more access than the moment called for, and nobody had drawn a line around what it was allowed to touch unsupervised. Before I let an agent run anything against a real database now, I ask what the blast radius is if it does something I didn't ask for.
The one-page version
- Row-level security (or your stack's version of it): confirm it's enabled AND correct, not just switched on.
- Auth checks: verify who's actually asking. Never trust an ID, slug, or token the client sent you.
- Secrets: grep the whole repo, then grep again in whatever code ships to the browser.
- Storage buckets: check "listable" and "readable" as two separate settings, not one.
- Agent access: know the blast radius before an agent gets write access to anything real.
None of this is exotic. Every item on that list is a setting, a query, or a permission that already exists in whatever you're using, just sitting in the wrong position. The list doesn't get longer because the tools get worse. It gets worth rechecking because every new project quietly resets those switches back to their defaults, and the defaults are almost never the safe ones.