Skip to content

Commit 599531d

Browse files
authored
fix: add validation for supabaseUrl (#1547)
* improve validation for supabaseUrl * move call * trim before check * improve regex + more tests
1 parent cae024c commit 599531d

File tree

3 files changed

+44
-5
lines changed

3 files changed

+44
-5
lines changed

src/SupabaseClient.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
DEFAULT_REALTIME_OPTIONS,
2020
} from './lib/constants'
2121
import { fetchWithAuth } from './lib/fetch'
22-
import { ensureTrailingSlash, applySettingDefaults } from './lib/helpers'
22+
import { applySettingDefaults, validateSupabaseUrl } from './lib/helpers'
2323
import { SupabaseAuthClient } from './lib/SupabaseAuthClient'
2424
import { Fetch, GenericSchema, SupabaseClientOptions, SupabaseAuthClientOptions } from './lib/types'
2525

@@ -101,12 +101,9 @@ export default class SupabaseClient<
101101
protected supabaseKey: string,
102102
options?: SupabaseClientOptions<SchemaName>
103103
) {
104-
if (!supabaseUrl) throw new Error('supabaseUrl is required.')
104+
const baseUrl = validateSupabaseUrl(supabaseUrl)
105105
if (!supabaseKey) throw new Error('supabaseKey is required.')
106106

107-
const _supabaseUrl = ensureTrailingSlash(supabaseUrl)
108-
const baseUrl = new URL(_supabaseUrl)
109-
110107
this.realtimeUrl = new URL('realtime/v1', baseUrl)
111108
this.realtimeUrl.protocol = this.realtimeUrl.protocol.replace('http', 'ws')
112109
this.authUrl = new URL('auth/v1', baseUrl)

src/lib/helpers.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,28 @@ export function applySettingDefaults<
7171

7272
return result
7373
}
74+
75+
/**
76+
* Validates a Supabase client URL
77+
*
78+
* @param {string} supabaseUrl - The Supabase client URL string.
79+
* @returns {URL} - The validated base URL.
80+
* @throws {Error}
81+
*/
82+
export function validateSupabaseUrl(supabaseUrl: string): URL {
83+
const trimmedUrl = supabaseUrl?.trim()
84+
85+
if (!trimmedUrl) {
86+
throw new Error('supabaseUrl is required.')
87+
}
88+
89+
if (!trimmedUrl.match(/^https?:\/\//i)) {
90+
throw new Error('Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.')
91+
}
92+
93+
try {
94+
return new URL(ensureTrailingSlash(trimmedUrl))
95+
} catch {
96+
throw Error('Invalid supabaseUrl: Provided URL is malformed.')
97+
}
98+
}

test/unit/SupabaseClient.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,23 @@ describe('SupabaseClient', () => {
2929
expect(() => createClient(URL, '')).toThrow('supabaseKey is required.')
3030
})
3131

32+
test('should validate supabaseUrl', () => {
33+
expect(() => createClient('https://xyz123.supabase.co', KEY)).not.toThrow()
34+
expect(() => createClient('http://localhost:54321', KEY)).not.toThrow()
35+
expect(() => createClient('http://[invalid', KEY)).toThrow(
36+
'Invalid supabaseUrl: Provided URL is malformed.'
37+
)
38+
expect(() =>
39+
createClient('postgresql://postgre:[email protected]:5432/postgres', KEY)
40+
).toThrow('Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.')
41+
expect(() => createClient('http:/localhost:3000', KEY)).toThrow(
42+
'Invalid supabaseUrl: Must be a valid HTTP or HTTPS URL.'
43+
)
44+
45+
expect(() => createClient(' https://xyz123.supabase.co ', KEY)).not.toThrow()
46+
expect(() => createClient('http://user:pass@localhost:54321', KEY)).not.toThrow()
47+
})
48+
3249
describe('URL Construction', () => {
3350
test('should construct URLs correctly', () => {
3451
const client = createClient(URL, KEY)

0 commit comments

Comments
 (0)