Skip to content

Commit

Permalink
Chore/update url regex (supabase#30138)
Browse files Browse the repository at this point in the history
* Allow excluding options in the url regex

* Add tests for options
  • Loading branch information
saltcod authored Oct 29, 2024
1 parent e537cf3 commit 110e8ed
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 18 deletions.
20 changes: 15 additions & 5 deletions apps/studio/components/interfaces/Auth/Auth.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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'),
Expand Down Expand Up @@ -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: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() })),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
16 changes: 13 additions & 3 deletions apps/studio/tests/components/Auth/Auth.constants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})
})

0 comments on commit 110e8ed

Please sign in to comment.