Index: branches/REL1_16/phase3/maintenance/parserTests.txt |
— | — | @@ -4438,7 +4438,24 @@ |
4439 | 4439 | |
4440 | 4440 | !! end |
4441 | 4441 | |
| 4442 | +!! test |
| 4443 | +CSS line continuation 1 |
| 4444 | +!! input |
| 4445 | +<div style="background-image: u\ rl(test.jpg);"></div> |
| 4446 | +!! result |
| 4447 | +<div></div> |
4442 | 4448 | |
| 4449 | +!! end |
| 4450 | + |
| 4451 | +!! test |
| 4452 | +CSS line continuation 2 |
| 4453 | +!! input |
| 4454 | +<div style="background-image: u\ rl(test.jpg); "></div> |
| 4455 | +!! result |
| 4456 | +<div></div> |
| 4457 | + |
| 4458 | +!! end |
| 4459 | + |
4443 | 4460 | !! article |
4444 | 4461 | Template:Identity |
4445 | 4462 | !! text |
Index: branches/REL1_16/phase3/includes/Sanitizer.php |
— | — | @@ -738,24 +738,48 @@ |
739 | 739 | * @return Mixed |
740 | 740 | */ |
741 | 741 | static function checkCss( $value ) { |
742 | | - $stripped = Sanitizer::decodeCharReferences( $value ); |
| 742 | + $value = Sanitizer::decodeCharReferences( $value ); |
743 | 743 | |
744 | 744 | // Remove any comments; IE gets token splitting wrong |
745 | | - $stripped = StringUtils::delimiterReplace( '/*', '*/', ' ', $stripped ); |
| 745 | + $value = StringUtils::delimiterReplace( '/*', '*/', ' ', $value ); |
746 | 746 | |
747 | | - $value = $stripped; |
748 | | - |
749 | | - // ... and continue checks |
750 | | - $stripped = preg_replace( '!\\\\([0-9A-Fa-f]{1,6})[ \\n\\r\\t\\f]?!e', |
751 | | - 'codepointToUtf8(hexdec("$1"))', $stripped ); |
752 | | - $stripped = str_replace( '\\', '', $stripped ); |
753 | | - if( preg_match( '/(?:expression|tps*:\/\/|url\\s*\().*/is', |
754 | | - $stripped ) ) { |
755 | | - # haxx0r |
| 747 | + // Decode escape sequences and line continuation |
| 748 | + // See the grammar in the CSS 2 spec, appendix D, Mozilla implements it accurately. |
| 749 | + // IE 8 doesn't implement it at all, but there's no way to introduce url() into |
| 750 | + // IE that doesn't hit Mozilla also. |
| 751 | + static $decodeRegex; |
| 752 | + if ( !$decodeRegex ) { |
| 753 | + $space = '[\\x20\\t\\r\\n\\f]'; |
| 754 | + $nl = '(?:\\n|\\r\\n|\\r|\\f)'; |
| 755 | + $backslash = '\\\\'; |
| 756 | + $decodeRegex = "/ $backslash |
| 757 | + (?: |
| 758 | + ($nl) | # 1. Line continuation |
| 759 | + ([0-9A-Fa-f]{1,6})$space? | # 2. character number |
| 760 | + (.) # 3. backslash cancelling special meaning |
| 761 | + )/xu"; |
| 762 | + } |
| 763 | + $decoded = preg_replace_callback( $decodeRegex, |
| 764 | + array( __CLASS__, 'cssDecodeCallback' ), $value ); |
| 765 | + if ( preg_match( '!expression|https?://|url\s*\(!i', $decoded ) ) { |
| 766 | + // Not allowed |
756 | 767 | return false; |
| 768 | + } else { |
| 769 | + // Allowed, return CSS with comments stripped |
| 770 | + return $value; |
757 | 771 | } |
| 772 | + } |
758 | 773 | |
759 | | - return $value; |
| 774 | + static function cssDecodeCallback( $matches ) { |
| 775 | + if ( $matches[1] !== '' ) { |
| 776 | + return ''; |
| 777 | + } elseif ( $matches[2] !== '' ) { |
| 778 | + return codepointToUtf8( hexdec( $matches[2] ) ); |
| 779 | + } elseif ( $matches[3] !== '' ) { |
| 780 | + return $matches[3]; |
| 781 | + } else { |
| 782 | + throw new MWException( __METHOD__.': invalid match' ); |
| 783 | + } |
760 | 784 | } |
761 | 785 | |
762 | 786 | /** |