/** * @dataProvider data_replace */ function test_replace($input, $output) { $replacer = new rcube_string_replacer(); $result = $replacer->replace($input); $result = $replacer->resolve($result); $this->assertEquals($output, $result); }
function test_linkrefs() { $input = "This is a sample message [1] to test the new linkref [ref0] replacement feature of [Roundcube].\n"; $input .= "\n"; $input .= "[1] http://en.wikipedia.org/wiki/Email\n"; $input .= "[ref0] www.link-ref.com\n"; $replacer = new rcube_string_replacer(); $result = $replacer->replace($input); $result = $replacer->resolve($result); $this->assertContains('[<a href="http://en.wikipedia.org/wiki/Email">1</a>] to', $result, "Numeric linkref replacements"); $this->assertContains('[<a href="http://www.link-ref.com">ref0</a>] repl', $result, "Alphanum linkref replacements"); $this->assertContains('of [Roundcube].', $result, "Don't touch strings wihtout an index entry"); }
/** * Replace all css definitions with #container [def] * and remove css-inlined scripting * * @param string CSS source code * @param string Container ID to use as prefix * * @return string Modified CSS source */ public static function mod_css_styles($source, $container_id, $allow_remote = false) { $last_pos = 0; $replacements = new rcube_string_replacer(); // ignore the whole block if evil styles are detected $source = self::xss_entity_decode($source); $stripped = preg_replace('/[^a-z\\(:;]/i', '', $source); $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\\(' : ''); if (preg_match("/{$evilexpr}/i", $stripped)) { return '/* evil! */'; } // cut out all contents between { and } while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) { $styles = substr($source, $pos + 1, $pos2 - ($pos + 1)); // check every line of a style block... if ($allow_remote) { $a_styles = preg_split('/;[\\r\\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY); foreach ($a_styles as $line) { $stripped = preg_replace('/[^a-z\\(:;]/i', '', $line); // ... and only allow strict url() values $regexp = '!url\\s*\\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\\)!Uims'; if (stripos($stripped, 'url(') && !preg_match($regexp, $line)) { $a_styles = array('/* evil! */'); break; } } $styles = join(";\n", $a_styles); } $key = $replacements->add($styles); $source = substr($source, 0, $pos + 1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source) - $pos2); $last_pos = $pos + 2; } // remove html comments and add #container to each tag selector. // also replace body definition because we also stripped off the <body> tag $styles = preg_replace(array('/(^\\s*<!--)|(-->\\s*$)/', '/(^\\s*|,\\s*|\\}\\s*)([a-z0-9\\._#\\*][a-z0-9\\.\\-_]*)/im', '/' . preg_quote($container_id, '/') . '\\s+body/i'), array('', "\\1#{$container_id} \\2", $container_id), $source); // put block contents back in $styles = $replacements->resolve($styles); return $styles; }
/** * Replace all css definitions with #container [def] * and remove css-inlined scripting * * @param string CSS source code * @param string Container ID to use as prefix * * @return string Modified CSS source */ public static function mod_css_styles($source, $container_id, $allow_remote=false) { $last_pos = 0; $replacements = new rcube_string_replacer; // ignore the whole block if evil styles are detected $source = self::xss_entity_decode($source); $stripped = preg_replace('/[^a-z\(:;]/i', '', $source); $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\(' : ''); if (preg_match("/$evilexpr/i", $stripped)) { return '/* evil! */'; } $strict_url_regexp = '!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims'; // cut out all contents between { and } while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) { $nested = strpos($source, '{', $pos+1); if ($nested && $nested < $pos2) // when dealing with nested blocks (e.g. @media), take the inner one $pos = $nested; $length = $pos2 - $pos - 1; $styles = substr($source, $pos+1, $length); // check every line of a style block... if ($allow_remote) { $a_styles = preg_split('/;[\r\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY); foreach ($a_styles as $line) { $stripped = preg_replace('/[^a-z\(:;]/i', '', $line); // ... and only allow strict url() values if (stripos($stripped, 'url(') && !preg_match($strict_url_regexp, $line)) { $a_styles = array('/* evil! */'); break; } } $styles = join(";\n", $a_styles); } $key = $replacements->add($styles); $repl = $replacements->get_replacement($key); $source = substr_replace($source, $repl, $pos+1, $length); $last_pos = $pos2 - ($length - strlen($repl)); } // remove html comments and add #container to each tag selector. // also replace body definition because we also stripped off the <body> tag $source = preg_replace( array( '/(^\s*<\!--)|(-->\s*$)/m', '/(^\s*|,\s*|\}\s*)([a-z0-9\._#\*][a-z0-9\.\-_]*)/im', '/'.preg_quote($container_id, '/').'\s+body/i', ), array( '', "\\1#$container_id \\2", $container_id, ), $source); // put block contents back in $source = $replacements->resolve($source); return $source; }