From e4a16094d6ceb4c368f0b745d9820636a0212c3d Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Sun, 14 Sep 2025 21:52:55 +0200 Subject: [PATCH] Eliminate code duplication in email sanitization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create centralized sanitization utilities in utils/helpers.ts - Add sanitizeDisplayName() with configurable quote escaping - Add sanitizeFromName() wrapper for consistent fromName handling - Replace duplicated sanitization logic in sendEmail.ts (9 lines → 1 line) - Replace duplicated sanitization logic in MailingService.ts (9 lines → 1 line) - Export new utilities from main index for external use - Maintain identical functionality while reducing maintenance overhead Benefits: - Single source of truth for email header sanitization - Consistent security handling across all email components - Easier to maintain and update sanitization logic - Configurable quote escaping for different use cases 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/index.ts | 2 ++ src/sendEmail.ts | 12 ++--------- src/services/MailingService.ts | 12 +++-------- src/utils/helpers.ts | 38 ++++++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/index.ts b/src/index.ts index 468c39b..1e66584 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,6 +26,8 @@ export { processEmails, retryFailedEmails, parseAndValidateEmails, + sanitizeDisplayName, + sanitizeFromName, } from './utils/helpers.js' // Email processing utilities diff --git a/src/sendEmail.ts b/src/sendEmail.ts index 881759d..e31e83d 100644 --- a/src/sendEmail.ts +++ b/src/sendEmail.ts @@ -1,5 +1,5 @@ import { Payload } from 'payload' -import { getMailing, renderTemplate, parseAndValidateEmails } from './utils/helpers.js' +import { getMailing, renderTemplate, parseAndValidateEmails, sanitizeFromName } from './utils/helpers.js' import { BaseEmailDocument } from './types/index.js' import { processJobById } from './utils/emailProcessor.js' @@ -104,15 +104,7 @@ export const sendEmail = async { + if (!displayName) return displayName + + let sanitized = displayName + .trim() + // Remove/replace newlines and carriage returns to prevent header injection + .replace(/[\r\n]/g, ' ') + // Remove control characters (except space and printable characters) + .replace(/[\x00-\x1F\x7F-\x9F]/g, '') + + // Escape quotes if needed (for email headers) + if (escapeQuotes) { + sanitized = sanitized.replace(/"/g, '\\"') + } + + return sanitized +} + +/** + * Sanitize and validate fromName for emails + * Wrapper around sanitizeDisplayName for consistent fromName handling + * @param fromName - The fromName to sanitize + * @returns Sanitized fromName or undefined if empty after sanitization + */ +export const sanitizeFromName = (fromName: string | null | undefined): string | undefined => { + if (!fromName) return undefined + + const sanitized = sanitizeDisplayName(fromName, false) + return sanitized.length > 0 ? sanitized : undefined +} + export const getMailing = (payload: Payload) => { const mailing = (payload as any).mailing if (!mailing) {