fix(mail): escape angularJS directive for description

This commit is contained in:
Hivert Quentin
2026-08-10 19:34:25 +02:00
parent 78807d01ae
commit 47133fdf3b
19 changed files with 38 additions and 21 deletions
+2 -1
View File
@@ -78,7 +78,8 @@
/* XSS protection */ /* XSS protection */
- (NSString *) removeHTMLTagsExceptAnchorTags; - (NSString *) removeHTMLTagsExceptAnchorTags;
- (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode; - (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode
stripAngular: (BOOL)stripAngular;
#ifndef GNUSTEP_BASE_LIBRARY #ifndef GNUSTEP_BASE_LIBRARY
- (BOOL) boolValue; - (BOOL) boolValue;
+17 -1
View File
@@ -936,7 +936,7 @@ static int cssEscapingCount;
* @param stripHTMLCode Remove all HTML code from content * @param stripHTMLCode Remove all HTML code from content
* @return A safe string * @return A safe string
*/ */
- (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode - (NSString *) stringWithoutHTMLInjection: (BOOL)stripHTMLCode stripAngular: (BOOL)stripAngular
{ {
NSString *result, *text, *newResult; NSString *result, *text, *newResult;
NSScanner *theScanner; NSScanner *theScanner;
@@ -1058,6 +1058,22 @@ static int cssEscapingCount;
// Remove @import css (in style tags) // Remove @import css (in style tags)
regex = [NSRegularExpression regularExpressionWithPattern:@"(<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e.*)([\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*@[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*m[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*o[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t)(.*<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*\\/[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*>)" regex = [NSRegularExpression regularExpressionWithPattern:@"(<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e.*)([\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*@[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*i[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*m[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*p[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*o[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*r[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t)(.*<[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*\\/[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*s[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*t[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*y[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*l[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*e[\\s\\u200B&#x09;&#x0A;&#x0D;\\\\0]*>)"
options: NSRegularExpressionCaseInsensitive error:&error]; options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"onrep***="];
result = [NSString stringWithString: newResult];
if(stripAngular) {
// Remove {{ and }} as they are interprated by angularJS with no way of escaping
regex = [NSRegularExpression regularExpressionWithPattern:@"(\\{\\{)|(&#x7b;&#x7b;)"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"{\\\\{"];
result = [NSString stringWithString: newResult];
regex = [NSRegularExpression regularExpressionWithPattern:@"(\\}\\})|(&#x7d;&#x7d;)"
options: NSRegularExpressionCaseInsensitive error:&error];
newResult = [regex stringByReplacingMatchesInString:result options:0 range:NSMakeRange(0, [result length]) withTemplate:@"}/}"];
result = [NSString stringWithString: newResult];
}
newResult = result; newResult = result;
while([regex numberOfMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length])] > 0) { while([regex numberOfMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length])] > 0) {
newResult = [regex stringByReplacingMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length]) withTemplate:@"$1@im****$3"]; newResult = [regex stringByReplacingMatchesInString:newResult options:0 range:NSMakeRange(0, [newResult length]) withTemplate:@"$1@im****$3"];
+1 -1
View File
@@ -93,7 +93,7 @@ static const NSString *kCacheMotdKey = @"admin-motd";
NSException *error; NSException *error;
NSString *safeMotd; NSString *safeMotd;
safeMotd = [motd stringWithoutHTMLInjection: NO]; safeMotd = [motd stringWithoutHTMLInjection: NO stripAngular: NO];
error = [[[GCSFolderManager defaultFolderManager] adminFolder] writeMotd: safeMotd]; error = [[[GCSFolderManager defaultFolderManager] adminFolder] writeMotd: safeMotd];
if (!error) { if (!error) {
[[SOGoCache sharedCache] setValue:safeMotd forKey:kCacheMotdKey]; [[SOGoCache sharedCache] setValue:safeMotd forKey:kCacheMotdKey];
+2 -2
View File
@@ -152,7 +152,7 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
rc = NO; rc = NO;
if ([fullName length]) if ([fullName length])
[identity setObject: [fullName stringWithoutHTMLInjection: YES] forKey: @"fullName"]; [identity setObject: [fullName stringWithoutHTMLInjection: YES stripAngular:NO] forKey: @"fullName"];
if ([email length]) if ([email length])
[identity setObject: email forKey: @"email"]; [identity setObject: email forKey: @"email"];
if ([replyTo length]) if ([replyTo length])
@@ -864,7 +864,7 @@ NSString *SOGoPasswordRecoverySecondaryEmail = @"SecondaryEmail";
if (mailIdentity && [mailIdentity objectForKey: @"fullName"]) { if (mailIdentity && [mailIdentity objectForKey: @"fullName"]) {
fullName = [NSString stringWithString: [mailIdentity objectForKey: @"fullName"]]; fullName = [NSString stringWithString: [mailIdentity objectForKey: @"fullName"]];
if (fullName) { if (fullName) {
[mailIdentity setObject: [fullName stringWithoutHTMLInjection: YES] forKey: @"fullName"]; [mailIdentity setObject: [fullName stringWithoutHTMLInjection: YES stripAngular:NO] forKey: @"fullName"];
[mailIdentities setObject: mailIdentity atIndexedSubscript: i]; [mailIdentities setObject: mailIdentity atIndexedSubscript: i];
} }
} }
+1 -1
View File
@@ -61,7 +61,7 @@
request = [context request]; request = [context request];
params = [[request contentAsString] objectFromJSONString]; params = [[request contentAsString] objectFromJSONString];
name = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES]; name = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES stripAngular:NO];
nameInContainer = nil; nameInContainer = nil;
if ([name length] > 0) if ([name length] > 0)
+1 -1
View File
@@ -485,7 +485,7 @@ static Class SOGoContactGCSEntryK = Nil;
co = [self clientObject]; co = [self clientObject];
card = [co vCard]; card = [co vCard];
request = [context request]; request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: YES] objectFromJSONString]; params = [[[request contentAsString] stringWithoutHTMLInjection: YES stripAngular:NO] objectFromJSONString];
forceSave = [[params objectForKey: @"ignoreDuplicate"] boolValue]; forceSave = [[params objectForKey: @"ignoreDuplicate"] boolValue];
[self setAttributes: params]; [self setAttributes: params];
+1 -1
View File
@@ -339,7 +339,7 @@
[list retain]; [list retain];
request = [context request]; request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: YES] objectFromJSONString]; params = [[[request contentAsString] stringWithoutHTMLInjection: YES stripAngular:NO] objectFromJSONString];
o = [params objectForKey: @"refs"]; o = [params objectForKey: @"refs"];
if (![o isKindOfClass: [NSArray class]]) if (![o isKindOfClass: [NSArray class]])
+1 -1
View File
@@ -587,7 +587,7 @@
//Sanitise the html content //Sanitise the html content
if([d objectForKey:@"content"]){ if([d objectForKey:@"content"]){
[d setObject: [[d objectForKey:@"content"] stringWithoutHTMLInjection: NO] forKey: @"content"]; [d setObject: [[d objectForKey:@"content"] stringWithoutHTMLInjection: NO stripAngular:YES] forKey: @"content"];
} }
return d; return d;
+1 -1
View File
@@ -201,7 +201,7 @@
content = [[[self generateResponse] contentAsString] stringWithoutHTMLInjection: NO]; content = [[[self generateResponse] contentAsString] stringWithoutHTMLInjection: NO stripAngular:YES];
if ([self respondsToSelector:@selector(getException)]) { if ([self respondsToSelector:@selector(getException)]) {
e = [self getException]; e = [self getException];
} }
+2 -2
View File
@@ -79,7 +79,7 @@
request = [context request]; request = [context request];
params = [[request contentAsString] objectFromJSONString]; params = [[request contentAsString] objectFromJSONString];
folderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES]; folderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES stripAngular:NO];
if ([folderName length] > 0) if ([folderName length] > 0)
{ {
encodedFolderName = [folderName stringByEncodingImap4FolderName]; encodedFolderName = [folderName stringByEncodingImap4FolderName];
@@ -145,7 +145,7 @@
// Retrieve new folder name from JSON payload // Retrieve new folder name from JSON payload
request = [context request]; request = [context request];
params = [[request contentAsString] objectFromJSONString]; params = [[request contentAsString] objectFromJSONString];
newFolderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES]; newFolderName = [[params objectForKey: @"name"] stringWithoutHTMLInjection: YES stripAngular:NO];
if (!newFolderName || [newFolderName length] == 0) if (!newFolderName || [newFolderName length] == 0)
{ {
+1 -1
View File
@@ -1178,7 +1178,7 @@
[msg addObject: [NSNumber numberWithBool: [self isMessageFlagged]]]; [msg addObject: [NSNumber numberWithBool: [self isMessageFlagged]]];
// Subject // Subject
[msg addObject: [[[self messageSubject] stringWithoutHTMLInjection: YES] stringWithoutHTMLInjection: NO]]; [msg addObject: [[[self messageSubject] stringWithoutHTMLInjection: YES stripAngular:NO] stringWithoutHTMLInjection: NO stripAngular:NO]];
// From // From
from = [[message objectForKey: @"envelope"] from]; from = [[message objectForKey: @"envelope"] from];
+1 -1
View File
@@ -363,7 +363,7 @@ static NSString *mailETag = nil;
if ([self formattedDate]) if ([self formattedDate])
[data setObject: [self formattedDate] forKey: @"date"]; [data setObject: [self formattedDate] forKey: @"date"];
if ([self messageSubject]) if ([self messageSubject])
[data setObject: [[[self messageSubject] stringWithoutHTMLInjection: YES] stringWithoutHTMLInjection: NO] forKey: @"subject"]; [data setObject: [[[self messageSubject] stringWithoutHTMLInjection: YES stripAngular:YES] stringWithoutHTMLInjection: NO stripAngular:YES] forKey: @"subject"];
if ((addresses = [addressFormatter dictionariesForArray: [co fromEnvelopeAddresses]])) if ((addresses = [addressFormatter dictionariesForArray: [co fromEnvelopeAddresses]]))
[data setObject: addresses forKey: @"from"]; [data setObject: addresses forKey: @"from"];
if ((addresses = [addressFormatter dictionariesForArray: [co toEnvelopeAddresses]])) if ((addresses = [addressFormatter dictionariesForArray: [co toEnvelopeAddresses]]))
+1 -1
View File
@@ -1091,7 +1091,7 @@ static const NSString *kJwtKey = @"jwt";
} }
//Check common injection //Check common injection
loginClean = [login stringWithoutHTMLInjection: YES]; loginClean = [login stringWithoutHTMLInjection: YES stripAngular:NO];
if(![loginClean isEqualToString: login]) if(![loginClean isEqualToString: login])
{ {
loginClean = @""; loginClean = @"";
+1 -1
View File
@@ -1750,7 +1750,7 @@ static NSArray *reminderValues = nil;
id o, v; id o, v;
requestStr = [[context request] contentAsString]; requestStr = [[context request] contentAsString];
requestStr = [requestStr stringWithoutHTMLInjection: NO]; requestStr = [requestStr stringWithoutHTMLInjection: NO stripAngular:NO];
o = [requestStr objectFromJSONString]; o = [requestStr objectFromJSONString];
results = nil; results = nil;
+1 -1
View File
@@ -403,7 +403,7 @@ static SoProduct *commonProduct = nil;
theme = [[context request] formValueForKey: @"theme"]; theme = [[context request] formValueForKey: @"theme"];
if ([theme length]) if ([theme length])
{ {
safeTheme = [theme stringWithoutHTMLInjection: YES]; safeTheme = [theme stringWithoutHTMLInjection: YES stripAngular:NO];
if([safeTheme isEqualToString: theme]) if([safeTheme isEqualToString: theme])
rel = [NSString stringWithFormat: @"%@?theme=%@", rel, theme]; rel = [NSString stringWithFormat: @"%@?theme=%@", rel, theme];
} }
+1 -1
View File
@@ -556,7 +556,7 @@
ex = nil; ex = nil;
request = [context request]; request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: NO] objectFromJSONString]; params = [[[request contentAsString] stringWithoutHTMLInjection: NO stripAngular:NO] objectFromJSONString];
if (params == nil) if (params == nil)
{ {
ex = [NSException exceptionWithName: @"JSONParsingException" ex = [NSException exceptionWithName: @"JSONParsingException"
+1 -1
View File
@@ -182,7 +182,7 @@ _intValueFromHex (NSString *hexString)
fActiveTasks = [folder activeTasks]; fActiveTasks = [folder activeTasks];
[calendar setObject: folderName forKey: @"id"]; [calendar setObject: folderName forKey: @"id"];
[calendar setObject: [fDisplayName stringWithoutHTMLInjection: YES] forKey: @"name"]; [calendar setObject: [fDisplayName stringWithoutHTMLInjection: YES stripAngular:NO] forKey: @"name"];
[calendar setObject: [folder calendarColor] forKey: @"color"]; [calendar setObject: [folder calendarColor] forKey: @"color"];
isActive = [NSNumber numberWithBool: [folder isActive]]; isActive = [NSNumber numberWithBool: [folder isActive]];
[calendar setObject: isActive forKey: @"active"]; [calendar setObject: isActive forKey: @"active"];
+1 -1
View File
@@ -335,7 +335,7 @@
ex = nil; ex = nil;
request = [context request]; request = [context request];
params = [[[request contentAsString] stringWithoutHTMLInjection: NO] objectFromJSONString]; params = [[[request contentAsString] stringWithoutHTMLInjection: NO stripAngular:NO] objectFromJSONString];
if (params == nil) if (params == nil)
{ {
ex = [NSException exceptionWithName: @"JSONParsingException" ex = [NSException exceptionWithName: @"JSONParsingException"
@@ -299,7 +299,7 @@
<label class="pseudo-input-label"> <label class="pseudo-input-label">
<var:string label:value="Description"/> <var:string label:value="Description"/>
</label> </label>
<div> <div ng-non-bindable="">
<md-content> <md-content>
<var:string value="authorativeEvent.comment.stringByDetectingURLs" const:insertBR="1" const:escapeHTML="NO"/> <var:string value="authorativeEvent.comment.stringByDetectingURLs" const:insertBR="1" const:escapeHTML="NO"/>
</md-content> </md-content>