mirror of
https://github.com/supabase/supabase.git
synced 2026-06-01 10:21:10 +08:00
Chore/update url regex (#30138)
* Allow excluding options in the url regex * Add tests for options
This commit is contained in:
@@ -26,8 +26,18 @@ const customSchemeRegex = /^([a-zA-Z][a-zA-Z0-9+.-]*):(?:\/{1,3})?([a-zA-Z0-9_.-
|
||||
// Exclude simple domain names without protocol
|
||||
const excludeSimpleDomainRegex = /^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$/
|
||||
|
||||
// combine the above regexes
|
||||
export const urlRegex = new RegExp(
|
||||
`(?!${excludeSimpleDomainRegex.source})((${baseUrlRegex.source})|(${localhostRegex.source})|(${appRegex.source})|(${chromeExtensionRegex.source})|(${customSchemeRegex.source}))`,
|
||||
'i'
|
||||
)
|
||||
// combine the above regexes, with optional exclusion of options
|
||||
// usage: urlRegex() or urlRegex({ excludeSimpleDomains: false })
|
||||
export function urlRegex(
|
||||
options: { excludeSimpleDomains?: boolean } = { excludeSimpleDomains: true }
|
||||
): RegExp {
|
||||
const { excludeSimpleDomains } = options
|
||||
const excludeSimpleDomainPart = excludeSimpleDomains
|
||||
? `(?!${excludeSimpleDomainRegex.source})`
|
||||
: ''
|
||||
|
||||
return new RegExp(
|
||||
`${excludeSimpleDomainPart}((${baseUrlRegex.source})|(${localhostRegex.source})|(${appRegex.source})|(${chromeExtensionRegex.source})|(${customSchemeRegex.source}))`,
|
||||
'i'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -570,7 +570,7 @@ const EXTERNAL_PROVIDER_AZURE = {
|
||||
then: (schema) => schema.required('Secret Value is required'),
|
||||
otherwise: (schema) => schema,
|
||||
}),
|
||||
EXTERNAL_AZURE_URL: string().matches(urlRegex, 'Must be a valid URL').optional(),
|
||||
EXTERNAL_AZURE_URL: string().matches(urlRegex(), 'Must be a valid URL').optional(),
|
||||
}),
|
||||
misc: {
|
||||
iconKey: 'microsoft-icon',
|
||||
@@ -812,7 +812,7 @@ const EXTERNAL_PROVIDER_GITLAB = {
|
||||
then: (schema) => schema.required('Client Secret is required'),
|
||||
otherwise: (schema) => schema,
|
||||
}),
|
||||
EXTERNAL_GITLAB_URL: string().matches(urlRegex, 'Must be a valid URL').optional(),
|
||||
EXTERNAL_GITLAB_URL: string().matches(urlRegex(), 'Must be a valid URL').optional(),
|
||||
}),
|
||||
misc: {
|
||||
iconKey: 'gitlab-icon',
|
||||
@@ -1007,8 +1007,8 @@ const EXTERNAL_PROVIDER_KEYCLOAK = {
|
||||
EXTERNAL_KEYCLOAK_URL: string().when('EXTERNAL_KEYCLOAK_ENABLED', {
|
||||
is: true,
|
||||
then: (schema) =>
|
||||
schema.matches(urlRegex, 'Must be a valid URL').required('Realm URL is required'),
|
||||
otherwise: (schema) => schema.matches(urlRegex, 'Must be a valid URL'),
|
||||
schema.matches(urlRegex(), 'Must be a valid URL').required('Realm URL is required'),
|
||||
otherwise: (schema) => schema.matches(urlRegex(), 'Must be a valid URL'),
|
||||
}),
|
||||
}),
|
||||
misc: {
|
||||
@@ -1317,7 +1317,7 @@ const EXTERNAL_PROVIDER_WORKOS = {
|
||||
validationSchema: object().shape({
|
||||
EXTERNAL_WORKOS_ENABLED: boolean().required(),
|
||||
EXTERNAL_WORKOS_URL: string()
|
||||
.matches(urlRegex, 'Must be a valid URL')
|
||||
.matches(urlRegex(), 'Must be a valid URL')
|
||||
.when('EXTERNAL_WORKOS_ENABLED', {
|
||||
is: true,
|
||||
then: (schema) => schema.required('WorkOS URL is required'),
|
||||
@@ -1408,7 +1408,7 @@ const PROVIDER_SAML = {
|
||||
},
|
||||
validationSchema: object().shape({
|
||||
SAML_ENABLED: boolean().required(),
|
||||
SAML_EXTERNAL_URL: string().matches(urlRegex, 'Must be a valid URL').optional(),
|
||||
SAML_EXTERNAL_URL: string().matches(urlRegex(), 'Must be a valid URL').optional(),
|
||||
SAML_ALLOW_ENCRYPTED_ASSERTIONS: boolean().optional(),
|
||||
}),
|
||||
misc: {
|
||||
|
||||
@@ -40,7 +40,7 @@ export const AddNewURLModal = ({ visible, allowList, onClose }: AddNewURLModalPr
|
||||
value: z
|
||||
.string()
|
||||
.min(1, 'Please provide a value')
|
||||
.regex(urlRegex, 'Please provide a valid URL')
|
||||
.regex(urlRegex(), 'Please provide a valid URL')
|
||||
.refine((value) => !allowList.includes(value), {
|
||||
message: 'URL already exists in the allow list',
|
||||
}),
|
||||
|
||||
@@ -76,7 +76,7 @@ const SmtpForm = () => {
|
||||
},
|
||||
then: (schema) =>
|
||||
schema
|
||||
.matches(urlRegex, 'Must be a valid URL or IP address')
|
||||
.matches(urlRegex({ excludeSimpleDomains: false }), 'Must be a valid URL or IP address')
|
||||
.required('Host URL is required.'),
|
||||
otherwise: (schema) => schema,
|
||||
}),
|
||||
|
||||
@@ -66,7 +66,7 @@ const httpRequestSchema = z.object({
|
||||
.string()
|
||||
.trim()
|
||||
.min(1, 'Please provide a URL')
|
||||
.regex(urlRegex, 'Please provide a valid URL')
|
||||
.regex(urlRegex(), 'Please provide a valid URL')
|
||||
.refine((value) => value.startsWith('http'), 'Please include HTTP/HTTPs to your URL'),
|
||||
timeoutMs: z.coerce.number().int().gte(1000).lte(5000).default(1000),
|
||||
httpHeaders: z.array(z.object({ name: z.string(), value: z.string() })),
|
||||
|
||||
@@ -45,7 +45,7 @@ const FORM_ID = 'log-drain-destination-form'
|
||||
const formUnion = z.discriminatedUnion('type', [
|
||||
z.object({
|
||||
type: z.literal('webhook'),
|
||||
url: z.string().regex(urlRegex, 'Endpoint URL is required and must be a valid URL'),
|
||||
url: z.string().regex(urlRegex(), 'Endpoint URL is required and must be a valid URL'),
|
||||
http: z.enum(['http1', 'http2']),
|
||||
gzip: z.boolean(),
|
||||
headers: z.record(z.string(), z.string()).optional(),
|
||||
|
||||
@@ -22,20 +22,30 @@ describe('Auth.constants: urlRegex', () => {
|
||||
]
|
||||
|
||||
validUrls.forEach((url) => {
|
||||
expect(urlRegex.test(url)).toBe(true)
|
||||
expect(urlRegex().test(url)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not match invalid URLs', () => {
|
||||
const invalidUrls = ['supabase', 'mailto:test@gmail.com', 'hello world.com', 'email@domain.com']
|
||||
|
||||
const failingInvalidUrls = invalidUrls.filter((url) => urlRegex.test(url))
|
||||
const failingInvalidUrls = invalidUrls.filter((url) => urlRegex().test(url))
|
||||
if (failingInvalidUrls.length > 0) {
|
||||
console.log('Failing invalid URLs:', failingInvalidUrls)
|
||||
}
|
||||
|
||||
invalidUrls.forEach((url) => {
|
||||
expect(urlRegex.test(url)).toBe(false)
|
||||
expect(urlRegex().test(url)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
it('should not match simple domain URLs when excludeSimpleDomains is true', () => {
|
||||
const simpleDomainUrl = 'smtp-pulse.com'
|
||||
expect(urlRegex({ excludeSimpleDomains: true }).test(simpleDomainUrl)).toBe(false)
|
||||
})
|
||||
|
||||
it('should match simple domain URLs when excludeSimpleDomains is false', () => {
|
||||
const simpleDomainUrl = 'smtp-pulse.com'
|
||||
expect(urlRegex({ excludeSimpleDomains: false }).test(simpleDomainUrl)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user