diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php index 2473894be1..9c01975403 100644 --- a/program/actions/mail/compose.php +++ b/program/actions/mail/compose.php @@ -989,22 +989,17 @@ public static function prepare_html_body($body, $wash_params = []) { static $part_no; - // Set attributes of the part container - $container_id = self::$COMPOSE['mode'] . 'body' . (++$part_no); - $wash_params += [ - 'safe' => self::$MESSAGE->is_safe, - 'css_prefix' => 'v' . $part_no, + 'safe' => self::$MESSAGE && self::$MESSAGE->is_safe, + 'css_prefix' => 'v' . (++$part_no), 'add_comments' => false, ]; - if (self::$COMPOSE['mode'] == rcmail_sendmail::MODE_DRAFT) { + if (self::$COMPOSE && self::$COMPOSE['mode'] == rcmail_sendmail::MODE_DRAFT) { // convert TinyMCE's empty-line sequence (#1490463) $body = preg_replace('/

\xC2\xA0<\/p>/', '


', $body); // remove tags (not their content) $wash_params['ignore_elements'] = ['body']; - } else { - $wash_params['container_id'] = $container_id; } // Make the HTML content safe and clean diff --git a/program/actions/mail/index.php b/program/actions/mail/index.php index 802dd05e67..9dc53c75f9 100644 --- a/program/actions/mail/index.php +++ b/program/actions/mail/index.php @@ -949,7 +949,7 @@ public static function wash_html($html, $p, $cid_replaces = []) 'css_prefix' => $p['css_prefix'], // internal configuration 'container_id' => $p['container_id'], - 'body_class' => $p['body_class'] ?? '', + 'body_class' => $p['body_class'] ?? 'rcmBody', ]; if (empty($p['inline_html'])) { @@ -974,14 +974,7 @@ public static function wash_html($html, $p, $cid_replaces = []) if (!empty($p['inline_html'])) { $washer->add_callback('body', 'rcmail_action_mail_index::washtml_callback'); - - if ($wash_opts['body_class']) { - self::$wash_html_body_attrs['class'] = $wash_opts['body_class']; - } - - if ($wash_opts['container_id']) { - self::$wash_html_body_attrs['id'] = $wash_opts['container_id']; - } + self::$wash_html_body_attrs['class'] = $wash_opts['body_class']; } if (empty($p['skip_washer_form_callback'])) { @@ -1149,11 +1142,10 @@ public static function washtml_callback($tagname, $attrib, $content, $washtml) if (strlen($out)) { $css_prefix = $washtml->get_config('css_prefix'); $is_safe = $washtml->get_config('allow_remote'); - $body_class = $washtml->get_config('body_class') ?: ''; + $body_class = $washtml->get_config('body_class'); $cont_id = $washtml->get_config('container_id') ?: ''; - $cont_id = trim($cont_id . ($body_class ? " div.{$body_class}" : '')); - $out = rcube_utils::mod_css_styles($out, $cont_id, $is_safe, $css_prefix); + $out = rcube_utils::mod_css_styles($out, $cont_id, $is_safe, $css_prefix, $body_class); $out = html::tag('style', ['type' => 'text/css'], $out); } @@ -1183,11 +1175,20 @@ public static function washtml_callback($tagname, $attrib, $content, $washtml) $style['background-image'] = "url({$value})"; } break; + case 'class': + if (!empty($value)) { + $attrs['class'] = trim(($attrs['class'] ?? '') . ' ' . $value); + } + break; default: $attrs[$attr_name] = $value; } } + if (isset($attrs['class']) && empty($attrs['class'])) { + unset($attrs['class']); + } + if (!empty($style)) { foreach ($style as $idx => $val) { $style[$idx] = $idx . ': ' . $val; @@ -1282,14 +1283,13 @@ public static function washtml_link_callback($tag, $attribs, $content, $washtml) if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) { $tempurl = 'tmp-' . md5($attrib['href']) . '.css'; - $_SESSION['modcssurls'][$tempurl] = $attrib['href']; - $attrib['href'] = $rcmail->url([ - 'task' => 'utils', - 'action' => 'modcss', - 'u' => $tempurl, - 'c' => $washtml->get_config('container_id'), - 'p' => $washtml->get_config('css_prefix'), - ]); + $_SESSION['modcssurls'][$tempurl] = [ + 'url' => $attrib['href'], + 'container_id' => $washtml->get_config('container_id'), + 'css_prefix' => $washtml->get_config('css_prefix'), + 'body_class' => $washtml->get_config('body_class'), + ]; + $attrib['href'] = $rcmail->url(['task' => 'utils', 'action' => 'modcss', 'u' => $tempurl]); $content = null; } elseif (preg_match('/^mailto:(.+)/i', $attrib['href'], $mailto)) { $url_parts = explode('?', html_entity_decode($mailto[1], \ENT_QUOTES, 'UTF-8'), 2); diff --git a/program/actions/mail/show.php b/program/actions/mail/show.php index 586453974f..1edb05c853 100644 --- a/program/actions/mail/show.php +++ b/program/actions/mail/show.php @@ -635,7 +635,6 @@ public static function message_body($attrib) $rcmail = rcmail::get_instance(); $safe_mode = self::$MESSAGE->is_safe || !empty($_GET['_safe']); $out = ''; - $part_no = 0; $header_attrib = []; foreach ($attrib as $attr => $value) { @@ -692,32 +691,8 @@ public static function message_body($attrib) self::message_error(); } - $plugin = $rcmail->plugins->exec_hook('message_body_prefix', - ['part' => $part, 'prefix' => '', 'message' => self::$MESSAGE]); - - // Set attributes of the part container - $container_class = $part->ctype_secondary == 'html' ? 'message-htmlpart' : 'message-part'; - $container_id = $container_class . (++$part_no); - $container_attrib = ['class' => $container_class, 'id' => $container_id]; - - $body_args = [ - 'safe' => $safe_mode, - 'plain' => !$rcmail->config->get('prefer_html'), - 'css_prefix' => 'v' . $part_no, - 'body_class' => 'rcmBody', - 'container_id' => $container_id, - 'container_attrib' => $container_attrib, - ]; - - // Parse the part content for display - $body = self::print_body($body, $part, $body_args); - - // check if the message body is PGP encrypted - if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) { - $rcmail->output->set_env('is_pgp_content', '#' . $container_id); - } - - $out .= html::div($body_args['container_attrib'], $plugin['prefix'] . $body); + // Process the part content for display + $out .= self::prepare_part_body($body, $part); } } } else { @@ -888,4 +863,40 @@ public static function mdn_request_handler($message) } } } + + /** + * Prepare message part content for display + */ + protected static function prepare_part_body($body, $part) + { + static $part_no; + + $rcmail = rcmail::get_instance(); + + $plugin = $rcmail->plugins->exec_hook('message_body_prefix', + ['part' => $part, 'prefix' => '', 'message' => self::$MESSAGE]); + + // Set attributes of the part container + $container_class = $part->ctype_secondary == 'html' ? 'message-htmlpart' : 'message-part'; + $container_id = $container_class . (++$part_no); + $container_attrib = ['class' => $container_class, 'id' => $container_id]; + + $body_args = [ + 'safe' => (self::$MESSAGE && self::$MESSAGE->is_safe) || !empty($_GET['_safe']), + 'plain' => !$rcmail->config->get('prefer_html'), + 'css_prefix' => 'v' . $part_no, + 'container_id' => $container_id, + 'container_attrib' => $container_attrib, + ]; + + // Parse the part content for display + $body = self::print_body($body, $part, $body_args); + + // check if the message body is PGP encrypted + if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) { + $rcmail->output->set_env('is_pgp_content', '#' . $body_args['container_id']); + } + + return html::div($body_args['container_attrib'], $plugin['prefix'] . $body); + } } diff --git a/program/actions/utils/modcss.php b/program/actions/utils/modcss.php index aebeb90a9d..227c163300 100644 --- a/program/actions/utils/modcss.php +++ b/program/actions/utils/modcss.php @@ -37,7 +37,8 @@ public function run($args = []) $rcmail->output->sendExitError(403, 'Unauthorized request'); } - $realurl = $_SESSION['modcssurls'][$url]; + $data = $_SESSION['modcssurls'][$url]; + $realurl = $data['url'] ?? ''; // don't allow any other connections than http(s) if (!preg_match('~^https?://~i', $realurl, $matches)) { @@ -59,16 +60,13 @@ public function run($args = []) rcube::raise_error($e, true, false); } - $cid = rcube_utils::get_input_string('_c', rcube_utils::INPUT_GET); - $prefix = rcube_utils::get_input_string('_p', rcube_utils::INPUT_GET); + if ($source !== false && $ctype && preg_match('~^text/(css|plain)~i', $ctype)) { + $container_id = $data['container_id'] ?? ''; + $css_prefix = $data['css_prefix'] ?? ''; + $body_class = $data['body_class'] ?? ''; - $container_id = preg_replace('/[^a-z0-9]/i', '', $cid); - $css_prefix = preg_replace('/[^a-z0-9]/i', '', $prefix); - $ctype_regexp = '~^text/(css|plain)~i'; - - if ($source !== false && $ctype && preg_match($ctype_regexp, $ctype)) { $rcmail->output->sendExit( - rcube_utils::mod_css_styles($source, $container_id, false, $css_prefix), + rcube_utils::mod_css_styles($source, $container_id, false, $css_prefix, $body_class), ['Content-Type: text/css'] ); } diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index db02400049..d85368c614 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -422,10 +422,11 @@ public static function html_identifier($str, $encode = false) * @param string $container_id Container ID to use as prefix * @param bool $allow_remote Allow remote content * @param string $prefix Prefix to be added to id/class identifier + * @param string $body_class Optional class name for the used-to-be-body element * * @return string Modified CSS source */ - public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '') + public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '', $body_class = '') { $source = self::xss_entity_decode($source); @@ -485,7 +486,7 @@ public static function mod_css_styles($source, $container_id, $allow_remote = fa // for cases like @media { body { position: fixed; } } (#5811) $excl = '(?!' . substr($replacements->pattern, 1, -1) . ')'; $regexp = '/(^\s*|,\s*|\}\s*|\{\s*)(' . $excl . ':?[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im'; - $callback = static function ($matches) use ($container_id, $prefix) { + $callback = static function ($matches) use ($container_id, $prefix, $body_class) { $replace = $matches[2]; if (stripos($replace, ':root') === 0) { @@ -497,6 +498,9 @@ public static function mod_css_styles($source, $container_id, $allow_remote = fa } if ($container_id) { + // replace body definition because we stripped off the tag + $replace = preg_replace('/^\s*body/i', $body_class ? ".{$body_class}" : '', $replace); + $replace = "#{$container_id} " . $replace; } @@ -509,12 +513,6 @@ public static function mod_css_styles($source, $container_id, $allow_remote = fa $source = preg_replace_callback($regexp, $callback, $source); } - // replace body definition because we also stripped off the tag - if ($container_id) { - $regexp = '/#' . preg_quote($container_id, '/') . '\s+body/i'; - $source = preg_replace($regexp, "#{$container_id}", $source); - } - // put block contents back in $source = $replacements->resolve($source); diff --git a/skins/elastic/styles/styles.less b/skins/elastic/styles/styles.less index 560c574a27..d9bff7b577 100644 --- a/skins/elastic/styles/styles.less +++ b/skins/elastic/styles/styles.less @@ -334,7 +334,7 @@ body.task-error-login #layout { margin-bottom: .5rem; } - div.rcmBody { + & > .rcmBody { // Remove margins that can be set by the mail message styles margin: 0 auto !important; // Fix floating table issue (#9804) diff --git a/tests/ActionTestCase.php b/tests/ActionTestCase.php index ee40796b7f..9176c3e91c 100644 --- a/tests/ActionTestCase.php +++ b/tests/ActionTestCase.php @@ -2,6 +2,7 @@ namespace Roundcube\Tests; +use Masterminds\HTML5; use PHPUnit\Framework\TestCase; /** @@ -268,6 +269,15 @@ protected static function loadSQLScript($db, $name) } } + /** + * Parse HTML output into DOMXPath object + */ + protected static function parseHtml($html) + { + $html5 = new HTML5(['disable_html_ns' => true]); + return new \DOMXPath($html5->loadHTML($html)); + } + /** * Call the action's run() method and handle exit exception */ diff --git a/tests/Actions/Mail/ComposeTest.php b/tests/Actions/Mail/ComposeTest.php index 691e2b3ce2..cc9bec0efa 100644 --- a/tests/Actions/Mail/ComposeTest.php +++ b/tests/Actions/Mail/ComposeTest.php @@ -19,6 +19,44 @@ public function test_class() $this->assertInstanceOf(\rcmail_action::class, $object); } + /** + * Test prepare_html_body() method + */ + public function test_prepare_html_body() + { + $action = new \rcmail_action_mail_compose(); + + $html = <<<'EOF' + + + + + +

Broken CSS selector

+ + + EOF; + + $body = $action->prepare_html_body($html); + $xpath = self::parseHtml($body); + + $this->assertCount(1, $xpath->query('//style')); + $this->assertSame('text/css', $xpath->query('//style')->item(0)->getAttribute('type')); + $this->assertSame( + '@media (min-width: 600px) { .v1body_class_name { color: red; } }', + trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('//style')->item(0)->textContent)) + ); + + $this->assertCount(1, $xpath->query('//div')); + $this->assertSame('v1bod', $xpath->query('//div')->item(0)->getAttribute('id')); + $this->assertSame('rcmBody v1body_class_name', $xpath->query('//div')->item(0)->getAttribute('class')); + $this->assertCount(1, $xpath->query('//div/p')); + } + /** * Test quote_text() method */ diff --git a/tests/Actions/Mail/IndexTest.php b/tests/Actions/Mail/IndexTest.php index 12be74c792..acbd584e2f 100644 --- a/tests/Actions/Mail/IndexTest.php +++ b/tests/Actions/Mail/IndexTest.php @@ -328,8 +328,6 @@ public function test_class() */ public function test_html() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $part = $this->get_html_part('src/htmlbody.txt'); $part->replaces = ['ex1.jpg' => 'part_1.2.jpg', 'ex2.jpg' => 'part_1.2.jpg']; @@ -364,8 +362,6 @@ public function test_html() */ public function test_html_xss() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $part = $this->get_html_part('src/htmlxss.txt'); $params = ['container_id' => 'foo', 'safe' => true]; $html = \rcmail_action_mail_index::print_body($part->body, $part, $params); @@ -383,8 +379,6 @@ public function test_html_xss() */ public function test_html_xss2() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $part = $this->get_html_part('src/BID-26800.txt'); $params = ['container_id' => 'dabody', 'safe' => true]; $washed = \rcmail_action_mail_index::print_body($part->body, $part, $params); @@ -398,8 +392,6 @@ public function test_html_xss2() */ public function test_html_xss3() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - // #1488850 $html = '

