OneShot

All packsMulti-Tenant Auth & RBAC CoreSolve

postgres rls using policy without with check cross tenant insert

A USING policy blocks reads but not cross-tenant inserts

A row-level security policy without a WITH CHECK clause correctly filters SELECTs but still lets a request INSERT rows into another tenant's org.

3 of 3 runs passedOne decision inside Multi-Tenant Auth & RBAC Core (nextjs-postgres).

What goes wrong

A Postgres RLS policy's USING clause governs which existing rows a query can see or touch (SELECT, UPDATE, DELETE) -- it says nothing about which rows a new INSERT is allowed to create. A policy written with only a USING clause correctly blocks tenant A from reading tenant B's rows, and still lets tenant A insert a row claiming to belong to tenant B's org_id.

Why agents get it wrong

USING reads as "the rule for this table" in the CREATE POLICY syntax, and a policy that filters reads correctly passes every test that checks whether cross-tenant data is visible -- the insert path is a separate, easy-to-forget clause (WITH CHECK) that most RLS tutorials mention once and most generated policies omit, because the SQL still runs without it.

What Multi-Tenant Auth & RBAC Core pre-decides

Every INSERT/UPDATE policy in the pack's frozen migration carries an explicit WITH CHECK (org_id = app.current_org_id() AND ...) alongside its USING clause -- separate policies per command, not one combined policy, specifically so a write path can't rely on a read-oriented rule that was never designed to gate it. A cross-tenant write attempt fails with 42501, and the verify suite's isolation tests probe writes directly, not just reads.

Source: ARCHITECTURE.md core decision 7, 'Traps this pack pre-empts'

Coming soon — $149Full receipt and details for Multi-Tenant Auth & RBAC Core

Related problems