Improve rate limit on sendInvite (#8150)

Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
Andrey Sobolev 2025-03-06 17:25:37 +07:00 committed by Andrey Sobolev
parent 622844c42f
commit cc7178a1d4
No known key found for this signature in database
GPG Key ID: BD80F68D68D8F7F2
2 changed files with 44 additions and 1 deletions

View File

@ -0,0 +1,33 @@
import { sanitizeEmail } from '../utils'
describe('sanitizeEmail', () => {
it('should lowercase and trim email', () => {
expect(sanitizeEmail(' Test@Example.com ')).toBe('TestExample.com')
})
it('should remove special characters', () => {
expect(sanitizeEmail('test<>/@{}[]example.com')).toBe('testexample.com')
})
it('should remove dangerous protocols', () => {
expect(sanitizeEmail('javascript:test@example.com')).toBe('testexample.com')
expect(sanitizeEmail('mailto:test@example.com')).toBe('testexample.com')
expect(sanitizeEmail('http://test@example.com')).toBe('testexample.com')
})
it('should limit length to 40 characters', () => {
const longEmail = 'verylongemailthatismorethanfortycharacters@example.com'
expect(sanitizeEmail(longEmail).length).toBe(40)
})
it('should handle null or invalid input', () => {
expect(sanitizeEmail('')).toBe('')
expect(sanitizeEmail(null as any)).toBe('')
expect(sanitizeEmail(undefined as any)).toBe('')
})
it('should preserve valid email addresses', () => {
expect(sanitizeEmail('user.name+tag@example.com')).toBe('user.name+tagexample.com')
expect(sanitizeEmail('test-email@domain.co.uk')).toBe('test-emaildomain.co.uk')
})
})

View File

@ -1183,6 +1183,16 @@ export async function sendEmail (info: EmailInfo): Promise<void> {
})
}
export function sanitizeEmail (email: string): string {
if (email == null || typeof email !== 'string') return ''
const sanitizedEmail = email
.trim()
.replace(/[<>/\\@{}()[\]'"`]/g, '') // Remove special chars and quotes
.replace(/^(http|ssh|ftp|https|mailto|javascript|data|file):?\/?\/?\s*/i, '') // Remove potentially dangerous protocols
.slice(0, 40)
return sanitizedEmail
}
export async function getInviteEmail (
branding: Branding | null,
email: string,
@ -1193,7 +1203,7 @@ export async function getInviteEmail (
): Promise<EmailInfo> {
const front = getFrontUrl(branding)
const link = concatLink(front, `/login/join?inviteId=${inviteId}`)
const ws = (workspace.name !== '' ? workspace.name : workspace.url).replace(/[<>/]/g, '').slice(0, 40)
const ws = sanitizeEmail(workspace.name !== '' ? workspace.name : workspace.url)
const lang = branding?.language
return {