/**
  * @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);
 }
Example #2
0
 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;
 }
Example #4
0
    /**
     * 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;
    }