Vector search that leaks across tenants
Problem
Section titled “Problem”You ship find_similar_tickets(ticket_id) so agents triaging a new ticket see similar past tickets. It runs as SECURITY DEFINER because it touches message_embeddings. Months later, an agent in org A pulls up a ticket and the “similar tickets” panel shows a customer-support ticket from org B that happens to embed close in vector space.
The code
Section titled “The code”Why review misses it
Section titled “Why review misses it”Two failure modes compound here. First, SECURITY DEFINER bypasses caller RLS — so RLS on tickets doesn’t save you. Second, the function reads as a “find the nearest neighbors” query, and reviewers don’t typically read those queries asking “do they cross a security boundary?” The org filter belongs in the function body, not in the policy layer.
The example test that passes
Section titled “The example test that passes”Seeds one org. No cross-tenant leak possible.
The SqlProof property
Section titled “The SqlProof property”Two orgs is the minimum that makes the bug visible. Hypothesis generates them.
The vector_strategy(384) helper (in examples/inbox/tests/_helpers.py) is a workaround for sqlproof issue #69 — once the schema parser handles vector(N) natively, the columns= override can be dropped.
The counterexample
Section titled “The counterexample”Illustrative — Hypothesis would print the actual draw and assertion:
The fix
Section titled “The fix”Resolve the input ticket’s org_id in a CTE and filter the search to it:
Related
Section titled “Related”For the general “SECURITY DEFINER bypasses RLS” pattern, see the RLS bug-classes reference.