File tree Expand file tree Collapse file tree 6 files changed +38
-9
lines changed
Expand file tree Collapse file tree 6 files changed +38
-9
lines changed Original file line number Diff line number Diff line change @@ -6,11 +6,11 @@ import type { TokenBucketConfig } from '@/lib/core/rate-limiter'
66import { RateLimiter } from '@/lib/core/rate-limiter'
77import { generateRequestId } from '@/lib/core/utils/request'
88import { getEmailDomain } from '@/lib/core/utils/urls'
9+ import { NO_EMAIL_HEADER_CONTROL_CHARS_REGEX } from '@/lib/messaging/email/header-safety'
910import { sendEmail } from '@/lib/messaging/email/mailer'
1011import { getFromEmailAddress } from '@/lib/messaging/email/utils'
1112
1213const logger = createLogger ( 'IntegrationRequestAPI' )
13- const NO_EMAIL_HEADER_CONTROL_CHARS_REGEX = / ^ [ ^ \r \n ] * $ /
1414
1515const rateLimiter = new RateLimiter ( )
1616
Original file line number Diff line number Diff line change @@ -6,11 +6,11 @@ import { getSession } from '@/lib/auth'
66import { env } from '@/lib/core/config/env'
77import { generateRequestId } from '@/lib/core/utils/request'
88import { getEmailDomain } from '@/lib/core/utils/urls'
9+ import { NO_EMAIL_HEADER_CONTROL_CHARS_REGEX } from '@/lib/messaging/email/header-safety'
910import { sendEmail } from '@/lib/messaging/email/mailer'
1011import { getFromEmailAddress } from '@/lib/messaging/email/utils'
1112
1213const logger = createLogger ( 'HelpAPI' )
13- const NO_EMAIL_HEADER_CONTROL_CHARS_REGEX = / ^ [ ^ \r \n ] * $ /
1414
1515const helpFormSchema = z . object ( {
1616 subject : z
Original file line number Diff line number Diff line change 11import { z } from 'zod'
22import { quickValidateEmail } from '@/lib/messaging/email/validation'
3-
4- const NO_EMAIL_HEADER_CONTROL_CHARS_REGEX = / ^ [ ^ \r \n ] * $ /
3+ import { NO_EMAIL_HEADER_CONTROL_CHARS_REGEX } from '@/lib/messaging/email/header-safety'
54
65export const DEMO_REQUEST_REGION_VALUES = [
76 'north_america' ,
Original file line number Diff line number Diff line change 1+ /**
2+ * @vitest -environment node
3+ */
4+ import { describe , expect , it } from 'vitest'
5+ import {
6+ EMAIL_HEADER_CONTROL_CHARS_REGEX ,
7+ hasEmailHeaderControlChars ,
8+ NO_EMAIL_HEADER_CONTROL_CHARS_REGEX ,
9+ } from '@/lib/messaging/email/header-safety'
10+
11+ describe ( 'email header safety' , ( ) => {
12+ it ( 'rejects CRLF characters consistently' , ( ) => {
13+ const injectedHeader = 'Hello\r\nBcc: attacker@example.com'
14+
15+ expect ( EMAIL_HEADER_CONTROL_CHARS_REGEX . test ( injectedHeader ) ) . toBe ( true )
16+ expect ( hasEmailHeaderControlChars ( injectedHeader ) ) . toBe ( true )
17+ expect ( NO_EMAIL_HEADER_CONTROL_CHARS_REGEX . test ( injectedHeader ) ) . toBe ( false )
18+ } )
19+
20+ it ( 'allows plain header content' , ( ) => {
21+ const safeHeader = 'Product feedback'
22+
23+ expect ( EMAIL_HEADER_CONTROL_CHARS_REGEX . test ( safeHeader ) ) . toBe ( false )
24+ expect ( hasEmailHeaderControlChars ( safeHeader ) ) . toBe ( false )
25+ expect ( NO_EMAIL_HEADER_CONTROL_CHARS_REGEX . test ( safeHeader ) ) . toBe ( true )
26+ } )
27+ } )
Original file line number Diff line number Diff line change 1+ export const EMAIL_HEADER_CONTROL_CHARS_REGEX = / [ \r \n ] /
2+
3+ export const NO_EMAIL_HEADER_CONTROL_CHARS_REGEX = / ^ [ ^ \r \n ] * $ /
4+
5+ export function hasEmailHeaderControlChars ( value : string ) : boolean {
6+ return EMAIL_HEADER_CONTROL_CHARS_REGEX . test ( value )
7+ }
Original file line number Diff line number Diff line change @@ -3,11 +3,11 @@ import { createLogger } from '@sim/logger'
33import { Resend } from 'resend'
44import { env } from '@/lib/core/config/env'
55import { getBaseUrl } from '@/lib/core/utils/urls'
6+ import { hasEmailHeaderControlChars } from '@/lib/messaging/email/header-safety'
67import { generateUnsubscribeToken , isUnsubscribed } from '@/lib/messaging/email/unsubscribe'
78import { getFromEmailAddress } from '@/lib/messaging/email/utils'
89
910const logger = createLogger ( 'Mailer' )
10- const EMAIL_HEADER_CONTROL_CHARS_REGEX = / [ \r \n ] /
1111
1212export type EmailType = 'transactional' | 'marketing' | 'updates' | 'notifications'
1313
@@ -65,10 +65,6 @@ interface PreparedEmailHeaderData {
6565 replyTo ?: string
6666}
6767
68- function hasEmailHeaderControlChars ( value : string ) : boolean {
69- return EMAIL_HEADER_CONTROL_CHARS_REGEX . test ( value )
70- }
71-
7268function sanitizeEmailSubject ( subject : string ) : string {
7369 return subject . replace ( / [ \r \n ] + / g, ' ' ) . trim ( )
7470}
You can’t perform that action at this time.
0 commit comments