Skip to content

Commit c353582

Browse files
authored
Merge pull request #932 from supabase/fix/catch-invalid-url-early
fix: catch invalid url early
2 parents 02933a1 + 86a1007 commit c353582

File tree

2 files changed

+83
-14
lines changed

2 files changed

+83
-14
lines changed

src/server/routes/index.ts

+29-14
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,39 @@ import { PG_CONNECTION, CRYPTO_KEY } from '../constants.js'
2626
export default async (fastify: FastifyInstance) => {
2727
// Adds a "pg" object to the request if it doesn't exist
2828
fastify.addHook('onRequest', (request, _reply, done) => {
29-
// Node converts headers to lowercase
30-
const encryptedHeader = request.headers['x-connection-encrypted']?.toString()
31-
if (encryptedHeader) {
29+
try {
30+
// Node converts headers to lowercase
31+
const encryptedHeader = request.headers['x-connection-encrypted']?.toString()
32+
if (encryptedHeader) {
33+
try {
34+
request.headers.pg = CryptoJS.AES.decrypt(encryptedHeader, CRYPTO_KEY)
35+
.toString(CryptoJS.enc.Utf8)
36+
.trim()
37+
} catch (e: any) {
38+
request.log.warn({
39+
message: 'failed to parse encrypted connstring',
40+
error: e.toString(),
41+
})
42+
throw new Error('failed to process upstream connection details')
43+
}
44+
} else {
45+
request.headers.pg = PG_CONNECTION
46+
}
47+
if (!request.headers.pg) {
48+
request.log.error({ message: 'failed to get connection string' })
49+
throw new Error('failed to get upstream connection details')
50+
}
51+
// Ensure the resulting connection string is a valid URL
3252
try {
33-
request.headers.pg = CryptoJS.AES.decrypt(encryptedHeader, CRYPTO_KEY).toString(
34-
CryptoJS.enc.Utf8
35-
)
36-
} catch (e: any) {
37-
request.log.warn({
38-
message: 'failed to parse encrypted connstring',
39-
error: e.toString(),
40-
})
53+
new URL(request.headers.pg)
54+
} catch (error) {
55+
request.log.error({ message: 'pg connection string is invalid url' })
4156
throw new Error('failed to process upstream connection details')
4257
}
43-
} else {
44-
request.headers.pg = PG_CONNECTION
58+
return done()
59+
} catch (err) {
60+
return done(err as Error)
4561
}
46-
done()
4762
})
4863

4964
fastify.register(ColumnPrivilegesRoute, { prefix: '/column-privileges' })

test/server/ssl.ts

+54
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,57 @@ test('query with ssl with root cert', async () => {
7272

7373
DEFAULT_POOL_CONFIG.ssl = defaultSsl
7474
})
75+
76+
test('query with invalid space empty encrypted connection string', async () => {
77+
const res = await app.inject({
78+
method: 'POST',
79+
path: '/query',
80+
headers: {
81+
'x-connection-encrypted': CryptoJS.AES.encrypt(` `, CRYPTO_KEY).toString(),
82+
},
83+
payload: { query: 'select 1;' },
84+
})
85+
expect(res.statusCode).toBe(500)
86+
expect(res.json()).toMatchInlineSnapshot(`
87+
{
88+
"error": "failed to get upstream connection details",
89+
}
90+
`)
91+
})
92+
93+
test('query with invalid empty encrypted connection string', async () => {
94+
const res = await app.inject({
95+
method: 'POST',
96+
path: '/query',
97+
headers: {
98+
'x-connection-encrypted': CryptoJS.AES.encrypt(``, CRYPTO_KEY).toString(),
99+
},
100+
payload: { query: 'select 1;' },
101+
})
102+
expect(res.statusCode).toBe(500)
103+
expect(res.json()).toMatchInlineSnapshot(`
104+
{
105+
"error": "failed to get upstream connection details",
106+
}
107+
`)
108+
})
109+
110+
test('query with missing host connection string encrypted connection string', async () => {
111+
const res = await app.inject({
112+
method: 'POST',
113+
path: '/query',
114+
headers: {
115+
'x-connection-encrypted': CryptoJS.AES.encrypt(
116+
`postgres://name:password@:5432/postgres?sslmode=prefer`,
117+
CRYPTO_KEY
118+
).toString(),
119+
},
120+
payload: { query: 'select 1;' },
121+
})
122+
expect(res.statusCode).toBe(500)
123+
expect(res.json()).toMatchInlineSnapshot(`
124+
{
125+
"error": "failed to process upstream connection details",
126+
}
127+
`)
128+
})

0 commit comments

Comments
 (0)