Private
Public Access
1
0

Phase 2: Enforce Row-Level Security at the PostgreSQL level

- Rewrite rls.ts with withTenant()/withBypassRLS() using parameterized
  set_config() in interactive Prisma transactions (no SQL injection)
- Add FORCE ROW LEVEL SECURITY on all 15 tenant-scoped tables
- Add __bypass__ sentinel and WITH CHECK clauses to every RLS policy
- Explicitly DISABLE RLS on tenants, tenant_hostnames, sessions
- Wrap all API route and page loader queries in withTenant()
- Wrap validateSession() in withBypassRLS() (joins RLS-protected users)
- Keep existing tenantId where-clause filters as optimization layer
- Add postgresql-client to Dockerfile and auto-apply RLS via entrypoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Catherine Renelle
2026-02-25 22:54:26 -05:00
parent f6ddfcfc62
commit bd12160ebb
15 changed files with 625 additions and 424 deletions
+20 -18
View File
@@ -1,30 +1,32 @@
import type { PageServerLoad } from './$types.js';
import { prisma } from '$lib/server/db.js';
import { withTenant } from '$lib/server/rls.js';
export const load: PageServerLoad = async ({ locals }) => {
const tenant = locals.tenant;
if (!tenant) return { boards: [] };
const where: any = { tenantId: tenant.id, archived: false };
const boards = await withTenant(tenant.id, async (tx) => {
const where: any = { tenantId: tenant.id, archived: false };
if (!locals.user) {
where.visibility = 'PUBLIC';
} else {
where.OR = [
{ visibility: 'PUBLIC' },
{ members: { some: { userId: locals.user.id } } }
];
}
if (!locals.user) {
where.visibility = 'PUBLIC';
} else {
where.OR = [
{ visibility: 'PUBLIC' },
{ members: { some: { userId: locals.user.id } } }
];
}
const boards = await prisma.board.findMany({
where,
include: {
members: {
include: { user: { select: { id: true, name: true, avatarUrl: true } } }
return tx.board.findMany({
where,
include: {
members: {
include: { user: { select: { id: true, name: true, avatarUrl: true } } }
},
_count: { select: { columns: true } }
},
_count: { select: { columns: true } }
},
orderBy: { updatedAt: 'desc' }
orderBy: { updatedAt: 'desc' }
});
});
return { boards };