v3.5.3 7 February 2026 Fix
RLS Coverage Gap Fix
Fixed
bug_reportstable — RLS FORCE was set but ENABLE was not, meaning policies existed but were not being enforced. The table owner (accountingrole) could bypass the four tenant isolation policies. ENABLE now applied — all operations enforced through RLS (bug_qfhk)user_organisationstable removed from RLS scope — Was incorrectly included in the v3.5.0 batch migration. This is a global lookup table used by tenant middleware to establish org context, so it fundamentally cannot require org context to read. RLS disabled, policies removed. Now correctly classified alongsideusers,organisations, andsessionsas a global table (bug_qfhk)rls-verify.sqlscript — Fixed three bugs: usespg_classfor FORCE column (was missing frompg_tables), nil UUID for no-context test (was empty string failing UUID cast), correcttl.amountcolumn in P&L EXPLAIN (was nonexistenttl.entry_type)
Root Cause
Both table issues originated in the v3.5.0 RLS batch migration (rls-enable.sql). bug_reports only partially applied (FORCE without ENABLE). user_organisations was included in the tenant table list but should have been classified as global — it’s queried by the tenant middleware via the connection pool before any RLS context exists, causing 403 NO_ORGANISATION errors on all tenant-scoped routes.
Discovery
Found during post-RLS verification (step 1 of rls-verify.sql) which checks both relrowsecurity and relforcerowsecurity via pg_class.
Verification
- 25 tenant-scoped tables show
rls_on=t, forced=t user_organisationscorrectly excluded from RLS (global table)- Full 8-step RLS verification suite passing
- Dashboard and all tenant routes loading correctly
- No application errors after fix
Files Changed
db/migrations/rls-verify.sql— Three bug fixes, org UUIDs populated