Improve CSS sanitizer for HTML messages
At-rules are now completely removed. Fixed a possible issue where the first rule would not be prefixed by our CSS selector. Fixes #3700pull/210/head
parent
875a4aca32
commit
45290c6414
1
NEWS
1
NEWS
|
@ -16,6 +16,7 @@ Bug fixes
|
||||||
- [web] fixed display of mailboxes list on mobiles (#3654)
|
- [web] fixed display of mailboxes list on mobiles (#3654)
|
||||||
- [web] restored Catalan and Slovak translations (#3687)
|
- [web] restored Catalan and Slovak translations (#3687)
|
||||||
- [web] fixed restore of mailboxes expansion state when multiple IMAP accounts are configured
|
- [web] fixed restore of mailboxes expansion state when multiple IMAP accounts are configured
|
||||||
|
- [web] improved CSS sanitizer for HTML messages (#3700)
|
||||||
- [core] strip X- tags when securing content (#3695)
|
- [core] strip X- tags when securing content (#3695)
|
||||||
|
|
||||||
3.1.0 (2016-05-18)
|
3.1.0 (2016-05-18)
|
||||||
|
|
|
@ -412,9 +412,35 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
showWhoWeAre();
|
showWhoWeAre();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* About CSS At-Rules (https://css-tricks.com/the-at-rules-of-css/)
|
||||||
|
*
|
||||||
|
* At-Rules follow two possible synthaxes:
|
||||||
|
*
|
||||||
|
* @[KEYWORD] (RULE);
|
||||||
|
* Examples:
|
||||||
|
* @charset "UTF-8";
|
||||||
|
* @import 'global.css';
|
||||||
|
* @namespace svg url(http://www.w3.org/2000/svg);
|
||||||
|
*
|
||||||
|
* @[KEYWORD] { (Nested Statements) }
|
||||||
|
* Examples:
|
||||||
|
* @font-face {
|
||||||
|
* font-family: 'MyWebFont';
|
||||||
|
* src: url('myfont.woff2') format('woff2'),
|
||||||
|
* url('myfont.woff') format('woff');
|
||||||
|
* }
|
||||||
|
* @media only screen
|
||||||
|
* and (min-device-width: 320px)
|
||||||
|
* and (max-device-width: 480px)
|
||||||
|
* and (-webkit-min-device-pixel-ratio: 2) {
|
||||||
|
* .module { width: 100%; }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
- (void) _appendStyle: (unichar *) _chars
|
- (void) _appendStyle: (unichar *) _chars
|
||||||
length: (NSUInteger) _len
|
length: (NSUInteger) _len
|
||||||
{
|
{
|
||||||
|
NSMutableString *declaration;
|
||||||
NSUInteger count, length;
|
NSUInteger count, length;
|
||||||
unichar *start, *currentChar;
|
unichar *start, *currentChar;
|
||||||
|
|
||||||
|
@ -428,16 +454,59 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
currentChar = _chars + count;
|
currentChar = _chars + count;
|
||||||
if (inCSSDeclaration)
|
if (inCSSDeclaration)
|
||||||
{
|
{
|
||||||
if (*currentChar == '}')
|
if (*currentChar < 32)
|
||||||
|
{
|
||||||
|
// Append substring since last valid character and reset start counter
|
||||||
|
if (currentChar > start)
|
||||||
|
[declaration appendString: [NSString stringWithCharacters: start
|
||||||
|
length: (currentChar - start)]];
|
||||||
|
start = currentChar + 1;
|
||||||
|
}
|
||||||
|
else if (*currentChar == '}')
|
||||||
{
|
{
|
||||||
inCSSDeclaration = NO;
|
inCSSDeclaration = NO;
|
||||||
|
if (hasEmbeddedCSS)
|
||||||
|
{
|
||||||
|
// End of at-rule definition; remove it from the stylesheet
|
||||||
hasEmbeddedCSS = NO;
|
hasEmbeddedCSS = NO;
|
||||||
|
start = currentChar + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Prefix CSS rule
|
||||||
|
length = (currentChar - start);
|
||||||
|
[declaration appendString: [NSString stringWithCharacters: start length: length]];
|
||||||
|
[css appendFormat: @".SOGoHTMLMail-CSS-Delimiter %@\n", declaration];
|
||||||
|
start = currentChar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*currentChar == ';')
|
||||||
|
{
|
||||||
|
// Add !important
|
||||||
|
if ((currentChar < _chars - 10) ||
|
||||||
|
!((*(currentChar-1) == 't' || *(currentChar-1) == 'T') &&
|
||||||
|
(*(currentChar-2) == 'n' || *(currentChar-2) == 'N') &&
|
||||||
|
(*(currentChar-3) == 'a' || *(currentChar-3) == 'A') &&
|
||||||
|
(*(currentChar-4) == 't' || *(currentChar-4) == 'T') &&
|
||||||
|
(*(currentChar-5) == 'r' || *(currentChar-5) == 'R') &&
|
||||||
|
(*(currentChar-6) == 'o' || *(currentChar-6) == 'O') &&
|
||||||
|
(*(currentChar-7) == 'p' || *(currentChar-7) == 'P') &&
|
||||||
|
(*(currentChar-8) == 'm' || *(currentChar-8) == 'M') &&
|
||||||
|
(*(currentChar-9) == 'i' || *(currentChar-9) == 'I') &&
|
||||||
|
*(currentChar-10) == '!'))
|
||||||
|
{
|
||||||
|
length = (currentChar - start);
|
||||||
|
[declaration appendFormat: @"%@ !important;",
|
||||||
|
[NSString stringWithCharacters: start length: length]];
|
||||||
|
start = currentChar + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (*currentChar < 32)
|
if (*currentChar < 32)
|
||||||
{
|
{
|
||||||
|
// Append substring since last valid character and reset start counter
|
||||||
if (currentChar > start)
|
if (currentChar > start)
|
||||||
[css appendString: [NSString stringWithCharacters: start
|
[css appendString: [NSString stringWithCharacters: start
|
||||||
length: (currentChar - start)]];
|
length: (currentChar - start)]];
|
||||||
|
@ -446,27 +515,37 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (*currentChar == '{')
|
if (*currentChar == '{')
|
||||||
|
{
|
||||||
|
// Start of rule declaration
|
||||||
inCSSDeclaration = YES;
|
inCSSDeclaration = YES;
|
||||||
|
declaration = [NSMutableString string];
|
||||||
|
}
|
||||||
if (*currentChar == '}')
|
if (*currentChar == '}')
|
||||||
// CSS syntax error: ending declaration character while not in a CSS declaration.
|
// CSS syntax error: ending declaration character while not in a CSS declaration.
|
||||||
// Ignore eveything from last CSS declaration.
|
// Ignore eveything from last CSS declaration.
|
||||||
start = currentChar + 1;
|
start = currentChar + 1;
|
||||||
else if (*currentChar == ',')
|
else if (hasEmbeddedCSS)
|
||||||
|
{
|
||||||
|
if (*currentChar == ';')
|
||||||
|
{
|
||||||
|
// End of at-rule definition; remove it from the stylesheet
|
||||||
hasEmbeddedCSS = NO;
|
hasEmbeddedCSS = NO;
|
||||||
else if (!hasEmbeddedCSS)
|
start = currentChar + 1;
|
||||||
{
|
|
||||||
if (*currentChar == '@')
|
|
||||||
hasEmbeddedCSS = YES;
|
|
||||||
else
|
|
||||||
if (*currentChar > 32)
|
|
||||||
{
|
|
||||||
length = (currentChar - start);
|
|
||||||
[css appendFormat: @"%@\n.SOGoHTMLMail-CSS-Delimiter ",
|
|
||||||
[NSString stringWithCharacters: start length: length]];
|
|
||||||
hasEmbeddedCSS = YES;
|
|
||||||
start = currentChar;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (*currentChar == ',')
|
||||||
|
{
|
||||||
|
// Prefix CSS selector
|
||||||
|
length = (currentChar - start);
|
||||||
|
[css appendFormat: @" .SOGoHTMLMail-CSS-Delimiter %@,",
|
||||||
|
[NSString stringWithCharacters: start length: length]];
|
||||||
|
start = currentChar + 1;
|
||||||
|
}
|
||||||
|
else if (*currentChar == '@')
|
||||||
|
{
|
||||||
|
// Start of at-rule definition
|
||||||
|
hasEmbeddedCSS = YES;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -619,21 +698,10 @@ static NSData* _sanitizeContent(NSData *theData)
|
||||||
|
|
||||||
- (void) _finishCSS
|
- (void) _finishCSS
|
||||||
{
|
{
|
||||||
NSRange excessiveDelimiter;
|
|
||||||
|
|
||||||
[css replaceString: @"<!--" withString: @""];
|
[css replaceString: @"<!--" withString: @""];
|
||||||
[css replaceString: @"-->" withString: @""];
|
[css replaceString: @"-->" withString: @""];
|
||||||
[css replaceString: @".SOGoHTMLMail-CSS-Delimiter body"
|
[css replaceString: @".SOGoHTMLMail-CSS-Delimiter body"
|
||||||
withString: @".SOGoHTMLMail-CSS-Delimiter"];
|
withString: @".SOGoHTMLMail-CSS-Delimiter"];
|
||||||
[css replaceString: @";" withString: @" !important;"];
|
|
||||||
|
|
||||||
excessiveDelimiter = [css rangeOfString: @".SOGoHTMLMail-CSS-Delimiter "
|
|
||||||
options: NSBackwardsSearch];
|
|
||||||
if (excessiveDelimiter.location != NSNotFound)
|
|
||||||
{
|
|
||||||
if (NSMaxRange (excessiveDelimiter) == [css length])
|
|
||||||
[css deleteCharactersInRange: excessiveDelimiter];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) endElement: (NSString *) _localName
|
- (void) endElement: (NSString *) _localName
|
||||||
|
|
Loading…
Reference in New Issue