fix: use set_config() instead of SET LOCAL for tenant RLS context
PostgreSQL SET commands do not support parameterized queries ($1),
causing "syntax error at or near $1" on all tenant-scoped operations.
Replaced with set_config('app.tenant_id', $1, true) which supports
parameters safely. Also added BEGIN/COMMIT transaction wrapping since
set_config(..., true) requires a transaction for LOCAL scope.
Fixed SQL injection vulnerability in tenant.ts which used unescaped
string interpolation.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -28,13 +28,18 @@ export async function withTenantDb<T>(
|
||||
): Promise<T> {
|
||||
const client: PoolClient = await pool.connect();
|
||||
try {
|
||||
// Set tenant context for RLS — uses parameterized SET to prevent SQL injection
|
||||
await client.query(`SET LOCAL app.tenant_id = $1`, [tenantId]);
|
||||
await client.query('BEGIN');
|
||||
// Set tenant context for RLS — set_config supports parameterized queries
|
||||
// Third arg `true` = LOCAL (scoped to current transaction only)
|
||||
await client.query(`SELECT set_config('app.tenant_id', $1, true)`, [tenantId]);
|
||||
const tenantDb = drizzle(client, { schema });
|
||||
return await callback(tenantDb);
|
||||
const result = await callback(tenantDb);
|
||||
await client.query('COMMIT');
|
||||
return result;
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
throw error;
|
||||
} finally {
|
||||
// RESET ensures no tenant context leaks to the next user of this connection
|
||||
await client.query(`RESET app.tenant_id`);
|
||||
client.release();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,14 @@ export async function withTenant<T>(
|
||||
): Promise<T> {
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
await client.query(`SET LOCAL app.tenant_id = '${tenantId}'`);
|
||||
return await fn(client);
|
||||
await client.query('BEGIN');
|
||||
await client.query(`SELECT set_config('app.tenant_id', $1, true)`, [tenantId]);
|
||||
const result = await fn(client);
|
||||
await client.query('COMMIT');
|
||||
return result;
|
||||
} catch (error) {
|
||||
await client.query('ROLLBACK');
|
||||
throw error;
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user