Index: branches/wmf/1.16wmf4/includes/StubObject.php |
— | — | @@ -152,7 +152,7 @@ |
153 | 153 | $code = strtolower( $code ); |
154 | 154 | |
155 | 155 | # Validate $code |
156 | | - if( empty( $code ) || !preg_match( '/^[a-z-]+$/', $code ) || ( $code === 'qqq' ) ) { |
| 156 | + if( empty( $code ) || !Language::isValidCode( $code ) || ( $code === 'qqq' ) ) { |
157 | 157 | wfDebug( "Invalid user language code\n" ); |
158 | 158 | $code = $wgContLanguageCode; |
159 | 159 | } |
Index: branches/wmf/1.16wmf4/includes/Sanitizer.php |
— | — | @@ -739,6 +739,13 @@ |
740 | 740 | // Remove any comments; IE gets token splitting wrong |
741 | 741 | $value = StringUtils::delimiterReplace( '/*', '*/', ' ', $value ); |
742 | 742 | |
| 743 | + // Remove anything after a comment-start token, to guard against |
| 744 | + // incorrect client implementations. |
| 745 | + $commentPos = strpos( $value, '/*' ); |
| 746 | + if ( $commentPos !== false ) { |
| 747 | + $value = substr( $value, 0, $commentPos ); |
| 748 | + } |
| 749 | + |
743 | 750 | // Decode escape sequences and line continuation |
744 | 751 | // See the grammar in the CSS 2 spec, appendix D. |
745 | 752 | static $decodeRegex, $reencodeTable; |
Index: branches/wmf/1.16wmf4/includes/StringUtils.php |
— | — | @@ -77,16 +77,20 @@ |
78 | 78 | } |
79 | 79 | |
80 | 80 | if ( $tokenType == 'start' ) { |
81 | | - $inputPos = $tokenOffset + $tokenLength; |
82 | 81 | # Only move the start position if we haven't already found a start |
83 | 82 | # This means that START START END matches outer pair |
84 | 83 | if ( !$foundStart ) { |
85 | 84 | # Found start |
| 85 | + $inputPos = $tokenOffset + $tokenLength; |
86 | 86 | # Write out the non-matching section |
87 | 87 | $output .= substr( $subject, $outputPos, $tokenOffset - $outputPos ); |
88 | 88 | $outputPos = $tokenOffset; |
89 | 89 | $contentPos = $inputPos; |
90 | 90 | $foundStart = true; |
| 91 | + } else { |
| 92 | + # Move the input position past the *first character* of START, |
| 93 | + # to protect against missing END when it overlaps with START |
| 94 | + $inputPos = $tokenOffset + 1; |
91 | 95 | } |
92 | 96 | } elseif ( $tokenType == 'end' ) { |
93 | 97 | if ( $foundStart ) { |
Index: branches/wmf/1.16wmf4/languages/Language.php |
— | — | @@ -144,6 +144,14 @@ |
145 | 145 | protected static function newFromCode( $code ) { |
146 | 146 | global $IP; |
147 | 147 | static $recursionLevel = 0; |
| 148 | + |
| 149 | + // Protect against path traversal below |
| 150 | + if ( !Language::isValidCode( $code ) |
| 151 | + || strcspn( $code, "/\\\000" ) !== strlen( $code ) ) |
| 152 | + { |
| 153 | + throw new MWException( "Invalid language code \"$code\"" ); |
| 154 | + } |
| 155 | + |
148 | 156 | if ( $code == 'en' ) { |
149 | 157 | $class = 'Language'; |
150 | 158 | } else { |
— | — | @@ -174,6 +182,14 @@ |
175 | 183 | } |
176 | 184 | |
177 | 185 | /** |
| 186 | + * Returns true if a language code string is of a valid form, whether or |
| 187 | + * not it exists. |
| 188 | + */ |
| 189 | + public static function isValidCode( $code ) { |
| 190 | + return (bool)preg_match( '/^[a-z-]+$/', $code ); |
| 191 | + } |
| 192 | + |
| 193 | + /** |
178 | 194 | * Get the LocalisationCache instance |
179 | 195 | */ |
180 | 196 | public static function getLocalisationCache() { |
— | — | @@ -2627,6 +2643,13 @@ |
2628 | 2644 | * @return string $prefix . $mangledCode . $suffix |
2629 | 2645 | */ |
2630 | 2646 | static function getFileName( $prefix = 'Language', $code, $suffix = '.php' ) { |
| 2647 | + // Protect against path traversal |
| 2648 | + if ( !Language::isValidCode( $code ) |
| 2649 | + || strcspn( $code, "/\\\000" ) !== strlen( $code ) ) |
| 2650 | + { |
| 2651 | + throw new MWException( "Invalid language code \"$code\"" ); |
| 2652 | + } |
| 2653 | + |
2631 | 2654 | return $prefix . str_replace( '-', '_', ucfirst( $code ) ) . $suffix; |
2632 | 2655 | } |
2633 | 2656 | |