diff --git a/CHANGELOG b/CHANGELOG index 4ff417ace..e2f96ff7a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -82,6 +82,7 @@ CHANGELOG Roundcube Webmail - Fix some PHP 7.4 compat. issues (#6884, #6866) - Fix security issue where it was possible to bypass the position:fixed CSS check in received messages (#6898) - Fix bug where some strict remote URIs in url() style were unintentionally blocked (#6899) +- Fix security issue where it was possible to bypass the CSS jail in HTML messages using :root pseudo-class (#6897) RELEASE 1.4-rc1 --------------- diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index f2f7bbf07..eaa1dd303 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -435,10 +435,14 @@ class rcube_utils // (?!##str) below is to not match with ##str_replacement_0## // from rcube_string_replacer used above, this is needed for // cases like @media { body { position: fixed; } } (#5811) - $regexp = '/(^\s*|,\s*|\}\s*|\{\s*)((?!##str)[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im'; + $regexp = '/(^\s*|,\s*|\}\s*|\{\s*)((?!##str):?[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im'; $callback = function($matches) use ($container_id, $prefix) { $replace = $matches[2]; + if (stripos($replace, ':root') === 0) { + $replace = substr($replace, 5); + } + if ($prefix) { $replace = str_replace(array('.', '#'), array(".$prefix", "#$prefix"), $replace); } @@ -447,6 +451,9 @@ class rcube_utils $replace = "#$container_id " . $replace; } + // Remove redundant spaces (for simpler testing) + $replace = preg_replace('/\s+/', ' ', $replace); + return str_replace($matches[2], $replace, $matches[0]); }; diff --git a/tests/Framework/Utils.php b/tests/Framework/Utils.php index 22cf9c581..3df90ffd0 100644 --- a/tests/Framework/Utils.php +++ b/tests/Framework/Utils.php @@ -256,6 +256,8 @@ class Framework_Utils extends PHPUnit_Framework_TestCase @media screen and (max-width: 699px) and (min-width: 520px) { li a.button { padding-left: 30px; } } + :root * { color: red; } + :root > * { top: 0; } '; $mod = rcube_utils::mod_css_styles($css, 'rc', true, 'test'); @@ -268,6 +270,9 @@ class Framework_Utils extends PHPUnit_Framework_TestCase $this->assertContains('#rc p > i ', $mod); $this->assertContains('#rc div#testsome', $mod); $this->assertContains('#rc li a.testbutton', $mod); + $this->assertNotContains(':root', $mod); + $this->assertContains('#rc * ', $mod); + $this->assertContains('#rc > * ', $mod); } function test_xss_entity_decode()