Firefox' . 'Internet Explorer

'; @@ -417,7 +409,7 @@ public function test_html_body_attributes() $part = $this->get_html_part(); $part->body = 'Foo'; - $params = ['safe' => true, 'add_comments' => false]; + $params = ['safe' => true, 'add_comments' => false, 'body_class' => '']; $washed = \rcmail_action_mail_index::print_body($part->body, $part, $params); $this->assertSame(str_replace('body', 'div', $part->body), $washed); @@ -449,15 +441,15 @@ public function test_wash_html() public function test_wash_html_body_style() { $html = '

test

'; - $params = ['container_id' => 'foo', 'add_comments' => false, 'safe' => false]; + $params = ['container_id' => 'foo', 'add_comments' => false, 'safe' => false, 'body_class' => '']; $washed = \rcmail_action_mail_index::wash_html($html, $params, []); - $this->assertSame('

test

', $washed); + $this->assertSame('

test

', $washed); $params['safe'] = true; $washed = \rcmail_action_mail_index::wash_html($html, $params, []); - $this->assertSame('

test

', $washed); + $this->assertSame('

test

', $washed); } /** @@ -468,8 +460,6 @@ public function test_wash_html_body_style() #[Group('mbstring')] public function test_washtml_utf8() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $part = $this->get_html_part('src/invalidchars.html'); $washed = \rcmail_action_mail_index::print_body($part->body, $part); @@ -481,8 +471,6 @@ public function test_washtml_utf8() */ public function test_meta_insertion() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $meta = ''; $args = [ 'inline_html' => false, @@ -521,8 +509,6 @@ public function test_meta_insertion() */ public function test_plaintext() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $part = new \rcube_message_part(); $part->ctype_primary = 'text'; $part->ctype_secondary = 'plain'; @@ -551,8 +537,6 @@ public function test_plaintext() */ public function test_mailto() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $part = $this->get_html_part('src/mailto.txt'); $params = ['container_id' => 'foo', 'safe' => false]; @@ -570,8 +554,6 @@ public function test_mailto() */ public function test_html_comments() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $part = $this->get_html_part('src/htmlcom.txt'); $washed = \rcmail_action_mail_index::print_body($part->body, $part, ['safe' => true]); @@ -586,8 +568,6 @@ public function test_html_comments() */ public function test_html_links() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - // disable relative links $html = 'test'; $body = \rcmail_action_mail_index::print_body($html, $this->get_html_part(), ['safe' => false, 'plain' => false]); @@ -609,8 +589,6 @@ public function test_html_links() */ public function test_html_link_xss() { - $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', ''); - $html = 'test'; $body = \rcmail_action_mail_index::print_body($html, $this->get_html_part(), ['safe' => false, 'plain' => false]); diff --git a/tests/Actions/Mail/ShowTest.php b/tests/Actions/Mail/ShowTest.php index 9970edc214..abee72391b 100644 --- a/tests/Actions/Mail/ShowTest.php +++ b/tests/Actions/Mail/ShowTest.php @@ -4,6 +4,8 @@ use Roundcube\Tests\ActionTestCase; +use function Roundcube\Tests\invokeMethod; + /** * Test class to test rcmail_action_mail_show */ @@ -18,4 +20,104 @@ public function test_class() $this->assertInstanceOf(\rcmail_action::class, $object); } + + /** + * Test prepare_part_body() method + */ + public function test_prepare_part_body() + { + $action = new \rcmail_action_mail_show(); + + // HTML sample (from #9911) focusing on body attributes and styles + $html = <<<'EOF' + + + + + +

Test

+ + + EOF; + + $body = invokeMethod($action, 'prepare_part_body', [$html, $this->get_html_part()]); + $xpath = self::parseHtml($body); + + $this->assertCount(1, $xpath->query('/html/div')); + $this->assertSame('message-htmlpart1', $xpath->query('/html/div')->item(0)->getAttribute('id')); + $this->assertSame('message-htmlpart', $xpath->query('/html/div')->item(0)->getAttribute('class')); + + $this->assertCount(1, $xpath->query('/html/div/style')); + $this->assertSame('text/css', $xpath->query('/html/div/style')->item(0)->getAttribute('type')); + $this->assertSame( + '@media (min-width: 600px) { #message-htmlpart1 .v1body_class_name { color: red; } }', + trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('/html/div/style')->item(0)->textContent)) + ); + + $this->assertCount(1, $xpath->query('/html/div/div')); + $this->assertSame('v1bod', $xpath->query('/html/div/div')->item(0)->getAttribute('id')); + $this->assertSame('rcmBody v1body_class_name', $xpath->query('/html/div/div')->item(0)->getAttribute('class')); + $this->assertSame('Test', $xpath->query('/html/div/div/p')->item(0)->textContent); + + // Invoking the method again (for the next part) use a different ID/prefix + $body = invokeMethod($action, 'prepare_part_body', [$html, $this->get_html_part()]); + $xpath = self::parseHtml($body); + + $this->assertCount(1, $xpath->query('/html/div')); + $this->assertSame('message-htmlpart2', $xpath->query('/html/div')->item(0)->getAttribute('id')); + $this->assertSame('message-htmlpart', $xpath->query('/html/div')->item(0)->getAttribute('class')); + + $this->assertCount(1, $xpath->query('/html/div/style')); + $this->assertSame( + '@media (min-width: 600px) { #message-htmlpart2 .v2body_class_name { color: red; } }', + trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('/html/div/style')->item(0)->textContent)) + ); + + $this->assertCount(1, $xpath->query('/html/div/div')); + $this->assertSame('v2bod', $xpath->query('/html/div/div')->item(0)->getAttribute('id')); + $this->assertSame('rcmBody v2body_class_name', $xpath->query('/html/div/div')->item(0)->getAttribute('class')); + + // Another HTML sample + $html = <<<'EOF' + + + +

