diff --git a/lib/__tests__/email-sanitization.test.ts b/lib/__tests__/email-sanitization.test.ts index 1f9c2cb2..b2426a68 100644 --- a/lib/__tests__/email-sanitization.test.ts +++ b/lib/__tests__/email-sanitization.test.ts @@ -168,19 +168,32 @@ describe('email-sanitization', () => { expect(sanitizeSignatureHtml(' ')).toBe(''); }); - it('should be stricter than email sanitization', () => { - const html = '

Text

Data
'; + it('should be stricter than email sanitization for script-bearing tags', () => { + const html = '

Text

Data
'; const emailClean = sanitizeEmailHtml(html); const signatureClean = sanitizeSignatureHtml(html); - // Email allows table + // Both preserve tables (signatures are universally table-based) expect(emailClean).toContain(''); + expect(signatureClean).toContain(' { + const signature = '
Name
'; + const clean = sanitizeSignatureHtml(signature); + expect(clean).toContain('cellpadding'); + expect(clean).toContain('cellspacing'); + expect(clean).toContain('valign'); + expect(clean).toContain('align'); + expect(clean).toContain('bgcolor'); + expect(clean).toContain('colspan'); + }); }); describe('parseHtmlSafely', () => { diff --git a/lib/email-sanitization.ts b/lib/email-sanitization.ts index bee197f2..20145f55 100644 --- a/lib/email-sanitization.ts +++ b/lib/email-sanitization.ts @@ -58,11 +58,19 @@ export function sanitizeEmailHtmlForIframe(html: string): string { /** * Sanitize HTML signature with stricter rules - * Allows basic formatting plus for company logos + * Allows basic formatting plus for company logos, plus table-based + * layouts (the de-facto standard for email signatures). */ export const SIGNATURE_SANITIZE_CONFIG = { - ALLOWED_TAGS: ['p', 'br', 'b', 'strong', 'i', 'em', 'u', 'a', 'span', 'div', 'img'], - ALLOWED_ATTR: ['href', 'style', 'class', 'src', 'alt', 'width', 'height', 'title'], + ALLOWED_TAGS: [ + 'p', 'br', 'b', 'strong', 'i', 'em', 'u', 'a', 'span', 'div', 'img', + 'table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', + ], + ALLOWED_ATTR: [ + 'href', 'style', 'class', 'src', 'alt', 'width', 'height', 'title', + 'cellpadding', 'cellspacing', 'border', 'valign', 'align', 'bgcolor', + 'colspan', 'rowspan', + ], ALLOW_DATA_ATTR: false, FORBID_TAGS: ['script', 'iframe', 'object', 'embed', 'video', 'audio'], FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover'],