RowShield

Help / Findings

Remediation SQL: how it is generated and applied safely

All plansLast reviewed 2026-08-23

Every finding that can carry a fix does. The generator's bar is SQL that is safe to run without reading it first, which rules out guessing: where the correct policy depends on your data model, the block emits what it can prove and marks the rest as TODO.

Built from your actual columns

Ownership is inferred from the snapshot taken moments earlier, not assumed. Columns named user_id, owner_id, created_by, author_id, profile_id, account_id, member_id or uid are candidates, and the type check matters as much as the name: a uuid column compares to auth.uid() directly, while a text column gets an explicit cast so the emitted policy actually runs.

When no candidate exists, the block says so, lists the columns it saw, and leaves a placeholder for you to fill rather than inventing an ownership column that is not there. Quoting is handled properly throughout: reserved words quoted, literals escaped, identifiers truncated to Postgres's 63-byte limit so generated names stay stable across environments.

For MISSING_WITH_CHECK, where a policy already has a USING expression, the generator reuses that expression as the write check, since read-and-write symmetry is the intent in almost every ownership model; widening it is a decision the block leaves visibly to you.

-- Generated by RowShield. Review before running in production.
ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.documents FORCE ROW LEVEL SECURITY;

CREATE POLICY documents_select_own
  ON public.documents
  FOR SELECT
  TO authenticated
  USING (owner_id = (SELECT auth.uid()));

-- insert, update and delete policies follow the same shape.

Why FORCE is always included

Without FORCE ROW LEVEL SECURITY the table owner bypasses every policy, and a Supabase migration or a psql session typically runs as the owner. An enable-without-force fix would look applied while protecting nothing from the very session that applied it, so the generator never emits one half without the other.

FORCE also closes the subtlest gap in hand-written advice copied from tutorials, which routinely omit it because the author tested as a non-owner role and everything appeared fine until the next deploy ran as owner and sailed past every policy.

Applying, and reversing, safely

Order matters. Create policies and enable RLS in one migration so no window exists where the table sits open, and expect a deny-all window for clients if you enable first and write policies after. Wrap policy changes in a transaction; the one exception is CREATE INDEX CONCURRENTLY, which Postgres refuses to run inside one, so index fixes arrive as standalone statements.

A practical sequence for an RLS-disabled finding: paste the policies and both ALTER statements into one migration, deploy to staging, point a connected project at staging, confirm the finding resolves, then ship to production and press Scan-now for confirmation.

Reversal is symmetric and belongs in the same commit as the forward migration: DROP statements reversing every CREATE, and NO FORCE ROW LEVEL SECURITY only if you genuinely intend to return the table to owner-bypass. Test the result as an affected role rather than as the owner, because the owner behaves differently from the roles your clients use. Bucket findings generate the private-flag update plus an example owner-scoped read policy keyed on the first folder segment.

One more habit worth adopting: apply remediation to staging with the same role you will use in production, because a fix that works only as table owner proves nothing about what your users will experience.

Related questions

Can I paste remediation SQL straight into production?
It is generated to be runnable and conservative, and the banner still asks for review, because only you know whether the inferred ownership model matches reality.
Why do some blocks contain a TODO?
Because inventing an ownership column would produce SQL that runs and protects nothing. A marked placeholder fails visibly during review instead of silently during an incident.

Did this answer your question? If not, tell us what is missing — article corrections go straight to the person who maintains it.

RowShield checks 9 rule classes continuously. This article describes shipped behaviour only.