Make WordPress Core

Changeset 32391


Ignore:
Timestamp:
05/06/2015 07:16:41 PM (11 years ago)
Author:
mdawaffe
Message:

WPDB: When checking that a string can be sent to MySQL, we shouldn't use mb_convert_encoding(), as it behaves differently to MySQL's character encoding conversion.

Merge of [32364] to the 3.7 branch.

Props mdawaffe, pento, nbachiyski, jorbin, johnjamesjacoby, jeremyfelt.

See #32165.

Location:
branches/3.7
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • branches/3.7/src/wp-admin/includes/upgrade.php

    r32318 r32391  
    412412                upgrade_373();
    413413
    414         if ( $wp_current_db_version < 26150 )
    415                 upgrade_378();
     414        if ( $wp_current_db_version < 26151 )
    416415
    417416        maybe_disable_link_manager();
     
    12791278 */
    12801279function upgrade_378() {
     1280}
     1281
     1282/**
     1283 * Execute changes made in WordPress 3.7.9.
     1284 *
     1285 * @since 3.7.9
     1286 */
     1287function upgrade_379() {
    12811288        global $wp_current_db_version, $wpdb;
    12821289
    1283         if ( $wp_current_db_version < 26150 ) {
     1290        if ( $wp_current_db_version < 26151 ) {
    12841291                $content_length = $wpdb->get_col_length( $wpdb->comments, 'comment_content' );
    1285                 if ( ! $content_length ) {
    1286                         $content_length = 65535;
    1287                 }
     1292                if ( false === $content_length ) {
     1293                        $content_length = array(
     1294                                'type'   => 'byte',
     1295                                'length' => 65535,
     1296                        );
     1297                } elseif ( ! is_array( $content_length ) ) {
     1298                        $length = (int) $content_length > 0 ? (int) $content_length : 65535;
     1299                        $content_length = array(
     1300                                'type'   => 'byte',
     1301                                'length' => $length
     1302                        );
     1303                }
     1304
     1305                if ( 'byte' !== $content_length['type'] ) {
     1306                        // Sites with malformed DB schemas are on their own.
     1307                        return;
     1308                }
     1309
     1310                $allowed_length = intval( $content_length['length'] ) - 10;
    12881311
    12891312                $comments = $wpdb->get_results(
    1290                         "SELECT comment_ID FROM $wpdb->comments
    1291                         WHERE comment_date_gmt > '2015-04-26'
    1292                         AND CHAR_LENGTH( comment_content ) >= $content_length
    1293                         AND ( comment_content LIKE '%<%' OR comment_content LIKE '%>%' )"
     1313                        "SELECT `comment_ID` FROM `{$wpdb->comments}`
     1314                                WHERE `comment_date_gmt` > '2015-04-26'
     1315                                AND LENGTH( `comment_content` ) >= {$allowed_length}
     1316                                AND ( `comment_content` LIKE '%<%' OR `comment_content` LIKE '%>%' )"
    12941317                );
    12951318
  • branches/3.7/src/wp-includes/compat.php

    r29388 r32391  
    1414}
    1515
    16 if ( !function_exists('mb_substr') ):
    17         function mb_substr( $str, $start, $length=null, $encoding=null ) {
    18                 return _mb_substr($str, $start, $length, $encoding);
    19         }
    20 endif;
    21 
    22 function _mb_substr( $str, $start, $length=null, $encoding=null ) {
    23         // the solution below, works only for utf-8, so in case of a different
    24         // charset, just use built-in substr
    25         $charset = get_option( 'blog_charset' );
    26         if ( !in_array( $charset, array('utf8', 'utf-8', 'UTF8', 'UTF-8') ) ) {
    27                 return is_null( $length )? substr( $str, $start ) : substr( $str, $start, $length);
    28         }
    29         // use the regex unicode support to separate the UTF-8 characters into an array
    30         preg_match_all( '/./us', $str, $match );
    31         $chars = is_null( $length )? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
    32         return implode( '', $chars );
     16/**
     17 * Returns whether PCRE/u (PCRE_UTF8 modifier) is available for use.
     18 *
     19 * @ignore
     20 * @since 4.2.2
     21 * @access private
     22 *
     23 * @param bool $set - Used for testing only
     24 *             null   : default - get PCRE/u capability
     25 *             false  : Used for testing - return false for future calls to this function
     26 *             'reset': Used for testing - restore default behavior of this function
     27 */
     28function _wp_can_use_pcre_u( $set = null ) {
     29        static $utf8_pcre = 'reset';
     30
     31        if ( null !== $set ) {
     32                $utf8_pcre = $set;
     33        }
     34
     35        if ( 'reset' === $utf8_pcre ) {
     36                $utf8_pcre = @preg_match( '/^./u', 'a' );
     37        }
     38
     39        return $utf8_pcre;
     40}
     41
     42if ( ! function_exists( 'mb_substr' ) ) :
     43        function mb_substr( $str, $start, $length = null, $encoding = null ) {
     44                return _mb_substr( $str, $start, $length, $encoding );
     45        }
     46endif;
     47
     48/*
     49 * Only understands UTF-8 and 8bit.  All other character sets will be treated as 8bit.
     50 * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
     51 * The behavior of this function for invalid inputs is undefined.
     52 */
     53function _mb_substr( $str, $start, $length = null, $encoding = null ) {
     54        if ( null === $encoding ) {
     55                $encoding = get_option( 'blog_charset' );
     56        }
     57
     58        // The solution below works only for UTF-8,
     59        // so in case of a different charset just use built-in substr()
     60        if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
     61                return is_null( $length ) ? substr( $str, $start ) : substr( $str, $start, $length );
     62        }
     63
     64        if ( _wp_can_use_pcre_u() ) {
     65                // Use the regex unicode support to separate the UTF-8 characters into an array
     66                preg_match_all( '/./us', $str, $match );
     67                $chars = is_null( $length ) ? array_slice( $match[0], $start ) : array_slice( $match[0], $start, $length );
     68                return implode( '', $chars );
     69        }
     70
     71        $regex = '/(
     72                  [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
     73                | [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
     74                | \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
     75                | [\xE1-\xEC][\x80-\xBF]{2}
     76                | \xED[\x80-\x9F][\x80-\xBF]
     77                | [\xEE-\xEF][\x80-\xBF]{2}
     78                | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
     79                | [\xF1-\xF3][\x80-\xBF]{3}
     80                | \xF4[\x80-\x8F][\x80-\xBF]{2}
     81        )/x';
     82
     83        $chars = array( '' ); // Start with 1 element instead of 0 since the first thing we do is pop
     84        do {
     85                // We had some string left over from the last round, but we counted it in that last round.
     86                array_pop( $chars );
     87
     88                // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
     89                $pieces = preg_split( $regex, $str, 1000, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY );
     90
     91                $chars = array_merge( $chars, $pieces );
     92        } while ( count( $pieces ) > 1 && $str = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
     93
     94        return join( '', array_slice( $chars, $start, $length ) );
     95}
     96
     97if ( ! function_exists( 'mb_strlen' ) ) :
     98        function mb_strlen( $str, $encoding = null ) {
     99                return _mb_strlen( $str, $encoding );
     100        }
     101endif;
     102
     103/*
     104 * Only understands UTF-8 and 8bit.  All other character sets will be treated as 8bit.
     105 * For $encoding === UTF-8, the $str input is expected to be a valid UTF-8 byte sequence.
     106 * The behavior of this function for invalid inputs is undefined.
     107 */
     108function _mb_strlen( $str, $encoding = null ) {
     109        if ( null === $encoding ) {
     110                $encoding = get_option( 'blog_charset' );
     111        }
     112
     113        // The solution below works only for UTF-8,
     114        // so in case of a different charset just use built-in strlen()
     115        if ( ! in_array( $encoding, array( 'utf8', 'utf-8', 'UTF8', 'UTF-8' ) ) ) {
     116                return strlen( $str );
     117        }
     118
     119        if ( _wp_can_use_pcre_u() ) {
     120                // Use the regex unicode support to separate the UTF-8 characters into an array
     121                preg_match_all( '/./us', $str, $match );
     122                return count( $match[0] );
     123        }
     124
     125        $regex = '/(?:
     126                  [\x00-\x7F]                  # single-byte sequences   0xxxxxxx
     127                | [\xC2-\xDF][\x80-\xBF]       # double-byte sequences   110xxxxx 10xxxxxx
     128                | \xE0[\xA0-\xBF][\x80-\xBF]   # triple-byte sequences   1110xxxx 10xxxxxx * 2
     129                | [\xE1-\xEC][\x80-\xBF]{2}
     130                | \xED[\x80-\x9F][\x80-\xBF]
     131                | [\xEE-\xEF][\x80-\xBF]{2}
     132                | \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
     133                | [\xF1-\xF3][\x80-\xBF]{3}
     134                | \xF4[\x80-\x8F][\x80-\xBF]{2}
     135        )/x';
     136
     137        $count = 1; // Start at 1 instead of 0 since the first thing we do is decrement
     138        do {
     139                // We had some string left over from the last round, but we counted it in that last round.
     140                $count--;
     141
     142                // Split by UTF-8 character, limit to 1000 characters (last array element will contain the rest of the string)
     143                $pieces = preg_split( $regex, $str, 1000 );
     144
     145                // Increment
     146                $count += count( $pieces );
     147        } while ( $str = array_pop( $pieces ) ); // If there's anything left over, repeat the loop.
     148
     149        // Fencepost: preg_split() always returns one extra item in the array
     150        return --$count;
    33151}
    34152
  • branches/3.7/src/wp-includes/version.php

    r32318 r32391  
    1212 * @global int $wp_db_version
    1313 */
    14 $wp_db_version = 26150;
     14$wp_db_version = 26151;
    1515
    1616/**
  • branches/3.7/src/wp-includes/wp-db.php

    r32318 r32391  
    13721372         */
    13731373        function _insert_replace_helper( $table, $data, $format = null, $type = 'INSERT' ) {
     1374                $this->insert_id = 0;
     1375
    13741376                if ( ! in_array( strtoupper( $type ), array( 'REPLACE', 'INSERT' ) ) ) {
    13751377                        return false;
     
    13921394                $sql = "$type INTO `$table` ($fields) VALUES ($formats)";
    13931395
    1394                 $this->insert_id = 0;
    13951396                $this->check_current_query = false;
    13961397                return $this->query( $this->prepare( $sql, $values ) );
     
    15891590                                // This checks %d/%f versus ! %s because it's sprintf() could take more.
    15901591                                $value['charset'] = false;
    1591                         } elseif ( $this->check_ascii( $value['value'] ) ) {
    1592                                 // If it's ASCII, then we don't need the charset. We can skip this field.
    1593                                 $value['charset'] = false;
    15941592                        } else {
    15951593                                $value['charset'] = $this->get_col_charset( $table, $field );
     
    15971595                                        return false;
    15981596                                }
    1599 
    1600                                 // This isn't ASCII. Don't have strip_invalid_text() re-check.
    1601                                 $value['ascii'] = false;
    16021597                        }
    16031598
     
    16301625                                        return false;
    16311626                                }
    1632                         }
    1633 
    1634                         if ( false !== $value['length'] && strlen( $value['value'] ) > $value['length'] ) {
    1635                                 return false;
    16361627                        }
    16371628
     
    19591950        /**
    19601951         * Retrieve the maximum string length allowed in a given column.
     1952         * The length may either be specified as a byte length or a character length.
    19611953         *
    19621954         * @since 4.2.1
     
    19651957         * @param string $table  Table name.
    19661958         * @param string $column Column name.
    1967          * @return mixed Max column length as an int. False if the column has no
    1968          *               length. WP_Error object if there was an error.
     1959         * @return mixed array( 'length' => (int), 'type' => 'byte' | 'char' )
     1960         *               false if the column has no length (for example, numeric column)
     1961         *               WP_Error object if there was an error.
    19691962         */
    19701963        public function get_col_length( $table, $column ) {
     
    19991992
    20001993                switch( $type ) {
     1994                        case 'char':
     1995                        case 'varchar':
     1996                                return array(
     1997                                        'type'   => 'char',
     1998                                        'length' => (int) $length,
     1999                                );
     2000                                break;
    20012001                        case 'binary':
    2002                         case 'char':
    20032002                        case 'varbinary':
    2004                         case 'varchar':
    2005                                 return $length;
     2003                                return array(
     2004                                        'type'   => 'byte',
     2005                                        'length' => (int) $length,
     2006                                );
    20062007                                break;
    20072008                        case 'tinyblob':
    20082009                        case 'tinytext':
    2009                                 return 255; // 2^8 - 1
     2010                                return array(
     2011                                        'type'   => 'byte',
     2012                                        'length' => 255,        // 2^8 - 1
     2013                                );
    20102014                                break;
    20112015                        case 'blob':
    20122016                        case 'text':
    2013                                 return 65535; // 2^16 - 1
     2017                                return array(
     2018                                        'type'   => 'byte',
     2019                                        'length' => 65535,      // 2^16 - 1
     2020                                );
    20142021                                break;
    20152022                        case 'mediumblob':
    20162023                        case 'mediumtext':
    2017                                 return 16777215; // 2^24 - 1
     2024                                return array(
     2025                                        'type'   => 'byte',
     2026                                        'length' => 16777215,   // 2^24 - 1
     2027                                );
    20182028                                break;
    20192029                        case 'longblob':
    20202030                        case 'longtext':
    2021                                 return 4294967295; // 2^32 - 1
     2031                                return array(
     2032                                        'type'   => 'byte',
     2033                                        'length' => 4294967295, // 2^32 - 1
     2034                                );
    20222035                                break;
    20232036                        default:
     
    21262139                // If any of the columns don't have one of these collations, it needs more sanity checking.
    21272140        protected function strip_invalid_text( $data ) {
    2128                 // Some multibyte character sets that we can check in PHP.
    2129                 $mb_charsets = array(
    2130                         'ascii'   => 'ASCII',
    2131                         'big5'    => 'BIG-5',
    2132                         'eucjpms' => 'eucJP-win',
    2133                         'gb2312'  => 'EUC-CN',
    2134                         'ujis'    => 'EUC-JP',
    2135                         'utf32'   => 'UTF-32',
    2136                 );
    2137 
    2138                 $supported_charsets = array();
    2139                 if ( function_exists( 'mb_list_encodings' ) ) {
    2140                         $supported_charsets = mb_list_encodings();
    2141                 }
    2142 
    21432141                $db_check_string = false;
    21442142
     
    21462144                        $charset = $value['charset'];
    21472145
    2148                         // Column isn't a string, or is latin1, which will will happily store anything.
    2149                         if ( false === $charset || 'latin1' === $charset ) {
     2146                        if ( is_array( $value['length'] ) ) {
     2147                                $length = $value['length']['length'];
     2148                        } else {
     2149                                $length = false;
     2150                        }
     2151
     2152                        // There's no charset to work with.
     2153                        if ( false === $charset ) {
    21502154                                continue;
    21512155                        }
    21522156
     2157                        // Column isn't a string.
    21532158                        if ( ! is_string( $value['value'] ) ) {
    21542159                                continue;
    21552160                        }
    21562161
    2157                         // ASCII is always OK.
    2158                         if ( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) ) {
    2159                                 continue;
    2160                         }
    2161 
    2162                         // Convert the text locally.
    2163                         if ( $supported_charsets ) {
    2164                                 if ( isset( $mb_charsets[ $charset ] ) && in_array( $mb_charsets[ $charset ], $supported_charsets ) ) {
    2165                                         $value['value'] = mb_convert_encoding( $value['value'], $mb_charsets[ $charset ], $mb_charsets[ $charset ] );
     2162                        $truncate_by_byte_length = 'byte' === $value['length']['type'];
     2163
     2164                        $needs_validation = true;
     2165                        if (
     2166                                // latin1 can store any byte sequence
     2167                                'latin1' === $charset
     2168                        ||
     2169                                // ASCII is always OK.
     2170                                ( ! isset( $value['ascii'] ) && $this->check_ascii( $value['value'] ) )
     2171                        ) {
     2172                                $truncate_by_byte_length = true;
     2173                                $needs_validation = false;
     2174                        }
     2175
     2176                        if ( $truncate_by_byte_length ) {
     2177                                mbstring_binary_safe_encoding();
     2178                                if ( false !== $length && strlen( $value['value'] ) > $length ) {
     2179                                        $value['value'] = substr( $value['value'], 0, $length );
     2180                                }
     2181                                reset_mbstring_encoding();
     2182
     2183                                if ( ! $needs_validation ) {
    21662184                                        continue;
    21672185                                }
     
    21692187
    21702188                        // utf8 can be handled by regex, which is a bunch faster than a DB lookup.
    2171                         if ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) {
     2189                        if ( ( 'utf8' === $charset || 'utf8mb3' === $charset || 'utf8mb4' === $charset ) && function_exists( 'mb_strlen' ) ) {
    21722190                                $regex = '/
    21732191                                        (
     
    21792197                                                |   [\xEE-\xEF][\x80-\xBF]{2}';
    21802198
    2181                                 if ( 'utf8mb4' === $charset) {
     2199                                if ( 'utf8mb4' === $charset ) {
    21822200                                        $regex .= '
    21832201                                                |    \xF0[\x90-\xBF][\x80-\xBF]{2} # four-byte sequences   11110xxx 10xxxxxx * 3
     
    21922210                                        /x';
    21932211                                $value['value'] = preg_replace( $regex, '$1', $value['value'] );
     2212
     2213
     2214                                if ( false !== $length && mb_strlen( $value['value'], 'UTF-8' ) > $length ) {
     2215                                        $value['value'] = mb_substr( $value['value'], 0, $length, 'UTF-8' );
     2216                                }
    21942217                                continue;
    21952218                        }
     
    22082231                                        }
    22092232
    2210                                         // Split the CONVERT() calls by charset, so we can make sure the connection is right
    2211                                         $queries[ $value['charset'] ][ $col ] = $this->prepare( "CONVERT( %s USING {$value['charset']} )", $value['value'] );
     2233                                        // We're going to need to truncate by characters or bytes, depending on the length value we have.
     2234                                        if ( 'byte' === $value['length']['type'] ) {
     2235                                                // Split the CONVERT() calls by charset, so we can make sure the connection is right
     2236                                                $queries[ $value['charset'] ][ $col ] = $this->prepare( "CONVERT( LEFT( CONVERT( %s USING binary ), %d ) USING {$value['charset']} )", $value['value'], $value['length']['length'] );
     2237                                        } else {
     2238                                                $queries[ $value['charset'] ][ $col ] = $this->prepare( "LEFT( CONVERT( %s USING {$value['charset']} ), %d )", $value['value'], $value['length']['length'] );
     2239                                        }
     2240
    22122241                                        unset( $data[ $col ]['db'] );
    22132242                                }
     
    22282257                                $this->check_current_query = false;
    22292258
    2230                                 $row = $this->get_row( "SELECT " . implode( ', ', $query ), ARRAY_N );
     2259                                $sql = array();
     2260                                foreach ( $query as $column => $column_query ) {
     2261                                        $sql[] = $column_query . " AS x_$column";
     2262                                }
     2263
     2264                                $row = $this->get_row( "SELECT " . implode( ', ', $sql ), ARRAY_A );
    22312265                                if ( ! $row ) {
    22322266                                        $this->set_charset( $this->dbh, $connection_charset );
     
    22342268                                }
    22352269
    2236                                 $cols = array_keys( $query );
    2237                                 $col_count = count( $cols );
    2238                                 for ( $ii = 0; $ii < $col_count; $ii++ ) {
    2239                                         $data[ $cols[ $ii ] ]['value'] = $row[ $ii ];
     2270                                foreach ( array_keys( $query ) as $column ) {
     2271                                        $data[ $column ]['value'] = $row["x_$column"];
    22402272                                }
    22412273                        }
     
    22792311                        'charset' => $charset,
    22802312                        'ascii'   => false,
     2313                        'length'  => false,
    22812314                );
    22822315
     
    23012334         */
    23022335        public function strip_invalid_text_for_column( $table, $column, $value ) {
    2303                 if ( ! is_string( $value ) || $this->check_ascii( $value ) ) {
     2336                if ( ! is_string( $value ) ) {
    23042337                        return $value;
    23052338                }
     
    23182351                                'value'   => $value,
    23192352                                'charset' => $charset,
    2320                                 'ascii'   => false,
     2353                                'length'  => $this->get_col_length( $table, $column ),
    23212354                        )
    23222355                );
  • branches/3.7/tests/phpunit/tests/comment.php

    r32318 r32391  
    2424                }
    2525
    26                 $post_id = $this->factory->post->create();
     26                $u = $this->factory->user->create();
     27                $post_id = $this->factory->post->create( array( 'post_author' => $u ) );
    2728
    2829                $data = array(
     
    3738                );
    3839
     40                add_filter( 'pre_option_moderation_notify', '__return_zero' );
    3941                $id = wp_new_comment( $data );
     42                remove_filter( 'pre_option_moderation_notify', '__return_zero' );
    4043
    41                 $this->assertFalse( $id );
     44                $this->assertEmpty( $id );
    4245
    4346                // Cleanup.
  • branches/3.7/tests/phpunit/tests/compat.php

    r25002 r32391  
    33/**
    44 * @group compat
     5 * @group security-153
    56 */
    67class Tests_Compat extends WP_UnitTestCase {
    7         function test_mb_substr() {
    8                 $this->assertEquals('баб', _mb_substr('баба', 0, 3));
    9                 $this->assertEquals('баб', _mb_substr('баба', 0, -1));
    10                 $this->assertEquals('баб', _mb_substr('баба', 0, -1));
    11                 $this->assertEquals('I am your б', _mb_substr('I am your баба', 0, 11));
     8        function utf8_string_lengths() {
     9                return array(
     10                        //                     string, character_length, byte_length
     11                        array(                 'баба',                4,           8 ),
     12                        array(                  'баб',                3,           6 ),
     13                        array(          'I am your б',               11,          12 ),
     14                        array(           '1111111111',               10,          10 ),
     15                        array(           '²²²²²²²²²²',               10,          20 ),
     16                        array( '3333333333',               10,          30 ),
     17                        array(           '𝟜𝟜𝟜𝟜𝟜𝟜𝟜𝟜𝟜𝟜',               10,          40 ),
     18                        array(      '1²3𝟜1²3𝟜1²3𝟜',               12,          30 ),
     19                );
     20        }
     21
     22        function utf8_substrings() {
     23                return array(
     24                        //               string, start, length, character_substring,   byte_substring
     25                        array(           'баба',     0,      3,               'баб',          "б\xD0" ),
     26                        array(           'баба',     0,     -1,               'баб',        "баб\xD0" ),
     27                        array(           'баба',     1,   null,               'аба',        "\xB1аба" ),
     28                        array(           'баба',    -3,   null,               'аба',          "\xB1а" ),
     29                        array(           'баба',    -3,      2,                'аб',       "\xB1\xD0" ),
     30                        array(           'баба',    -1,      2,                 'а',           "\xB0" ),
     31                        array( 'I am your баба',     0,     11,       'I am your б', "I am your \xD0" ),
     32                );
     33        }
     34
     35        /**
     36         * @dataProvider utf8_string_lengths
     37         */
     38        function test_mb_strlen( $string, $expected_character_length ) {
     39                $this->assertEquals( $expected_character_length, _mb_strlen( $string, 'UTF-8' ) );
     40        }
     41
     42        /**
     43         * @dataProvider utf8_string_lengths
     44         */
     45        function test_mb_strlen_via_regex( $string, $expected_character_length ) {
     46                _wp_can_use_pcre_u( false );
     47                $this->assertEquals( $expected_character_length, _mb_strlen( $string, 'UTF-8' ) );
     48                _wp_can_use_pcre_u( 'reset' );
     49        }
     50
     51        /**
     52         * @dataProvider utf8_string_lengths
     53         */
     54        function test_8bit_mb_strlen( $string, $expected_character_length, $expected_byte_length ) {
     55                $this->assertEquals( $expected_byte_length, _mb_strlen( $string, '8bit' ) );
     56        }
     57
     58        /**
     59         * @dataProvider utf8_substrings
     60         */
     61        function test_mb_substr( $string, $start, $length, $expected_character_substring ) {
     62                $this->assertEquals( $expected_character_substring, _mb_substr( $string, $start, $length, 'UTF-8' ) );
     63        }
     64
     65        /**
     66         * @dataProvider utf8_substrings
     67         */
     68        function test_mb_substr_via_regex( $string, $start, $length, $expected_character_substring ) {
     69                _wp_can_use_pcre_u( false );
     70                $this->assertEquals( $expected_character_substring, _mb_substr( $string, $start, $length, 'UTF-8' ) );
     71                _wp_can_use_pcre_u( 'reset' );
     72        }
     73
     74        /**
     75         * @dataProvider utf8_substrings
     76         */
     77        function test_8bit_mb_substr( $string, $start, $length, $expected_character_substring, $expected_byte_substring ) {
     78                $this->assertEquals( $expected_byte_substring, _mb_substr( $string, $start, $length, '8bit' ) );
     79        }
     80
     81        function test_mb_substr_phpcore(){
     82                /* https://github.com/php/php-src/blob/php-5.6.8/ext/mbstring/tests/mb_substr_basic.phpt */
     83                $string_ascii = 'ABCDEF';
     84                $string_mb = base64_decode('5pel5pys6Kqe44OG44Kt44K544OI44Gn44GZ44CCMDEyMzTvvJXvvJbvvJfvvJjvvJnjgII=');
     85
     86                $this->assertEquals( 'DEF', _mb_substr($string_ascii, 3) );
     87                $this->assertEquals( 'DEF', _mb_substr($string_ascii, 3, 5, 'ISO-8859-1') );
     88
     89                // specific latin-1 as that is the default the core php test opporates under   
     90                $this->assertEquals( 'peacrOiqng==' , base64_encode( _mb_substr($string_mb, 2, 7, 'latin-1' ) ) );
     91                $this->assertEquals( '6Kqe44OG44Kt44K544OI44Gn44GZ', base64_encode( _mb_substr($string_mb, 2, 7, 'utf-8') ) );
     92
     93                /* https://github.com/php/php-src/blob/php-5.6.8/ext/mbstring/tests/mb_substr_variation1.phpt */
     94                $start = 0;
     95                $length = 5;
     96                $unset_var = 10;
     97                unset ($unset_var);
     98                $heredoc = <<<EOT
     99hello world
     100EOT;
     101                $inputs = array(
     102                /*1*/  0,
     103                           1,
     104                           12345,
     105                           -2345,
     106                           // float data
     107                /*5*/  10.5,
     108                           -10.5,
     109                           12.3456789000e10,
     110                           12.3456789000E-10,
     111                           .5,
     112                           // null data
     113                /*10*/ NULL,
     114                           null,
     115                           // boolean data
     116                /*12*/ true,
     117                           false,
     118                           TRUE,
     119                           FALSE,
     120                           // empty data
     121                /*16*/ "",
     122                           '',
     123                           // string data
     124                /*18*/ "string",
     125                           'string',
     126                           $heredoc,
     127                           // object data
     128                /*21*/ new classA(),
     129                           // undefined data
     130                /*22*/ @$undefined_var,
     131                           // unset data
     132                /*23*/ @$unset_var,
     133                );
     134                $outputs = array(
     135                        "0",
     136                        "1",
     137                        "12345",
     138                        "-2345",
     139                        "10.5",
     140                        "-10.5",
     141                        "12345",
     142                        "1.234",
     143                        "0.5",
     144                        "",
     145                        "",
     146                        "1",
     147                        "",
     148                        "1",
     149                        "",
     150                        "",
     151                        "",
     152                        "strin",
     153                        "strin",
     154                        "hello",
     155                        "Class",
     156                        "",
     157                        "",
     158                );
     159                $iterator = 0;
     160                foreach($inputs as $input) {
     161                        $this->assertEquals( $outputs[$iterator] ,  _mb_substr($input, $start, $length) );
     162                        $iterator++;
     163                }
     164
    12165        }
    13166
     
    35188        }
    36189}
     190
     191/* used in test_mb_substr_phpcore */
     192class classA {
     193        public function __toString() {
     194                return "Class A object";
     195        }
     196}
  • branches/3.7/tests/phpunit/tests/db.php

    r32318 r32391  
    511511                                'format' => '%s',
    512512                                'charset' => $expected_charset,
    513                                 'ascii' => false,
    514513                                'length' => $wpdb->get_col_length( $wpdb->posts, 'post_content' ),
    515514                        )
  • branches/3.7/tests/phpunit/tests/db/charset.php

    r32275 r32391  
    77 *
    88 * @group wpdb
     9 * @group security-153
    910 */
    1011class Tests_DB_Charset extends WP_UnitTestCase {
     
    2930                                'charset'  => 'latin1',
    3031                                'value'    => "\xf0\x9f\x8e\xb7",
    31                                 'expected' => "\xf0\x9f\x8e\xb7"
     32                                'expected' => "\xf0\x9f\x8e\xb7",
     33                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     34                        ),
     35                        'latin1_char_length' => array(
     36                                // latin1. latin1 never changes.
     37                                'charset'  => 'latin1',
     38                                'value'    => str_repeat( 'A', 11 ),
     39                                'expected' => str_repeat( 'A', 10 ),
     40                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     41                        ),
     42                        'latin1_byte_length' => array(
     43                                // latin1. latin1 never changes.
     44                                'charset'  => 'latin1',
     45                                'value'    => str_repeat( 'A', 11 ),
     46                                'expected' => str_repeat( 'A', 10 ),
     47                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    3248                        ),
    3349                        'ascii' => array(
     
    3551                                'charset'  => 'ascii',
    3652                                'value'    => 'Hello World',
    37                                 'expected' => 'Hello World'
     53                                'expected' => 'Hello World',
     54                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     55                        ),
     56                        'ascii_char_length' => array(
     57                                // ascii gets special treatment, make sure it's covered
     58                                'charset'  => 'ascii',
     59                                'value'    => str_repeat( 'A', 11 ),
     60                                'expected' => str_repeat( 'A', 10 ),
     61                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     62                        ),
     63                        'ascii_byte_length' => array(
     64                                // ascii gets special treatment, make sure it's covered
     65                                'charset'  => 'ascii',
     66                                'value'    => str_repeat( 'A', 11 ),
     67                                'expected' => str_repeat( 'A', 10 ),
     68                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    3869                        ),
    3970                        'utf8' => array(
     
    4172                                'charset'  => 'utf8',
    4273                                'value'    => "H€llo\xf0\x9f\x98\x88World¢",
    43                                 'expected' => 'H€lloWorld¢'
     74                                'expected' => 'H€lloWorld¢',
     75                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     76                        ),
     77                        'utf8_23char_length' => array(
     78                                // utf8 only allows <= 3-byte chars
     79                                'charset'  => 'utf8',
     80                                'value'    => str_repeat( "²3", 10 ),
     81                                'expected' => str_repeat( "²3", 5 ),
     82                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     83                        ),
     84                        'utf8_23byte_length' => array(
     85                                // utf8 only allows <= 3-byte chars
     86                                'charset'  => 'utf8',
     87                                'value'    => str_repeat( "²3", 10 ),
     88                                'expected' => "²3²3",
     89                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
     90                        ),
     91                        'utf8_3char_length' => array(
     92                                // utf8 only allows <= 3-byte chars
     93                                'charset'  => 'utf8',
     94                                'value'    => str_repeat( "3", 11 ),
     95                                'expected' => str_repeat( "3", 10 ),
     96                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     97                        ),
     98                        'utf8_3byte_length' => array(
     99                                // utf8 only allows <= 3-byte chars
     100                                'charset'  => 'utf8',
     101                                'value'    => str_repeat( "3", 11 ),
     102                                'expected' => "333",
     103                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    44104                        ),
    45105                        'utf8mb3' => array(
     
    47107                                'charset'  => 'utf8mb3',
    48108                                'value'    => "H€llo\xf0\x9f\x98\x88World¢",
    49                                 'expected' => 'H€lloWorld¢'
     109                                'expected' => 'H€lloWorld¢',
     110                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     111                        ),
     112                        'utf8mb3_23char_length' => array(
     113                                // utf8mb3 should behave the same an utf8
     114                                'charset'  => 'utf8mb3',
     115                                'value'    => str_repeat( "²3", 10 ),
     116                                'expected' => str_repeat( "²3", 5 ),
     117                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     118                        ),
     119                        'utf8mb3_23byte_length' => array(
     120                                // utf8mb3 should behave the same an utf8
     121                                'charset'  => 'utf8mb3',
     122                                'value'    => str_repeat( "²3", 10 ),
     123                                'expected' => "²3²3",
     124                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
     125                        ),
     126                        'utf8mb3_3char_length' => array(
     127                                // utf8mb3 should behave the same an utf8
     128                                'charset'  => 'utf8mb3',
     129                                'value'    => str_repeat( "3", 11 ),
     130                                'expected' => str_repeat( "3", 10 ),
     131                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     132                        ),
     133                        'utf8mb3_3byte_length' => array(
     134                                // utf8mb3 should behave the same an utf8
     135                                'charset'  => 'utf8mb3',
     136                                'value'    => str_repeat( "3", 10 ),
     137                                'expected' => "333",
     138                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    50139                        ),
    51140                        'utf8mb4' => array(
     
    53142                                'charset'  => 'utf8mb4',
    54143                                'value'    => "H€llo\xf0\x9f\x98\x88World¢",
    55                                 'expected' => "H€llo\xf0\x9f\x98\x88World¢"
     144                                'expected' => "H€llo\xf0\x9f\x98\x88World¢",
     145                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     146                        ),
     147                        'utf8mb4_234char_length' => array(
     148                                // utf8mb4 allows 4-byte characters, too
     149                                'charset'  => 'utf8mb4',
     150                                'value'    => str_repeat( "²3𝟜", 10 ),
     151                                'expected' => "²3𝟜²3𝟜²3𝟜²",
     152                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     153                        ),
     154                        'utf8mb4_234byte_length' => array(
     155                                // utf8mb4 allows 4-byte characters, too
     156                                'charset'  => 'utf8mb4',
     157                                'value'    => str_repeat( "²3𝟜", 10 ),
     158                                'expected' => "²3𝟜",
     159                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
     160                        ),
     161                        'utf8mb4_4char_length' => array(
     162                                // utf8mb4 allows 4-byte characters, too
     163                                'charset'  => 'utf8mb4',
     164                                'value'    => str_repeat( "𝟜", 11 ),
     165                                'expected' => str_repeat( "𝟜", 10 ),
     166                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     167                        ),
     168                        'utf8mb4_4byte_length' => array(
     169                                // utf8mb4 allows 4-byte characters, too
     170                                'charset'  => 'utf8mb4',
     171                                'value'    => str_repeat( "𝟜", 10 ),
     172                                'expected' => "𝟜𝟜",
     173                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    56174                        ),
    57175                        'koi8r' => array(
     
    59177                                'value'    => "\xfdord\xf2ress",
    60178                                'expected' => "\xfdord\xf2ress",
     179                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     180                        ),
     181                        'koi8r_char_length' => array(
     182                                'charset'  => 'koi8r',
     183                                'value'    => str_repeat( "\xfd\xf2", 10 ),
     184                                'expected' => str_repeat( "\xfd\xf2", 5 ),
     185                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     186                        ),
     187                        'koi8r_byte_length' => array(
     188                                'charset'  => 'koi8r',
     189                                'value'    => str_repeat( "\xfd\xf2", 10 ),
     190                                'expected' => str_repeat( "\xfd\xf2", 5 ),
     191                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    61192                        ),
    62193                        'hebrew' => array(
     
    64195                                'value'    => "\xf9ord\xf7ress",
    65196                                'expected' => "\xf9ord\xf7ress",
     197                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     198                        ),
     199                        'hebrew_char_length' => array(
     200                                'charset'  => 'hebrew',
     201                                'value'    => str_repeat( "\xf9\xf7", 10 ),
     202                                'expected' => str_repeat( "\xf9\xf7", 5 ),
     203                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     204                        ),
     205                        'hebrew_byte_length' => array(
     206                                'charset'  => 'hebrew',
     207                                'value'    => str_repeat( "\xf9\xf7", 10 ),
     208                                'expected' => str_repeat( "\xf9\xf7", 5 ),
     209                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    66210                        ),
    67211                        'cp1251' => array(
     
    69213                                'value'    => "\xd8ord\xd0ress",
    70214                                'expected' => "\xd8ord\xd0ress",
     215                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     216                        ),
     217                        'cp1251_char_length' => array(
     218                                'charset'  => 'cp1251',
     219                                'value'    => str_repeat( "\xd8\xd0", 10 ),
     220                                'expected' => str_repeat( "\xd8\xd0", 5 ),
     221                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     222                        ),
     223                        'cp1251_byte_length' => array(
     224                                'charset'  => 'cp1251',
     225                                'value'    => str_repeat( "\xd8\xd0", 10 ),
     226                                'expected' => str_repeat( "\xd8\xd0", 5 ),
     227                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    71228                        ),
    72229                        'tis620' => array(
     
    74231                                'value'    => "\xccord\xe3ress",
    75232                                'expected' => "\xccord\xe3ress",
     233                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     234                        ),
     235                        'tis620_char_length' => array(
     236                                'charset'  => 'tis620',
     237                                'value'    => str_repeat( "\xcc\xe3", 10 ),
     238                                'expected' => str_repeat( "\xcc\xe3", 5 ),
     239                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     240                        ),
     241                        'tis620_byte_length' => array(
     242                                'charset'  => 'tis620',
     243                                'value'    => str_repeat( "\xcc\xe3", 10 ),
     244                                'expected' => str_repeat( "\xcc\xe3", 5 ),
     245                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    76246                        ),
    77247                        'false' => array(
     
    79249                                'charset'  => false,
    80250                                'value'    => 100,
    81                                 'expected' => 100
     251                                'expected' => 100,
     252                                'length'   => false,
    82253                        ),
    83254                );
     
    95266                                'charset'  => 'big5',
    96267                                'value'    => $big5,
    97                                 'expected' => $big5
     268                                'expected' => $big5,
     269                                'length'   => array( 'type' => 'char', 'length' => 100 ),
     270                        );
     271
     272                        $fields['big5_char_length'] = array(
     273                                'charset'  => 'big5',
     274                                'value'    => str_repeat( $big5, 10 ),
     275                                'expected' => str_repeat( $big5, 3 ) . 'a',
     276                                'length'   => array( 'type' => 'char', 'length' => 10 ),
     277                        );
     278
     279                        $fields['big5_byte_length'] = array(
     280                                'charset'  => 'big5',
     281                                'value'    => str_repeat( $big5, 10 ),
     282                                'expected' => str_repeat( $big5, 2 ) . 'a',
     283                                'length'   => array( 'type' => 'byte', 'length' => 10 ),
    98284                        );
    99285                }
     
    167353
    168354                $all_ascii_fields = array(
    169                         'post_content' => array( 'value' => 'foo foo foo!', 'format' => '%s', 'charset' => false ),
    170                         'post_excerpt' => array( 'value' => 'bar bar bar!', 'format' => '%s', 'charset' => false ),
     355                        'post_content' => array( 'value' => 'foo foo foo!', 'format' => '%s', 'charset' => $charset ),
     356                        'post_excerpt' => array( 'value' => 'bar bar bar!', 'format' => '%s', 'charset' => $charset ),
    171357                );
    172358
    173359                // This is the same data used in process_field_charsets_for_nonexistent_table()
    174360                $non_ascii_string_fields = array(
    175                         'post_content' => array( 'value' => '¡foo foo foo!', 'format' => '%s', 'charset' => $charset, 'ascii' => false ),
    176                         'post_excerpt' => array( 'value' => '¡bar bar bar!', 'format' => '%s', 'charset' => $charset, 'ascii' => false ),
     361                        'post_content' => array( 'value' => '¡foo foo foo!', 'format' => '%s', 'charset' => $charset ),
     362                        'post_excerpt' => array( 'value' => '¡bar bar bar!', 'format' => '%s', 'charset' => $charset ),
    177363                );
    178364
     
    541727                self::$_wpdb->query( $drop );
    542728        }
     729
     730        function test_strip_invalid_test_for_column_bails_if_ascii_input_too_long() {
     731                global $wpdb;
     732
     733                // TEXT column
     734                $stripped = $wpdb->strip_invalid_text_for_column( $wpdb->comments, 'comment_content', str_repeat( 'A', 65536 ) );
     735                $this->assertEquals( 65535, strlen( $stripped ) );
     736
     737                // VARCHAR column
     738                $stripped = $wpdb->strip_invalid_text_for_column( $wpdb->comments, 'comment_agent', str_repeat( 'A', 256 ) );
     739                $this->assertEquals( 255, strlen( $stripped ) );
     740        }
    543741}
Note: See TracChangeset for help on using the changeset viewer.