Proyectos de Subversion Moodle

Rev

Rev 1 | Mostrar el archivo completo | | | Autoría | Ultima modificación | Ver Log |

Rev 1 Rev 1441
Línea 414... Línea 414...
414
     *
414
     *
415
     * @return string Modified CSS source
415
     * @return string Modified CSS source
416
     */
416
     */
417
    public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '')
417
    public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '')
418
    {
418
    {
419
        $last_pos     = 0;
-
 
420
        $replacements = new rcube_string_replacer;
-
 
421
 
-
 
422
        // ignore the whole block if evil styles are detected
-
 
423
        $source   = self::xss_entity_decode($source);
419
        $source   = self::xss_entity_decode($source);
424
        $stripped = preg_replace('/[^a-z\(:;]/i', '', $source);
-
 
425
        $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\((?!data:image)' : '');
-
 
Línea -... Línea 420...
-
 
420
 
-
 
421
        // No @import allowed
426
 
422
        // TODO: We should just remove it, not invalidate the whole content
427
        if (preg_match("/$evilexpr/i", $stripped)) {
423
        if (stripos($source, '@import') !== false) {
428
            return '/* evil! */';
424
            return '/* evil! */';
Línea -... Línea 425...
-
 
425
        }
-
 
426
 
-
 
427
        // Incomplete style expression
-
 
428
        if (strpos($source, '{') === false) {
-
 
429
            return '/* invalid! */';
-
 
430
        }
-
 
431
 
429
        }
432
        // To prevent from a double-escaping tricks we consider a script with
-
 
433
        // any escape sequences (after de-escaping them above) an evil script.
-
 
434
        // This probably catches many valid scripts, but we\'re on the safe side.
-
 
435
        if (preg_match('/\\\[0-9a-fA-F]{2}/', $source)) {
Línea 430... Línea 436...
430
 
436
            return '/* evil! */';
431
        $strict_url_regexp = '!url\s*\(\s*["\']?(https?:)//[a-z0-9/._+-]+["\']?\s*\)!Uims';
437
        }
Línea -... Línea 438...
-
 
438
 
-
 
439
        // remove html comments
-
 
440
        $source = preg_replace('/(^\s*<\!--)|(-->\s*$)/m', '', $source);
-
 
441
 
-
 
442
        $url_callback = static function ($url) use ($allow_remote) {
-
 
443
            if (strpos($url, 'data:image') === 0) {
-
 
444
                return $url;
-
 
445
            }
-
 
446
            if ($allow_remote && preg_match('|^https?://[a-z0-9/._+-]+$|i', $url)) {
-
 
447
                return $url;
-
 
448
            }
-
 
449
        };
432
 
450
 
433
        // remove html comments
451
        $last_pos = 0;
434
        $source = preg_replace('/(^\s*<\!--)|(-->\s*$)/m', '', $source);
452
        $replacements = new rcube_string_replacer();
435
 
453
 
436
        // cut out all contents between { and }
454
        // cut out all contents between { and }
437
        while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) {
455
        while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) {
438
            $nested = strpos($source, '{', $pos+1);
456
            $nested = strpos($source, '{', $pos+1);
439
            if ($nested && $nested < $pos2) { // when dealing with nested blocks (e.g. @media), take the inner one
457
            if ($nested && $nested < $pos2) { // when dealing with nested blocks (e.g. @media), take the inner one
440
                $pos = $nested;
-
 
441
            }
-
 
442
            $length = $pos2 - $pos - 1;
-
 
443
            $styles = substr($source, $pos+1, $length);
458
                $pos = $nested;
444
            $output = '';
-
 
445
 
-
 
446
            // check every css rule in the style block...
-
 
447
            foreach (self::parse_css_block($styles) as $rule) {
-
 
448
                // Remove 'page' attributes (#7604)
-
 
449
                if ($rule[0] == 'page') {
-
 
450
                    continue;
-
 
451
                }
-
 
452
 
-
 
453
                // Convert position:fixed to position:absolute (#5264)
-
 
454
                if ($rule[0] == 'position' && strcasecmp($rule[1], 'fixed') === 0) {
-
 
Línea 455... Línea -...
455
                    $rule[1] = 'absolute';
-
 
456
                }
-
 
457
                else if ($allow_remote) {
-
 
458
                    $stripped = preg_replace('/[^a-z\(:;]/i', '', $rule[1]);
-
 
459
 
-
 
460
                    // allow data:image and strict url() values only
-
 
461
                    if (
-
 
462
                        stripos($stripped, 'url(') !== false
-
 
463
                        && stripos($stripped, 'url(data:image') === false
-
 
464
                        && !preg_match($strict_url_regexp, $rule[1])
-
 
465
                    ) {
-
 
466
                        $rule[1] = '/* evil! */';
-
 
467
                    }
-
 
468
                }
459
            }
469
 
460
            $length = $pos2 - $pos - 1;
470
                $output .= sprintf(" %s: %s;", $rule[0] , $rule[1]);
461
            $styles = substr($source, $pos+1, $length);
471
            }
462
            $styles = self::sanitize_css_block($styles, $url_callback);
472
 
463
 
Línea 517... Línea 508...
517
 
508
 
518
        return $source;
509
        return $source;
Línea 519... Línea 510...
519
    }
510
    }
-
 
511
 
-
 
512
    /**
-
 
513
     * Parse and sanitize single CSS block
-
 
514
     *
-
 
515
     * @param string    $styles       CSS styles block
-
 
516
     * @param ?callable $url_callback URL validator callback
-
 
517
     *
-
 
518
     * @return string
-
 
519
     */
-
 
520
    public static function sanitize_css_block($styles, $url_callback = null)
-
 
521
    {
-
 
522
        $output = [];
-
 
523
 
-
 
524
        // check every css rule in the style block...
-
 
525
        foreach (self::parse_css_block($styles) as $rule) {
-
 
526
            $property = $rule[0];
-
 
527
            $value = $rule[1];
-
 
528
 
-
 
529
            if ($property == 'page') {
-
 
530
                // Remove 'page' attributes (#7604)
-
 
531
                continue;
-
 
532
            } elseif ($property == 'position' && strcasecmp($value, 'fixed') === 0) {
-
 
533
                // Convert position:fixed to position:absolute (#5264)
-
 
534
                $value = 'absolute';
-
 
535
            } elseif (preg_match('/expression|image-set/i', $value)) {
-
 
536
                continue;
-
 
537
            } else {
-
 
538
                $value = '';
-
 
539
                foreach (self::explode_css_property_block($rule[1]) as $val) {
-
 
540
                    if ($url_callback && preg_match('/^url\s*\(/i', $val)) {
-
 
541
                        if (preg_match('/^url\s*\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)/iu', $val, $match)) {
-
 
542
                            if ($url = $url_callback($match[1])) {
-
 
543
                                $value .= ' url(' . $url . ')';
-
 
544
                            }
-
 
545
                        }
-
 
546
                    } else {
-
 
547
                        // whitelist ?
-
 
548
                        $value .= ' ' . $val;
-
 
549
 
-
 
550
                        // #1488535: Fix size units, so width:800 would be changed to width:800px
-
 
551
                        if ($val
-
 
552
                            && preg_match('/^(left|right|top|bottom|width|height)/i', $property)
-
 
553
                            && preg_match('/^[0-9]+$/', $val)
-
 
554
                        ) {
-
 
555
                            $value .= 'px';
-
 
556
                        }
-
 
557
                    }
-
 
558
                }
-
 
559
            }
-
 
560
 
-
 
561
            if (strlen($value)) {
-
 
562
                $output[] = $property . ': ' . trim($value);
-
 
563
            }
-
 
564
        }
-
 
565
 
-
 
566
        return count($output) > 0 ? implode('; ', $output) . ';' : '';
-
 
567
    }
520
 
568
 
521
    /**
569
    /**
522
     * Explode css style. Property names will be lower-cased and trimmed.
570
     * Explode css style. Property names will be lower-cased and trimmed.
523
     * Values will be trimmed. Invalid entries will be skipped.
571
     * Values will be trimmed. Invalid entries will be skipped.
524
     *
572
     *
Línea 589... Línea 637...
589
 
637
 
590
        return $result;
638
        return $result;
Línea 591... Línea 639...
591
    }
639
    }
-
 
640
 
-
 
641
    /**
-
 
642
     * Explode css style value
-
 
643
     *
-
 
644
     * @param string $style CSS style
-
 
645
     *
-
 
646
     * @return array List of CSS values
-
 
647
     */
-
 
648
    public static function explode_css_property_block($style)
-
 
649
    {
-
 
650
        $style = preg_replace('/\s+/', ' ', $style);
-
 
651
        $result = [];
-
 
652
        $strlen = strlen($style);
-
 
653
        $q = false;
-
 
654
 
-
 
655
        // explode value
-
 
656
        for ($p = $i = 0; $i < $strlen; $i++) {
-
 
657
            if (($style[$i] == '"' || $style[$i] == "'") && ($i == 0 || $style[$i - 1] != '\\')) {
-
 
658
                if ($q == $style[$i]) {
-
 
659
                    $q = false;
-
 
660
                } elseif (!$q) {
-
 
661
                    $q = $style[$i];
-
 
662
                }
-
 
663
            }
-
 
664
 
-
 
665
            if (!$q && $style[$i] == ' ' && ($i == 0 || !preg_match('/[,\(]/', $style[$i - 1]))) {
-
 
666
                $result[] = substr($style, $p, $i - $p);
-
 
667
                $p = $i + 1;
-
 
668
            }
-
 
669
        }
-
 
670
 
-
 
671
        $result[] = (string) substr($style, $p);
-
 
672
 
-
 
673
        return $result;
-
 
674
    }
592
 
675
 
593
    /**
676
    /**
594
     * Generate CSS classes from mimetype and filename extension
677
     * Generate CSS classes from mimetype and filename extension
595
     *
678
     *
596
     * @param string $mimetype Mimetype
679
     * @param string $mimetype Mimetype