Tests: vollständige Suite auch mit RLS_ENFORCED=true grün

- test-tenant-isolation: Compound-Key mit fremdem Mandanten – im Owner-Betrieb Throw
  (Tenant-Guard), unter scharfer RLS liefert die DB null; beides = kein Datenabfluss
- run-tests.ts: lädt .env und leitet RLS_DATABASE_URL (Rolle craftvia_app) aus
  DATABASE_URL ab, wenn RLS_ENFORCED=true und keine URL gesetzt ist

Nachweis: RLS_ENFORCED=true npm run test → 52/52; npm run gate → 52/52.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 18:33:22 +02:00
co-authored by Claude Opus 5
parent bc58738129
commit 6687ef2e03
2 changed files with 27 additions and 2 deletions
+17 -2
View File
@@ -29,6 +29,21 @@ async function expectThrow(fn: () => Promise<unknown>, msg: string) {
}
}
/**
* Compound keys with a foreign tenantId: in owner mode the tenant guard throws (fail-closed);
* with RLS_ENFORCED=true the database already hides the row, so the query returns null.
* Both outcomes mean "no data leak" — anything else fails.
*/
async function expectThrowOrNullUnderRls(fn: () => Promise<unknown>, msg: string) {
if (process.env.RLS_ENFORCED !== "true") return expectThrow(fn, msg);
try {
const r = await fn();
ok(r === null, `${msg} (RLS: null)${r === null ? "" : ` — statt null: ${JSON.stringify(r)}`}`);
} catch {
ok(true, `${msg} (RLS: Throw)`);
}
}
/** Erwartet, dass `fn` `null` liefert (kein Datensatz, kein Abfluss). */
async function expectNull(fn: () => Promise<unknown>, msg: string) {
const r = await fn();
@@ -98,7 +113,7 @@ async function main() {
);
// (6) Compound-Unique-Key (tenantId_key) mit fremdem tenantId, select ohne tenantId.
await expectThrow(
await expectThrowOrNullUnderRls(
() =>
dbA.role.findUnique({
where: { tenantId_key: { tenantId: tenantB.id, key: roleB.key } },
@@ -108,7 +123,7 @@ async function main() {
);
// (7) Compound-Unique-Key mit fremdem tenantId, ohne select.
await expectThrow(
await expectThrowOrNullUnderRls(
() =>
dbA.role.findUnique({
where: { tenantId_key: { tenantId: tenantB.id, key: roleB.key } },