fix: harden identity HTTP transport
This commit is contained in:
@@ -6,6 +6,8 @@ export type IdentityDrizzleDatabase = NodePgDatabase<typeof identityAdapterSchem
|
||||
|
||||
export type IdentityDatabaseErrorCode = "IDENTITY_DATABASE_CONFIGURATION_INVALID";
|
||||
|
||||
const maximumPostgresTimeoutMs = 2_147_483_647;
|
||||
|
||||
/** Never carries the offending connection string, host, or underlying driver error. */
|
||||
export class IdentityDatabaseError extends Error {
|
||||
readonly code: IdentityDatabaseErrorCode;
|
||||
@@ -21,6 +23,11 @@ export class IdentityDatabaseError extends Error {
|
||||
|
||||
export interface IdentityDatabaseOptions {
|
||||
readonly connectionTimeoutMs?: number;
|
||||
/** Bounds both `query_timeout` and `statement_timeout`: a hung or unexpectedly slow query on
|
||||
* the identity connection is aborted rather than holding the pool connection (and the caller)
|
||||
* indefinitely. `connectionTimeoutMs` alone only bounds establishing a connection — it does
|
||||
* nothing once a query is already running on one. */
|
||||
readonly operationTimeoutMs?: number;
|
||||
}
|
||||
|
||||
export interface IdentityDatabaseHandle {
|
||||
@@ -44,6 +51,24 @@ function validateConnectionString(connectionString: string): string {
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
/** Mirrors `core/src/migrations/postgres-database.ts`'s timeout validation exactly: an explicit
|
||||
* timeout must be a positive safe integer no larger than PostgreSQL's own maximum, or it is
|
||||
* rejected as invalid configuration rather than silently passed through to `pg`. */
|
||||
function validateTimeout(value: unknown): number | undefined {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (
|
||||
typeof value !== "number" ||
|
||||
!Number.isSafeInteger(value) ||
|
||||
value <= 0 ||
|
||||
value > maximumPostgresTimeoutMs
|
||||
) {
|
||||
throw new IdentityDatabaseError("IDENTITY_DATABASE_CONFIGURATION_INVALID");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly constructs the Core-owned Drizzle database instance used by the identity factory.
|
||||
* Building the `pg.Pool` performs no network I/O until a query is issued, and this function is
|
||||
@@ -54,10 +79,20 @@ export function createIdentityDrizzleDatabase(
|
||||
options: IdentityDatabaseOptions = {},
|
||||
): IdentityDatabaseHandle {
|
||||
const validatedConnectionString = validateConnectionString(connectionString);
|
||||
const poolConfig: PoolConfig = { connectionString: validatedConnectionString };
|
||||
if (options.connectionTimeoutMs !== undefined) {
|
||||
poolConfig.connectionTimeoutMillis = options.connectionTimeoutMs;
|
||||
}
|
||||
const validatedConnectionTimeoutMs = validateTimeout(options.connectionTimeoutMs);
|
||||
const validatedOperationTimeoutMs = validateTimeout(options.operationTimeoutMs);
|
||||
const poolConfig: PoolConfig = {
|
||||
connectionString: validatedConnectionString,
|
||||
...(validatedConnectionTimeoutMs === undefined
|
||||
? {}
|
||||
: { connectionTimeoutMillis: validatedConnectionTimeoutMs }),
|
||||
...(validatedOperationTimeoutMs === undefined
|
||||
? {}
|
||||
: {
|
||||
query_timeout: validatedOperationTimeoutMs,
|
||||
statement_timeout: validatedOperationTimeoutMs,
|
||||
}),
|
||||
};
|
||||
|
||||
const pool = new Pool(poolConfig);
|
||||
// pg's own documentation requires an error listener on the pool: an idle client can be
|
||||
|
||||
Reference in New Issue
Block a user