Allow clean background:url(...) styles in safe mode. This will make Roundcube pass the Email Standards Acid Test

pull/1/head
thomascube 13 years ago
parent 231fae7ad4
commit 2b017e7f79

@ -883,19 +883,19 @@ function rcmail_get_edit_field($col, $value, $attrib, $type='text')
* @param string Container ID to use as prefix * @param string Container ID to use as prefix
* @return string Modified CSS source * @return string Modified CSS source
*/ */
function rcmail_mod_css_styles($source, $container_id) function rcmail_mod_css_styles($source, $container_id, $allow_remote=false)
{ {
$last_pos = 0; $last_pos = 0;
$replacements = new rcube_string_replacer; $replacements = new rcube_string_replacer;
// ignore the whole block if evil styles are detected // ignore the whole block if evil styles are detected
$stripped = preg_replace('/[^a-z\(:;]/', '', rcmail_xss_entity_decode($source)); $source = rcmail_xss_entity_decode($source);
if (preg_match('/expression|behavior|url\(|import[^a]/', $stripped)) $stripped = preg_replace('/[^a-z\(:;]/i', '', $source);
$evilexpr = 'expression|behavior' . (!$allow_remote ? '|url\(|import[^a]' : '');
if (preg_match("/$evilexpr/i", $stripped) // don't accept No-Gos
|| (strpos($stripped, 'url(') && !preg_match('!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Ui', $source))) // only allow clean urls
return '/* evil! */'; return '/* evil! */';
// remove css comments (sometimes used for some ugly hacks)
$source = preg_replace('!/\*(.+)\*/!Ums', '', $source);
// cut out all contents between { and } // cut out all contents between { and }
while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos)))
{ {
@ -937,7 +937,7 @@ function rcmail_xss_entity_decode($content)
{ {
$out = html_entity_decode(html_entity_decode($content)); $out = html_entity_decode(html_entity_decode($content));
$out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entity_decode_callback', $out); $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entity_decode_callback', $out);
$out = preg_replace('#/\*.*\*/#Um', '', $out); $out = preg_replace('#/\*.*\*/#Ums', '', $out);
return $out; return $out;
} }

@ -243,7 +243,7 @@ class washtml
case XML_ELEMENT_NODE: //Check element case XML_ELEMENT_NODE: //Check element
$tagName = strtolower($node->tagName); $tagName = strtolower($node->tagName);
if ($callback = $this->handlers[$tagName]) { if ($callback = $this->handlers[$tagName]) {
$dump .= call_user_func($callback, $tagName, $this->wash_attribs($node), $this->dumpHtml($node)); $dump .= call_user_func($callback, $tagName, $this->wash_attribs($node), $this->dumpHtml($node), $this);
} }
else if (isset($this->_html_elements[$tagName])) { else if (isset($this->_html_elements[$tagName])) {
$content = $this->dumpHtml($node); $content = $this->dumpHtml($node);
@ -301,6 +301,14 @@ class washtml
return $this->dumpHtml($node); return $this->dumpHtml($node);
} }
/**
* Getter for config parameters
*/
public function get_config($prop)
{
return $this->config[$prop];
}
} }
?> ?>

@ -822,7 +822,7 @@ function rcmail_plain_body($body, $flowed=false)
/** /**
* Callback function for washtml cleaning class * Callback function for washtml cleaning class
*/ */
function rcmail_washtml_callback($tagname, $attrib, $content) function rcmail_washtml_callback($tagname, $attrib, $content, $washtml)
{ {
switch ($tagname) { switch ($tagname) {
case 'form': case 'form':
@ -834,8 +834,11 @@ function rcmail_washtml_callback($tagname, $attrib, $content)
$stripped = preg_replace('/[^a-zA-Z\(:;]/', '', rcmail_xss_entity_decode($content)); $stripped = preg_replace('/[^a-zA-Z\(:;]/', '', rcmail_xss_entity_decode($content));
// now check for evil strings like expression, behavior or url() // now check for evil strings like expression, behavior or url()
if (!preg_match('/expression|behavior|url\(|import[^a]/', $stripped)) { if (!preg_match('/expression|behavior/i', $stripped)) {
$out = html::tag('style', array('type' => 'text/css'), $content); if (!$washtml->get_config('allow_remote') && preg_match('/url\(|import[^a]/i', $stripped))
$washtml->extlinks = true;
else
$out = html::tag('style', array('type' => 'text/css'), $content);
break; break;
} }
@ -1021,7 +1024,7 @@ function rcmail_message_body($attrib)
$body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html'])); $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
if ($part->ctype_secondary == 'html') { if ($part->ctype_secondary == 'html') {
$body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs); $body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs, $safe_mode);
$div_attr = array('class' => 'message-htmlpart'); $div_attr = array('class' => 'message-htmlpart');
$style = array(); $style = array();
@ -1104,7 +1107,7 @@ function rcmail_resolve_base($body)
/** /**
* modify a HTML message that it can be displayed inside a HTML page * modify a HTML message that it can be displayed inside a HTML page
*/ */
function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null) function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null, $allow_remote=false)
{ {
$last_style_pos = 0; $last_style_pos = 0;
$cont_id = $container_id.($body_id ? ' div.'.$body_id : ''); $cont_id = $container_id.($body_id ? ' div.'.$body_id : '');
@ -1116,7 +1119,7 @@ function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null
// replace all css definitions with #container [def] // replace all css definitions with #container [def]
$styles = rcmail_mod_css_styles( $styles = rcmail_mod_css_styles(
substr($body, $pos, $pos2-$pos), $cont_id); substr($body, $pos, $pos2-$pos), $cont_id, $allow_remote);
$body = substr_replace($body, $styles, $pos, $pos2-$pos); $body = substr_replace($body, $styles, $pos, $pos2-$pos);
$last_style_pos = $pos2; $last_style_pos = $pos2;

Loading…
Cancel
Save