Index: branches/wmf-deployment/maintenance/parserTests.txt |
— | — | @@ -4388,7 +4388,24 @@ |
4389 | 4389 | |
4390 | 4390 | !! end |
4391 | 4391 | |
| 4392 | +!! test |
| 4393 | +CSS line continuation 1 |
| 4394 | +!! input |
| 4395 | +<div style="background-image: u\ rl(test.jpg);"></div> |
| 4396 | +!! result |
| 4397 | +<div></div> |
4392 | 4398 | |
| 4399 | +!! end |
| 4400 | + |
| 4401 | +!! test |
| 4402 | +CSS line continuation 2 |
| 4403 | +!! input |
| 4404 | +<div style="background-image: u\ rl(test.jpg); "></div> |
| 4405 | +!! result |
| 4406 | +<div></div> |
| 4407 | + |
| 4408 | +!! end |
| 4409 | + |
4393 | 4410 | !! article |
4394 | 4411 | Template:Identity |
4395 | 4412 | !! text |
Index: branches/wmf-deployment/includes/Sanitizer.php |
— | — | @@ -665,24 +665,48 @@ |
666 | 666 | * @return Mixed |
667 | 667 | */ |
668 | 668 | static function checkCss( $value ) { |
669 | | - $stripped = Sanitizer::decodeCharReferences( $value ); |
| 669 | + $value = Sanitizer::decodeCharReferences( $value ); |
670 | 670 | |
671 | 671 | // Remove any comments; IE gets token splitting wrong |
672 | | - $stripped = StringUtils::delimiterReplace( '/*', '*/', ' ', $stripped ); |
| 672 | + $value = StringUtils::delimiterReplace( '/*', '*/', ' ', $value ); |
673 | 673 | |
674 | | - $value = $stripped; |
675 | | - |
676 | | - // ... and continue checks |
677 | | - $stripped = preg_replace( '!\\\\([0-9A-Fa-f]{1,6})[ \\n\\r\\t\\f]?!e', |
678 | | - 'codepointToUtf8(hexdec("$1"))', $stripped ); |
679 | | - $stripped = str_replace( '\\', '', $stripped ); |
680 | | - if( preg_match( '/(?:expression|tps*:\/\/|url\\s*\().*/is', |
681 | | - $stripped ) ) { |
682 | | - # haxx0r |
| 674 | + // Decode escape sequences and line continuation |
| 675 | + // See the grammar in the CSS 2 spec, appendix D, Mozilla implements it accurately. |
| 676 | + // IE 8 doesn't implement it at all, but there's no way to introduce url() into |
| 677 | + // IE that doesn't hit Mozilla also. |
| 678 | + static $decodeRegex; |
| 679 | + if ( !$decodeRegex ) { |
| 680 | + $space = '[\\x20\\t\\r\\n\\f]'; |
| 681 | + $nl = '(?:\\n|\\r\\n|\\r|\\f)'; |
| 682 | + $backslash = '\\\\'; |
| 683 | + $decodeRegex = "/ $backslash |
| 684 | + (?: |
| 685 | + ($nl) | # 1. Line continuation |
| 686 | + ([0-9A-Fa-f]{1,6})$space? | # 2. character number |
| 687 | + (.) # 3. backslash cancelling special meaning |
| 688 | + )/xu"; |
| 689 | + } |
| 690 | + $decoded = preg_replace_callback( $decodeRegex, |
| 691 | + array( __CLASS__, 'cssDecodeCallback' ), $value ); |
| 692 | + if ( preg_match( '!expression|https?://|url\s*\(!i', $decoded ) ) { |
| 693 | + // Not allowed |
683 | 694 | return false; |
| 695 | + } else { |
| 696 | + // Allowed, return CSS with comments stripped |
| 697 | + return $value; |
684 | 698 | } |
| 699 | + } |
685 | 700 | |
686 | | - return $value; |
| 701 | + static function cssDecodeCallback( $matches ) { |
| 702 | + if ( $matches[1] !== '' ) { |
| 703 | + return ''; |
| 704 | + } elseif ( $matches[2] !== '' ) { |
| 705 | + return codepointToUtf8( hexdec( $matches[2] ) ); |
| 706 | + } elseif ( $matches[3] !== '' ) { |
| 707 | + return $matches[3]; |
| 708 | + } else { |
| 709 | + throw new MWException( __METHOD__.': invalid match' ); |
| 710 | + } |
687 | 711 | } |
688 | 712 | |
689 | 713 | /** |