From d466d9ea576ae9169bc670a7f8b46210b5757a2e Mon Sep 17 00:00:00 2001 From: Hivert Quentin Date: Sun, 1 Nov 2026 11:42:07 +0100 Subject: [PATCH] fix(mail): protect tags with unsafe image --- UI/MailPartViewers/UIxMailPartHTMLViewer.m | 32 +++++++++++++++---- .../js/Mailer/Message.service.js | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/UI/MailPartViewers/UIxMailPartHTMLViewer.m b/UI/MailPartViewers/UIxMailPartHTMLViewer.m index 3989c51ce..0786ddf35 100644 --- a/UI/MailPartViewers/UIxMailPartHTMLViewer.m +++ b/UI/MailPartViewers/UIxMailPartHTMLViewer.m @@ -50,6 +50,9 @@ static NSArray *BannedTags = nil; /* Tags that can't have any contents (no end tag) */ static NSArray *VoidTags= nil; +/* Substrings that make an inline style attribute unsafe (may load remote content) */ +static NSArray *UnsafeStyleNeedles = nil; + static xmlCharEncoding _xmlCharsetForCharset (NSString *charset) { @@ -151,7 +154,7 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content) if (!BannedTags) BannedTags = [[NSArray alloc] initWithObjects: @"script", @"frameset", @"frame", @"iframe", @"applet", @"link", - @"base", @"meta", @"title", nil]; + @"base", @"meta", @"title", @"svg", nil]; if (!VoidTags) { /* see http://www.w3.org/TR/html4/index/elements.html */ @@ -160,6 +163,11 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content) @"img", @"input", @"isindex", @"link", @"meta", @"param", @"", nil]; } + + if (!UnsafeStyleNeedles) + UnsafeStyleNeedles = [[NSArray alloc] initWithObjects: @"url", @"image-set", + @"image(", @"src(", @"cross-fade", + @"@import", @"expression", @"\\", nil]; } - (id) init @@ -494,7 +502,10 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content) lowerName = [_localName lowercaseString]; if (inStyle || ignoredContent) - ; + { + if (ignoredContent && [lowerName isEqualToString: ignoreTag]) + ignoredContent++; + } else if ([lowerName isEqualToString: @"base"]) ; else if ([lowerName isEqualToString: @"meta"]) @@ -553,7 +564,7 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content) { skipAttribute = NO; name = [[_attributes nameAtIndex: count] lowercaseString]; - if ([name isEqualToString: @"src"]) + if ([name isEqualToString: @"src"] || [name isEqualToString: @"srcset"] ) { value = [_attributes valueAtIndex: count]; if ([value hasPrefix: @"cid:"]) @@ -585,12 +596,12 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content) } } if(!isBase64) - name = @"unsafe-src"; + name = [NSString stringWithFormat: @"unsafe-%@", name]; } else skipAttribute = YES; } - else if ([name isEqualToString: @"background"] || + else if ([name isEqualToString: @"background"] || [name isEqualToString: @"poster"] || (([name isEqualToString: @"data"] || [name isEqualToString: @"classid"]) && [lowerName isEqualToString: @"object"])) @@ -619,8 +630,15 @@ static NSString *_sanitizeHtmlForDisplay(NSString *content) value = [_attributes valueAtIndex: count]; lowerValue = [[value lowercaseString] stringByReplacingString: @"\"" withString: @""]; - if ([lowerValue rangeOfString: @"url"].location != NSNotFound) - name = [NSString stringWithFormat: @"unsafe-%@", name]; + int i, n; + for (i = 0, n = [UnsafeStyleNeedles count]; i < n; i++) + { + if ([lowerValue rangeOfString: [UnsafeStyleNeedles objectAtIndex: i]].location != NSNotFound) + { + name = [NSString stringWithFormat: @"unsafe-%@", name]; + break; + } + } } else if ([name isEqualToString: @"rel"]) { diff --git a/UI/WebServerResources/js/Mailer/Message.service.js b/UI/WebServerResources/js/Mailer/Message.service.js index 565c14736..eaf71e72c 100644 --- a/UI/WebServerResources/js/Mailer/Message.service.js +++ b/UI/WebServerResources/js/Mailer/Message.service.js @@ -429,7 +429,7 @@ if (angular.isUndefined(part.unsafeContent)) { part.unsafeContent = document.createElement('div'); part.unsafeContent.innerHTML = part.safeContent; - angular.forEach(['src', 'data', 'classid', 'background', 'style'], function (suffix) { + angular.forEach(['src', 'srcset', 'data', 'classid', 'background', 'style', 'poster'], function (suffix) { var elements = part.unsafeContent.querySelectorAll('[unsafe-' + suffix + ']'), element, value,