← back to Rentv
test(pr-crm): tenant-isolation RLS invariant guard (Class-A, TK-10290)
2fc31e97b9b8b2795d56ba6f193c7c657242cb26 · 2026-08-10 07:59:34 -0700 · Steve Abrams
Fails if any future table with a tenant_id column ships without RLS+FORCE+policy
(the classic cross-tenant-leak footgun), except the two documented pre-tenant auth
tables. Completes the hardening recommended in the approved setPassword memo. Passes
on the current schema; suite 99->100 green. Test-only — no runtime change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Files touched
A test/pr/tenant-rls-invariant.test.js
Diff
commit 2fc31e97b9b8b2795d56ba6f193c7c657242cb26
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 10 07:59:34 2026 -0700
test(pr-crm): tenant-isolation RLS invariant guard (Class-A, TK-10290)
Fails if any future table with a tenant_id column ships without RLS+FORCE+policy
(the classic cross-tenant-leak footgun), except the two documented pre-tenant auth
tables. Completes the hardening recommended in the approved setPassword memo. Passes
on the current schema; suite 99->100 green. Test-only — no runtime change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---
test/pr/tenant-rls-invariant.test.js | 42 ++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/test/pr/tenant-rls-invariant.test.js b/test/pr/tenant-rls-invariant.test.js
new file mode 100644
index 00000000..28e67d98
--- /dev/null
+++ b/test/pr/tenant-rls-invariant.test.js
@@ -0,0 +1,42 @@
+'use strict';
+// Regression guard (TK-10290, Class A): EVERY table with a tenant_id column must have RLS
+// enabled + FORCEd + a policy — except the two documented pre-tenant auth tables (pr_users,
+// pr_sessions), which are RLS-exempt by design and manually tenant-scope their queries.
+// A new tenant table that ships without RLS would silently leak across tenants; this test
+// fails loudly if that ever happens. Runs against a THROWAWAY DB (rentv_pr_test).
+process.env.PR_DB_NAME = 'rentv_pr_test';
+delete process.env.PR_DATABASE_URL;
+
+const test = require('node:test');
+const assert = require('node:assert');
+const { execSync } = require('child_process');
+
+execSync(
+ `psql -d postgres -tAc "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname='rentv_pr_test' AND pid <> pg_backend_pid()" 2>/dev/null; `
+ + 'dropdb --if-exists rentv_pr_test 2>/dev/null; createdb rentv_pr_test',
+ { shell: '/bin/bash' },
+);
+
+const db = require('../../src/pr/db');
+
+// Documented RLS-exempt auth tables (login must resolve a user across tenants pre-context).
+const RLS_EXEMPT = ['pr_users', 'pr_sessions'];
+
+test('every tenant_id table has RLS+FORCE+policy (except documented auth tables)', async () => {
+ await db.runMigrations({});
+ const unguarded = await db.rows(`
+ SELECT c.relname
+ FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace AND n.nspname='public'
+ WHERE c.relkind='r'
+ AND EXISTS (SELECT 1 FROM pg_attribute a WHERE a.attrelid=c.oid AND a.attname='tenant_id' AND NOT a.attisdropped)
+ AND c.relname <> ALL($1::text[])
+ AND NOT (c.relrowsecurity AND c.relforcerowsecurity
+ AND EXISTS (SELECT 1 FROM pg_policy p WHERE p.polrelid=c.oid))
+ ORDER BY c.relname`, [RLS_EXEMPT]);
+ assert.deepEqual(
+ unguarded.map((r) => r.relname), [],
+ `tenant_id table(s) missing RLS+FORCE+policy: ${unguarded.map((r) => r.relname).join(', ')} — `
+ + `add ENABLE+FORCE ROW LEVEL SECURITY + a tenant policy (see migration 007/008), or if it is a `
+ + `pre-tenant auth table, add it to RLS_EXEMPT here AND ensure its queries manually scope tenant_id.`,
+ );
+});
← 87c2b9fa auto-data-snapshot: 2026-08-10T07:52:37 (7 data files) — dat
·
back to Rentv
·
auto-data-snapshot: 2026-08-10T08:23:29 (7 data files) — dat 93f14ecf →