| Index: trunk/phase3/includes/Sanitizer.php |
| — | — | @@ -650,10 +650,6 @@ |
| 651 | 651 | # http://msdn.microsoft.com/workshop/author/dhtml/overview/recalc.asp |
| 652 | 652 | if( $attribute == 'style' ) { |
| 653 | 653 | $value = Sanitizer::checkCss( $value ); |
| 654 | | - if( $value === false ) { |
| 655 | | - # haxx0r |
| 656 | | - continue; |
| 657 | | - } |
| 658 | 654 | } |
| 659 | 655 | |
| 660 | 656 | if ( $attribute === 'id' ) { |
| — | — | @@ -750,10 +746,8 @@ |
| 751 | 747 | $value = StringUtils::delimiterReplace( '/*', '*/', ' ', $value ); |
| 752 | 748 | |
| 753 | 749 | // Decode escape sequences and line continuation |
| 754 | | - // See the grammar in the CSS 2 spec, appendix D, Mozilla implements it accurately. |
| 755 | | - // IE 8 doesn't implement it at all, but there's no way to introduce url() into |
| 756 | | - // IE that doesn't hit Mozilla also. |
| 757 | | - static $decodeRegex; |
| | 750 | + // See the grammar in the CSS 2 spec, appendix D. |
| | 751 | + static $decodeRegex, $reencodeTable; |
| 758 | 752 | if ( !$decodeRegex ) { |
| 759 | 753 | $space = '[\\x20\\t\\r\\n\\f]'; |
| 760 | 754 | $nl = '(?:\\n|\\r\\n|\\r|\\f)'; |
| — | — | @@ -762,30 +756,41 @@ |
| 763 | 757 | (?: |
| 764 | 758 | ($nl) | # 1. Line continuation |
| 765 | 759 | ([0-9A-Fa-f]{1,6})$space? | # 2. character number |
| 766 | | - (.) # 3. backslash cancelling special meaning |
| | 760 | + (.) | # 3. backslash cancelling special meaning |
| | 761 | + () | # 4. backslash at end of string |
| 767 | 762 | )/xu"; |
| 768 | 763 | } |
| 769 | | - $decoded = preg_replace_callback( $decodeRegex, |
| | 764 | + $value = preg_replace_callback( $decodeRegex, |
| 770 | 765 | array( __CLASS__, 'cssDecodeCallback' ), $value ); |
| 771 | | - if ( preg_match( '!expression|https?://|url\s*\(!i', $decoded ) ) { |
| 772 | | - // Not allowed |
| 773 | | - return false; |
| 774 | | - } else { |
| 775 | | - // Allowed, return CSS with comments stripped |
| 776 | | - return $value; |
| | 766 | + |
| | 767 | + // Reject problematic keywords and control characters |
| | 768 | + if ( preg_match( '/[\000-\010\016-\037\177]/', $value ) ) { |
| | 769 | + return '/* invalid control char */'; |
| | 770 | + } elseif ( preg_match( '! expression | filter\s*: | accelerator\s*: | url\s*\( !ix', $value ) ) { |
| | 771 | + return '/* insecure input */'; |
| 777 | 772 | } |
| | 773 | + return $value; |
| 778 | 774 | } |
| 779 | 775 | |
| 780 | 776 | static function cssDecodeCallback( $matches ) { |
| 781 | 777 | if ( $matches[1] !== '' ) { |
| | 778 | + // Line continuation |
| 782 | 779 | return ''; |
| 783 | 780 | } elseif ( $matches[2] !== '' ) { |
| 784 | | - return codepointToUtf8( hexdec( $matches[2] ) ); |
| | 781 | + $char = codepointToUtf8( hexdec( $matches[2] ) ); |
| 785 | 782 | } elseif ( $matches[3] !== '' ) { |
| 786 | | - return $matches[3]; |
| | 783 | + $char = $matches[3]; |
| 787 | 784 | } else { |
| 788 | | - throw new MWException( __METHOD__.': invalid match' ); |
| | 785 | + $char = '\\'; |
| 789 | 786 | } |
| | 787 | + if ( $char == "\n" || $char == '"' || $char == "'" || $char == '\\' ) { |
| | 788 | + // These characters need to be escaped in strings |
| | 789 | + // Clean up the escape sequence to avoid parsing errors by clients |
| | 790 | + return '\\' . dechex( ord( $char ) ) . ' '; |
| | 791 | + } else { |
| | 792 | + // Decode unnecessary escape |
| | 793 | + return $char; |
| | 794 | + } |
| 790 | 795 | } |
| 791 | 796 | |
| 792 | 797 | /** |