Correlated RLS subqueries
Problem
Section titled “Problem”You ship a SELECT policy on tickets so that “agents only see their org’s tickets.” Code-review goes fine; the local test passes. In production, an agent in a different org runs a list query and gets back every other org’s tickets.
The code (schema/001_initial.sql)
Section titled “The code (schema/001_initial.sql)”Why review misses it
Section titled “Why review misses it”The shape “EXISTS (SELECT 1 FROM org_members WHERE user_id = auth.uid())” reads as “is the caller a member?” — and reviewers pattern-match on that intent. The missing correlation back to tickets.org_id is invisible until you ask “a member of which org?”.
The example test that passes
Section titled “The example test that passes”One org, one ticket — the policy returns the row, the test is green. The cross-org leak only surfaces when the test data contains two distinct orgs.
The SqlProof property
Section titled “The SqlProof property”The counterexample
Section titled “The counterexample”The fix (schema/003_fix_tickets_rls.sql)
Section titled “The fix (schema/003_fix_tickets_rls.sql)”One line. Two-org property tests catch every version of this bug.