RowShield
Guides

Audit the Supabase policies Cursor wrote

Cursor tends to produce policies that look right. The failures are subtle rather than absent, which makes them harder to catch by reading and easier to catch by checking.

Rules that check this

Policies that are always true

A placeholder `USING (true)` written to get something working, then never narrowed. RowShield normalises the deparsed expression and recognises the constant-true forms: `true`, `1 = 1`, `NOT false`, matching string literals, and any OR arm that is one of those.

Read policies without matching write policies

The common shape is a correct SELECT policy and an INSERT policy with no WITH CHECK. Reads are locked down, writes are not, and nothing in the app surfaces the difference until someone writes a row they should not be able to.

The performance tax

Generated policies almost always call `auth.uid()` directly rather than wrapping it, and almost never come with an index on the column they filter. Neither is a security bug; together they are the reason the bill grows.

A five-minute review ritual for AI-written policies

After any Cursor session that touched the schema, run the same four questions in order. Is RLS enabled on every new table, with FORCE so migrations cannot bypass it? Does every policy bind real roles — authenticated or anon — rather than only service_role? Does every writing command carry its own WITH CHECK scoped to ownership? And is there an index on whatever column those policies filter?

The ritual works because each question maps to a catalog query with a yes-or-no answer; nothing depends on reading intentions out of generated prose. When all four answers are clean you are done. When one is not, the fix is mechanical rather than interpretive.

The honest limitation is repetition: rituals survive about two sprints of deadline pressure before someone ships "just this once" without them. Automating the four questions into a scheduled scan is precisely what RowShield does — the same checks, the same queries, executed against the live project whether or not anyone remembered to ask.

What good generated policies look like

Worth calibrating against, because review sharpens when the target is concrete. A sound AI-written policy names the command explicitly rather than leaning on ALL; targets authenticated or anon deliberately instead of defaulting to public; expresses ownership through a real column compared against a wrapped auth.uid(); and pairs every INSERT with a WITH CHECK so writes cannot forge ownership.

Just as telling are the habits of careful generators: they add FORCE ROW LEVEL SECURITY unprompted, they comment which application flow each policy serves, and they leave TODO markers where an ownership column could not be inferred rather than guessing a column name that silently matches nothing.

When your Cursor output looks like this, accept it with confidence and move on. When it does not, the gaps are mechanical to fix — and if the volume of generated policies makes manual reading unrealistic, that is the signal to let scheduled scans carry the checklist for you while review attention goes to the genuinely novel parts of the diff. Calibration like this is what keeps review sustainable over months rather than days, which is when most hand-rolled checklists quietly disappear. The four questions also make delegation honest: whoever touched the schema last can answer them in the PR description, and anyone with dashboard access can verify the answers.

Frequently asked

Can I re-run this after every prompt?
That is the intended use. Connect the project and it runs on a schedule; the "Scan now" button forces one immediately.
Does it read my source code?
No. It reads the database catalog — what actually shipped — rather than what the source implies.

Check your project in about ten seconds

Paste a URL. No signup, no writes, nothing stored.

Run the free audit
cursor supabase securitycursor generated supabase missing with checkai generated supabase rls