Skip to content

Commit 98213f8

Browse files
fix: default to SSL for non-localhost connections, remove buggy cleanUrl stripping (#2430)
Co-authored-by: Mariano Fuentes <marfuen98@gmail.com>
1 parent c58045f commit 98213f8

6 files changed

Lines changed: 78 additions & 106 deletions

File tree

apps/api/prisma/client.ts

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,49 @@
11
import { PrismaClient } from '@prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
54
const globalForPrisma = global as unknown as { prisma: PrismaClient };
65

76
/**
8-
* Derive pg SSL config from the DATABASE_URL sslmode parameter.
9-
*
10-
* pg-connection-string parses sslmode=require into ssl: {} (empty object),
11-
* and pg@8+ treats ssl: {} as { rejectUnauthorized: true }, which rejects
12-
* AWS RDS Proxy's self-signed certificate. This is a known pg@8 breaking
13-
* change (https://node-postgres.com/announcements#ssl-by-default).
7+
* Derive pg SSL config from the DATABASE_URL.
148
*
15-
* Per PostgreSQL spec (https://www.postgresql.org/docs/current/libpq-ssl.html):
16-
* - require: encrypt connection, skip certificate verification
17-
* - verify-ca: encrypt + verify server CA
18-
* - verify-full: encrypt + verify CA + verify hostname
9+
* pg@8+ defaults rejectUnauthorized to true, which rejects AWS RDS Proxy's
10+
* certificate (signed by internal AWS CA, not in Node.js root CA store).
1911
*
20-
* Per Prisma v7 migration docs: "SSL certificate defaults have changed.
21-
* Previously [v6 Rust engine], invalid SSL certificates were ignored."
22-
* (https://www.prisma.io/docs/orm/more/upgrades/to-v7)
12+
* Per PostgreSQL sslmode spec:
13+
* - disable: no SSL
14+
* - require: encrypt, skip certificate verification
15+
* - verify-ca: encrypt + verify CA
16+
* - verify-full: encrypt + verify CA + hostname
2317
*
24-
* Our infra enforces sslmode=require with RDS Proxy (requireTls: true).
25-
* The proxy's certificate is signed by an internal AWS CA not in Node.js's
26-
* root CA store, so rejectUnauthorized: false is required. The connection
27-
* is still TLS-encrypted — only identity verification is skipped.
18+
* When no sslmode is set, we default to SSL with rejectUnauthorized: false
19+
* for non-localhost connections (matches Prisma v6 behavior where the Rust
20+
* engine silently accepted all certificates).
2821
*/
2922
function getSslConfig(url: string): boolean | { rejectUnauthorized: boolean } | undefined {
30-
const match = url.match(/sslmode=(\w[\w-]*)/);
31-
if (!match) return undefined;
23+
const sslmodeMatch = url.match(/sslmode=(\w[\w-]*)/);
3224

33-
const mode = match[1];
34-
switch (mode) {
35-
case 'disable':
36-
return undefined;
37-
case 'require':
38-
case 'no-verify':
39-
return { rejectUnauthorized: false };
40-
case 'verify-ca':
41-
case 'verify-full':
42-
return { rejectUnauthorized: true };
43-
default:
44-
return undefined;
25+
if (sslmodeMatch) {
26+
switch (sslmodeMatch[1]) {
27+
case 'disable':
28+
return undefined;
29+
case 'require':
30+
case 'no-verify':
31+
return { rejectUnauthorized: false };
32+
case 'verify-ca':
33+
case 'verify-full':
34+
return { rejectUnauthorized: true };
35+
}
4536
}
37+
38+
// No sslmode specified — enable SSL for non-localhost (production default)
39+
const isLocalhost = /localhost|127\.0\.0\.1|::1/.test(url);
40+
return isLocalhost ? undefined : { rejectUnauthorized: false };
4641
}
4742

4843
function createPrismaClient(): PrismaClient {
4944
const url = process.env.DATABASE_URL!;
5045
const ssl = getSslConfig(url);
51-
// Strip sslmode from connection string — pg parses it independently and
52-
// can override our explicit ssl config. We handle SSL entirely via the ssl option.
53-
const cleanUrl = url.replace(/[?&]sslmode=\w[\w-]*/g, '').replace(/\?&/, '?').replace(/\?$/, '');
54-
const adapter = new PrismaPg({ connectionString: cleanUrl, ssl });
46+
const adapter = new PrismaPg({ connectionString: url, ssl });
5547
return new PrismaClient({ adapter });
5648
}
5749

apps/app/prisma/client.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
11
import { PrismaClient } from '../src/generated/prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
54
const globalForPrisma = global as unknown as { prisma: PrismaClient };
65

7-
/**
8-
* Derive pg SSL config from the DATABASE_URL sslmode parameter.
9-
* See apps/api/prisma/client.ts for detailed documentation.
10-
*/
116
function getSslConfig(url: string): boolean | { rejectUnauthorized: boolean } | undefined {
12-
const match = url.match(/sslmode=(\w[\w-]*)/);
13-
if (!match) return undefined;
14-
const mode = match[1];
15-
switch (mode) {
16-
case 'disable': return undefined;
17-
case 'require': case 'no-verify': return { rejectUnauthorized: false };
18-
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
19-
default: return undefined;
7+
const sslmodeMatch = url.match(/sslmode=(\w[\w-]*)/);
8+
if (sslmodeMatch) {
9+
switch (sslmodeMatch[1]) {
10+
case 'disable': return undefined;
11+
case 'require': case 'no-verify': return { rejectUnauthorized: false };
12+
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
13+
}
2014
}
15+
const isLocalhost = /localhost|127\.0\.0\.1|::1/.test(url);
16+
return isLocalhost ? undefined : { rejectUnauthorized: false };
2117
}
2218

2319
function createPrismaClient(): PrismaClient {
2420
const url = process.env.DATABASE_URL!;
2521
const ssl = getSslConfig(url);
26-
const cleanUrl = url.replace(/[?&]sslmode=\w[\w-]*/g, '').replace(/\?&/, '?').replace(/\?$/, '');
27-
const adapter = new PrismaPg({ connectionString: cleanUrl, ssl });
22+
const adapter = new PrismaPg({ connectionString: url, ssl });
2823
return new PrismaClient({ adapter });
2924
}
3025

apps/framework-editor/prisma/client.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
11
import { PrismaClient } from '@prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
54
const globalForPrisma = global as unknown as { prisma: PrismaClient };
65

7-
/**
8-
* Derive pg SSL config from the DATABASE_URL sslmode parameter.
9-
* See apps/api/prisma/client.ts for detailed documentation.
10-
*/
116
function getSslConfig(url: string): boolean | { rejectUnauthorized: boolean } | undefined {
12-
const match = url.match(/sslmode=(\w[\w-]*)/);
13-
if (!match) return undefined;
14-
const mode = match[1];
15-
switch (mode) {
16-
case 'disable': return undefined;
17-
case 'require': case 'no-verify': return { rejectUnauthorized: false };
18-
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
19-
default: return undefined;
7+
const sslmodeMatch = url.match(/sslmode=(\w[\w-]*)/);
8+
if (sslmodeMatch) {
9+
switch (sslmodeMatch[1]) {
10+
case 'disable': return undefined;
11+
case 'require': case 'no-verify': return { rejectUnauthorized: false };
12+
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
13+
}
2014
}
15+
const isLocalhost = /localhost|127\.0\.0\.1|::1/.test(url);
16+
return isLocalhost ? undefined : { rejectUnauthorized: false };
2117
}
2218

2319
function createPrismaClient(): PrismaClient {
2420
const url = process.env.DATABASE_URL!;
2521
const ssl = getSslConfig(url);
26-
const cleanUrl = url.replace(/[?&]sslmode=\w[\w-]*/g, '').replace(/\?&/, '?').replace(/\?$/, '');
27-
const adapter = new PrismaPg({ connectionString: cleanUrl, ssl });
22+
const adapter = new PrismaPg({ connectionString: url, ssl });
2823
return new PrismaClient({ adapter });
2924
}
3025

apps/portal/prisma/client.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
11
import { PrismaClient } from '../src/generated/prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
54
const globalForPrisma = global as unknown as { prisma: PrismaClient };
65

7-
/**
8-
* Derive pg SSL config from the DATABASE_URL sslmode parameter.
9-
* See apps/api/prisma/client.ts for detailed documentation.
10-
*/
116
function getSslConfig(url: string): boolean | { rejectUnauthorized: boolean } | undefined {
12-
const match = url.match(/sslmode=(\w[\w-]*)/);
13-
if (!match) return undefined;
14-
const mode = match[1];
15-
switch (mode) {
16-
case 'disable': return undefined;
17-
case 'require': case 'no-verify': return { rejectUnauthorized: false };
18-
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
19-
default: return undefined;
7+
const sslmodeMatch = url.match(/sslmode=(\w[\w-]*)/);
8+
if (sslmodeMatch) {
9+
switch (sslmodeMatch[1]) {
10+
case 'disable': return undefined;
11+
case 'require': case 'no-verify': return { rejectUnauthorized: false };
12+
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
13+
}
2014
}
15+
const isLocalhost = /localhost|127\.0\.0\.1|::1/.test(url);
16+
return isLocalhost ? undefined : { rejectUnauthorized: false };
2117
}
2218

2319
function createPrismaClient(): PrismaClient {
2420
const url = process.env.DATABASE_URL!;
2521
const ssl = getSslConfig(url);
26-
const cleanUrl = url.replace(/[?&]sslmode=\w[\w-]*/g, '').replace(/\?&/, '?').replace(/\?$/, '');
27-
const adapter = new PrismaPg({ connectionString: cleanUrl, ssl });
22+
const adapter = new PrismaPg({ connectionString: url, ssl });
2823
return new PrismaClient({ adapter });
2924
}
3025

packages/db/scripts/combine-schemas.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,22 @@ import { PrismaPg } from '@prisma/adapter-pg';
5555
const globalForPrisma = global as unknown as { prisma: PrismaClient };
5656
5757
function getSslConfig(url: string) {
58-
const match = url.match(/sslmode=(\\w[\\w-]*)/);
59-
if (!match) return undefined;
60-
const mode = match[1];
61-
switch (mode) {
62-
case 'disable': return undefined;
63-
case 'require': case 'no-verify': return { rejectUnauthorized: false };
64-
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
65-
default: return undefined;
58+
const sslmodeMatch = url.match(/sslmode=(\\w[\\w-]*)/);
59+
if (sslmodeMatch) {
60+
switch (sslmodeMatch[1]) {
61+
case 'disable': return undefined;
62+
case 'require': case 'no-verify': return { rejectUnauthorized: false };
63+
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
64+
}
6665
}
66+
const isLocalhost = /localhost|127\\.0\\.0\\.1|::1/.test(url);
67+
return isLocalhost ? undefined : { rejectUnauthorized: false };
6768
}
6869
6970
function createPrismaClient(): PrismaClient {
7071
const url = process.env.DATABASE_URL!;
7172
const ssl = getSslConfig(url);
72-
const cleanUrl = url.replace(/[?&]sslmode=\\w[\\w-]*/g, '').replace(/\\?&/, '?').replace(/\\?$/, '');
73-
const adapter = new PrismaPg({ connectionString: cleanUrl, ssl });
73+
const adapter = new PrismaPg({ connectionString: url, ssl });
7474
return new PrismaClient({ adapter });
7575
}
7676

packages/db/src/client.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
11
import { PrismaClient } from '@prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
54
const globalForPrisma = global as unknown as { prisma: PrismaClient };
65

7-
/**
8-
* Derive pg SSL config from the DATABASE_URL sslmode parameter.
9-
* See apps/api/prisma/client.ts for detailed documentation.
10-
*/
116
function getSslConfig(url: string): boolean | { rejectUnauthorized: boolean } | undefined {
12-
const match = url.match(/sslmode=(\w[\w-]*)/);
13-
if (!match) return undefined;
14-
const mode = match[1];
15-
switch (mode) {
16-
case 'disable': return undefined;
17-
case 'require': case 'no-verify': return { rejectUnauthorized: false };
18-
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
19-
default: return undefined;
7+
const sslmodeMatch = url.match(/sslmode=(\w[\w-]*)/);
8+
if (sslmodeMatch) {
9+
switch (sslmodeMatch[1]) {
10+
case 'disable': return undefined;
11+
case 'require': case 'no-verify': return { rejectUnauthorized: false };
12+
case 'verify-ca': case 'verify-full': return { rejectUnauthorized: true };
13+
}
2014
}
15+
const isLocalhost = /localhost|127\.0\.0\.1|::1/.test(url);
16+
return isLocalhost ? undefined : { rejectUnauthorized: false };
2117
}
2218

2319
function createPrismaClient(): PrismaClient {
2420
const url = process.env.DATABASE_URL!;
2521
const ssl = getSslConfig(url);
26-
const cleanUrl = url.replace(/[?&]sslmode=\w[\w-]*/g, '').replace(/\?&/, '?').replace(/\?$/, '');
27-
const adapter = new PrismaPg({ connectionString: cleanUrl, ssl });
22+
const adapter = new PrismaPg({ connectionString: url, ssl });
2823
return new PrismaClient({ adapter });
2924
}
3025

0 commit comments

Comments
 (0)