VibeCheck-AI

Row-level security is where vibe-coded apps leak

Rule packJul 9, 20266 min read

The pattern we see most: the frontend checks who you are, the database does not. Anyone with the anon key can read the whole table. How to tell whether yours does.

This is the most common serious finding in apps built quickly with an assistant, and it is not really a coding mistake. It is a mistake about where a rule belongs.

Your app checks who is signed in before it shows someone their data. That check lives in your interface. It works, and it is not the problem. The problem is that your database was never told to check anything, and your interface is not the only way to reach your database.

What that means in practice

Hosted databases like Supabase and Firebase are designed to be queried directly from the browser, using a public key that ships inside your frontend. That key is not a secret — it is meant to be visible. What stops it reading everything is a set of rules on the database itself saying which rows each person may see.

If those rules are off, the public key reads every row in the table. Not through a bug, and not through anything clever. It is the documented behaviour of the tool, working exactly as designed, protecting nothing because nobody told it what to protect.

So: every user's email address, every order, every private message. Readable by anyone who opens devtools and copies a key that was always meant to be public.

How to check yours in two minutes

Open your database dashboard and look at the table list for whether row-level security is enabled per table. Enabled is necessary but not sufficient — a table with RLS on and a policy that resolves to true for everyone is exactly as open as one with RLS off, and it looks compliant in the list.

Then look for any key labelled service-role, admin or secret in your frontend code. Those bypass every rule you have written. If one is in browser code, that is the finding to deal with before anything else on this page.

What we can and cannot tell you

We read your committed migrations. If the rules are in the repository, we check them and report which tables are open and which policies are permissive.

If your project has no committed migrations — because you configured everything through the dashboard, which is the normal way to use these tools — there is nothing in your repository for us to read. That is reported as could not check, with instructions for reading it yourself. It is never reported as a pass, because a table we never looked at is not a table we found to be safe.

Stop reading, start scanning.

Free needs no card. One repository, read-only, two scans a day.