Why the same finding returns — and what finally breaks the cycle
Findings return because fixing a database state does not fix the habits that produce it: the drop-and-recreate migration removes yesterday's policies along with yesterday's table, the next generated component repeats the omission it learned, and nothing compares the project against its own past until a person chooses to look.
This page names the four reliable ways findings return, then breaks the cycle from both ends — the process habit that prevents re-entry, and the comparison practice that catches it when prevention slips. It is hand-written guidance; the final section marks precisely where product behaviour begins and what remains manual.
RowShield does not detect this yet. This guide gives you the catalog queries to check it yourself. The nine rules that do ship are listed on the rules index.
The shape of the loop
The classic telling: a finding is confirmed on Friday, fixed directly in the production console, and celebrated. No migration records the fix, no review sees it, and Monday's deploy rebuilds the table from source — which is now the only place defining its shape — restoring the original omission with mechanical fidelity. Nothing malfunctions anywhere in that story. The fix simply lived in a place the pipeline does not read, and the pipeline obeyed the place it does.
Four reliable ways findings return
Recreated objects shed their history: DROP TABLE takes its policies with it, and CREATE TABLE arrives bare. Feature velocity repeats omissions: every sprint adds tables, and some stop one line short again, because the person who learned the lesson last quarter is in a different meeting. Copied and generated code reintroduces idioms: USING (true), missing WITH CHECK and unwrapped auth calls are the patterns tutorials and generators emit by default. And environments diverge: staging gets hardened during the incident, production inherits the next deploy unchanged.
Breaking the loop with process
The preventive half is a ritual, not a tool purchase. After any migration touching tables, rerun the catalog queries — the thirty-second version fits in a saved snippet:
SELECT c.relname AS table_name, c.relrowsecurity AS rls_enabled
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind IN ('r', 'p') AND n.nspname = 'public'
AND c.relrowsecurity = false;
SELECT tablename, policyname FROM pg_policies
WHERE schemaname = 'public';Breaking the loop with comparison
Rituals fail on busy weeks, which is why the detective half compares the project against itself on a schedule. Each scan snapshots the catalog; each snapshot diffs against the last; differences become events with names. A finding appearing for the first time is news about your schema. A finding coming back is louder news about your process, because a regression means a fix existed, shipped, and was undone — the loop is already turning, and someone should know before the client does. Silence between scans means unchanged, which is the property that lets a quiet channel mean something.
A precision note, and where the machinery fits
Precision matters on this site, so: transitions are product behaviour, not a named rule. The nine shipped rules detect states; created, regressed and resolved are labels the scan comparison applies afterwards, and no tenth rule called regression exists in the registry this site's coverage tests enforce. Because that layer carries no rule backing, this page is marked manual even though the behaviour ships — and equally plainly, RowShield does not automate adopting the ritual: no tool makes your team run the queries or read the pull-request description. What it does continuously is re-run the checks, diff, and tell you which kind of news arrived.
Remediation SQL is generated from your actual columns when a finding appears, and applying it stays human by design. RowShield is an independent product, unaffiliated with and not endorsed by Supabase. Connect a project at rowshield.dev/audit to give the comparison a baseline — the public check needs no account.
Frequently asked
- What is the difference between a new finding and a regression?
- A new finding says the project reached a bad state for the first time; a regression says a state you already fixed came back. Regressions implicate the process meant to hold the fix, which is why they warrant the stronger response.
- Does RowShield repair findings automatically?
- No. Remediation SQL is generated from your real columns and waits for a human to apply it — automatic writes to a production database are the failure mode the product exists to prevent.
- Why do AI-generated migrations keep omitting RLS?
- Generators optimise for creating working structure quickly, and policies encode intent the generator cannot know. Treat every generated migration as containing an unwritten policy section until a check proves otherwise.
Check your project in about ten seconds
Paste a URL. No signup, no writes, nothing stored.
Run the free audit