Test

+ + + EOF; + + $body = invokeMethod($action, 'prepare_part_body', [$html, $this->get_html_part()]); + $xpath = self::parseHtml($body); + + $this->assertSame('message-htmlpart3', $xpath->query('/html/div')->item(0)->getAttribute('id')); + $this->assertSame( + '#message-htmlpart3 .rcmBody, #message-htmlpart3 .rcmBody > p { color: red; } ' + . '#message-htmlpart3 .v3bt p, #message-htmlpart3 .rcmBody.v3bt { color: unset; }', + trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('/html/div/style')->item(0)->textContent)) + ); + $this->assertSame('', $xpath->query('/html/div/div')->item(0)->getAttribute('id')); + $this->assertSame('rcmBody v3bt', $xpath->query('/html/div/div')->item(0)->getAttribute('class')); + } + + /** + * Helper method to create a HTML message part object + */ + protected function get_html_part($body = null) + { + $part = new \rcube_message_part(); + $part->ctype_primary = 'text'; + $part->ctype_secondary = 'html'; + $part->body = $body ? file_get_contents(TESTS_DIR . $body) : null; + $part->replaces = []; + + return $part; + } } diff --git a/tests/Actions/Utils/ModcssTest.php b/tests/Actions/Utils/ModcssTest.php index 2f93d60361..4089a205ad 100644 --- a/tests/Actions/Utils/ModcssTest.php +++ b/tests/Actions/Utils/ModcssTest.php @@ -49,7 +49,7 @@ public function test_run() $this->assertNull($output->getOutput()); // Valid url pointing to non-existing resource - $_SESSION['modcssurls'][$key] = $url; + $_SESSION['modcssurls'][$key] = ['url' => $url]; HttpClientMock::setResponses([ [404], @@ -64,9 +64,11 @@ public function test_run() // Valid url pointing to an existing resource $url = 'https://raw.githubusercontent.com/roundcube/roundcubemail/master/program/resources/tinymce/content.css'; - $_SESSION['modcssurls'][$key] = $url; - $_GET['_p'] = 'prefix'; - $_GET['_c'] = 'cid'; + $_SESSION['modcssurls'][$key] = [ + 'url' => $url, + 'css_prefix' => 'prefix', + 'container_id' => 'cid', + ]; $this->runAndAssert($action, OutputHtmlMock::E_EXIT); diff --git a/tests/Framework/UtilsTest.php b/tests/Framework/UtilsTest.php index 9086d81593..1ea2a095e3 100644 --- a/tests/Framework/UtilsTest.php +++ b/tests/Framework/UtilsTest.php @@ -240,7 +240,7 @@ public function test_mod_css_styles_xss() $this->assertSame('/* evil! */', $mod); $mod = \rcube_utils::mod_css_styles("body.main2cols { background-image: url('../images/leftcol.png'); }", 'rcmbody'); - $this->assertSame('#rcmbody.main2cols {}', $mod); + $this->assertSame('#rcmbody .main2cols {}', $mod); $mod = \rcube_utils::mod_css_styles('p { left:expression(document.body.offsetWidth-20); }', 'rcmbody'); $this->assertSame('#rcmbody p {}', $mod); diff --git a/tests/MessageRendering/InlineImageTest.php b/tests/MessageRendering/InlineImageTest.php index b96f089d3a..8d17fb00f1 100644 --- a/tests/MessageRendering/InlineImageTest.php +++ b/tests/MessageRendering/InlineImageTest.php @@ -24,7 +24,7 @@ public function testImageFromDataUri() $this->assertStringContainsString('?_task=mail&_action=get&_mbox=INBOX&_uid=', $src); $this->assertStringContainsString('&_part=2&_embed=1&_mimeclass=image', $src); - $this->assertSame('v1signature', $divElements[2]->attributes->getNamedItem('class')->textContent); + $this->assertStringMatchesFormat('v%isignature', $divElements[2]->attributes->getNamedItem('class')->textContent); // This matches a non-breakable space. $this->assertMatchesRegularExpression('|^\x{00a0}$|u', $divElements[2]->textContent);