Make WordPress Core

Changeset 32307


Ignore:
Timestamp:
04/27/2015 02:47:22 PM (9 years ago)
Author:
pento
Message:

WPDB: Sanity check that any strings being stored in the DB are not too long to store correctly.

Merge [32299] to the 4.1 and 4.2 branches.

Location:
branches
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • branches/4.1/src/wp-includes/wp-db.php

    r32262 r32307  
    19271927    protected function process_fields( $table, $data, $format ) {
    19281928        $data = $this->process_field_formats( $data, $format );
     1929        if ( false === $data ) {
     1930            return false;
     1931        }
     1932
    19291933        $data = $this->process_field_charsets( $data, $table );
     1934        if ( false === $data ) {
     1935            return false;
     1936        }
     1937
     1938        $data = $this->process_field_lengths( $data, $table );
    19301939        if ( false === $data ) {
    19311940            return false;
     
    20042013                // This isn't ASCII. Don't have strip_invalid_text() re-check.
    20052014                $value['ascii'] = false;
     2015            }
     2016
     2017            $data[ $field ] = $value;
     2018        }
     2019
     2020        return $data;
     2021    }
     2022
     2023    /**
     2024     * For string fields, record the maximum string length that field can safely save.
     2025     *
     2026     * @since 4.2.1
     2027     * @access protected
     2028     *
     2029     * @param array  $data  As it comes from the wpdb::process_field_charsets() method.
     2030     * @param string $table Table name.
     2031     * @return array|False The same array as $data with additional 'length' keys, or false if
     2032     *                     any of the values were too long for their corresponding field.
     2033     */
     2034    protected function process_field_lengths( $data, $table ) {
     2035        foreach ( $data as $field => $value ) {
     2036            if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
     2037                // We can skip this field if we know it isn't a string.
     2038                // This checks %d/%f versus ! %s because it's sprintf() could take more.
     2039                $value['length'] = false;
     2040            } else {
     2041                $value['length'] = $this->get_col_length( $table, $field );
     2042                if ( is_wp_error( $value['length'] ) ) {
     2043                    return false;
     2044                }
     2045            }
     2046
     2047            if ( false !== $value['length'] && strlen( $value['value'] ) > $value['length'] ) {
     2048                return false;
    20062049            }
    20072050
     
    23372380
    23382381    /**
     2382     * Retrieve the maximum string length allowed in a given column.
     2383     *
     2384     * @since 4.2.1
     2385     * @access public
     2386     *
     2387     * @param string $table  Table name.
     2388     * @param string $column Column name.
     2389     * @return mixed Max column length as an int. False if the column has no
     2390     *               length. WP_Error object if there was an error.
     2391     */
     2392    public function get_col_length( $table, $column ) {
     2393        $tablekey = strtolower( $table );
     2394        $columnkey = strtolower( $column );
     2395
     2396        // Skip this entirely if this isn't a MySQL database.
     2397        if ( false === $this->is_mysql ) {
     2398            return false;
     2399        }
     2400
     2401        if ( empty( $this->col_meta[ $tablekey ] ) ) {
     2402            // This primes column information for us.
     2403            $table_charset = $this->get_table_charset( $table );
     2404            if ( is_wp_error( $table_charset ) ) {
     2405                return $table_charset;
     2406            }
     2407        }
     2408
     2409        if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {
     2410            return false;
     2411        }
     2412
     2413        $typeinfo = explode( '(', $this->col_meta[ $tablekey ][ $columnkey ]->Type );
     2414
     2415        $type = strtolower( $typeinfo[0] );
     2416        if ( ! empty( $typeinfo[1] ) ) {
     2417            $length = trim( $typeinfo[1], ')' );
     2418        } else {
     2419            $length = false;
     2420        }
     2421
     2422        switch( $type ) {
     2423            case 'binary':
     2424            case 'char':
     2425            case 'varbinary':
     2426            case 'varchar':
     2427                return $length;
     2428                break;
     2429            case 'tinyblob':
     2430            case 'tinytext':
     2431                return 255; // 2^8 - 1
     2432                break;
     2433            case 'blob':
     2434            case 'text':
     2435                return 65535; // 2^16 - 1
     2436                break;
     2437            case 'mediumblob':
     2438            case 'mediumtext':
     2439                return 16777215; // 2^24 - 1
     2440                break;
     2441            case 'longblob':
     2442            case 'longtext':
     2443                return 4294967295; // 2^32 - 1
     2444                break;
     2445            default:
     2446                return false;
     2447        }
     2448
     2449        return false;
     2450    }
     2451
     2452    /**
    23392453     * Check if a string is ASCII.
    23402454     *
  • branches/4.1/tests/phpunit/tests/comment.php

    r30402 r32307  
    4343        $this->assertSame( array(), $found );
    4444    }
     45
     46    public function test_comment_content_length() {
     47        // `wp_new_comment()` checks REMOTE_ADDR, so we fake it to avoid PHP notices.
     48        if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
     49            $remote_addr = $_SERVER['REMOTE_ADDR'];
     50        } else {
     51            $_SERVER['REMOTE_ADDR'] = '';
     52        }
     53
     54        $post_id = $this->factory->post->create();
     55
     56        $data = array(
     57            'comment_post_ID' => $post_id,
     58            'comment_author' => rand_str(),
     59            'comment_author_url' => '',
     60            'comment_author_email' => '',
     61            'comment_type' => '',
     62            'comment_content' => str_repeat( 'A', 65536 ),
     63            'comment_date' => '2011-01-01 10:00:00',
     64            'comment_date_gmt' => '2011-01-01 10:00:00',
     65        );
     66
     67        $id = wp_new_comment( $data );
     68
     69        $this->assertFalse( $id );
     70
     71        // Cleanup.
     72        if ( isset( $remote_addr ) ) {
     73            $_SERVER['REMOTE_ADDR'] = $remote_addr;
     74        } else {
     75            unset( $_SERVER['REMOTE_ADDR'] );
     76        }
     77    }
    4578}
  • branches/4.1/tests/phpunit/tests/db.php

    r32163 r32307  
    748748                'charset' => $expected_charset,
    749749                'ascii' => false,
     750                'length' => $wpdb->get_col_length( $wpdb->posts, 'post_content' ),
    750751            )
    751752        );
  • branches/4.2/src/wp-includes/wp-db.php

    r32261 r32307  
    19471947    protected function process_fields( $table, $data, $format ) {
    19481948        $data = $this->process_field_formats( $data, $format );
     1949        if ( false === $data ) {
     1950            return false;
     1951        }
     1952
    19491953        $data = $this->process_field_charsets( $data, $table );
     1954        if ( false === $data ) {
     1955            return false;
     1956        }
     1957
     1958        $data = $this->process_field_lengths( $data, $table );
    19501959        if ( false === $data ) {
    19511960            return false;
     
    20242033                // This isn't ASCII. Don't have strip_invalid_text() re-check.
    20252034                $value['ascii'] = false;
     2035            }
     2036
     2037            $data[ $field ] = $value;
     2038        }
     2039
     2040        return $data;
     2041    }
     2042
     2043    /**
     2044     * For string fields, record the maximum string length that field can safely save.
     2045     *
     2046     * @since 4.2.1
     2047     * @access protected
     2048     *
     2049     * @param array  $data  As it comes from the wpdb::process_field_charsets() method.
     2050     * @param string $table Table name.
     2051     * @return array|False The same array as $data with additional 'length' keys, or false if
     2052     *                     any of the values were too long for their corresponding field.
     2053     */
     2054    protected function process_field_lengths( $data, $table ) {
     2055        foreach ( $data as $field => $value ) {
     2056            if ( '%d' === $value['format'] || '%f' === $value['format'] ) {
     2057                // We can skip this field if we know it isn't a string.
     2058                // This checks %d/%f versus ! %s because it's sprintf() could take more.
     2059                $value['length'] = false;
     2060            } else {
     2061                $value['length'] = $this->get_col_length( $table, $field );
     2062                if ( is_wp_error( $value['length'] ) ) {
     2063                    return false;
     2064                }
     2065            }
     2066
     2067            if ( false !== $value['length'] && mb_strlen( $value['value'] ) > $value['length'] ) {
     2068                return false;
    20262069            }
    20272070
     
    23632406
    23642407    /**
     2408     * Retrieve the maximum string length allowed in a given column.
     2409     *
     2410     * @since 4.2.1
     2411     * @access public
     2412     *
     2413     * @param string $table  Table name.
     2414     * @param string $column Column name.
     2415     * @return mixed Max column length as an int. False if the column has no
     2416     *               length. WP_Error object if there was an error.
     2417     */
     2418    public function get_col_length( $table, $column ) {
     2419        $tablekey = strtolower( $table );
     2420        $columnkey = strtolower( $column );
     2421
     2422        // Skip this entirely if this isn't a MySQL database.
     2423        if ( false === $this->is_mysql ) {
     2424            return false;
     2425        }
     2426
     2427        if ( empty( $this->col_meta[ $tablekey ] ) ) {
     2428            // This primes column information for us.
     2429            $table_charset = $this->get_table_charset( $table );
     2430            if ( is_wp_error( $table_charset ) ) {
     2431                return $table_charset;
     2432            }
     2433        }
     2434
     2435        if ( empty( $this->col_meta[ $tablekey ][ $columnkey ] ) ) {
     2436            return false;
     2437        }
     2438
     2439        $typeinfo = explode( '(', $this->col_meta[ $tablekey ][ $columnkey ]->Type );
     2440
     2441        $type = strtolower( $typeinfo[0] );
     2442        if ( ! empty( $typeinfo[1] ) ) {
     2443            $length = trim( $typeinfo[1], ')' );
     2444        } else {
     2445            $length = false;
     2446        }
     2447
     2448        switch( $type ) {
     2449            case 'binary':
     2450            case 'char':
     2451            case 'varbinary':
     2452            case 'varchar':
     2453                return $length;
     2454                break;
     2455            case 'tinyblob':
     2456            case 'tinytext':
     2457                return 255; // 2^8 - 1
     2458                break;
     2459            case 'blob':
     2460            case 'text':
     2461                return 65535; // 2^16 - 1
     2462                break;
     2463            case 'mediumblob':
     2464            case 'mediumtext':
     2465                return 16777215; // 2^24 - 1
     2466                break;
     2467            case 'longblob':
     2468            case 'longtext':
     2469                return 4294967295; // 2^32 - 1
     2470                break;
     2471            default:
     2472                return false;
     2473        }
     2474
     2475        return false;
     2476    }
     2477
     2478    /**
    23652479     * Check if a string is ASCII.
    23662480     *
  • branches/4.2/tests/phpunit/tests/comment.php

    r31615 r32307  
    113113        }
    114114    }
     115
     116    public function test_comment_content_length() {
     117        // `wp_new_comment()` checks REMOTE_ADDR, so we fake it to avoid PHP notices.
     118        if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
     119            $remote_addr = $_SERVER['REMOTE_ADDR'];
     120        } else {
     121            $_SERVER['REMOTE_ADDR'] = '';
     122        }
     123
     124        $post_id = $this->factory->post->create();
     125
     126        $data = array(
     127            'comment_post_ID' => $post_id,
     128            'comment_author' => rand_str(),
     129            'comment_author_url' => '',
     130            'comment_author_email' => '',
     131            'comment_type' => '',
     132            'comment_content' => str_repeat( 'A', 65536 ),
     133            'comment_date' => '2011-01-01 10:00:00',
     134            'comment_date_gmt' => '2011-01-01 10:00:00',
     135        );
     136
     137        $id = wp_new_comment( $data );
     138
     139        $this->assertFalse( $id );
     140
     141        // Cleanup.
     142        if ( isset( $remote_addr ) ) {
     143            $_SERVER['REMOTE_ADDR'] = $remote_addr;
     144        } else {
     145            unset( $_SERVER['REMOTE_ADDR'] );
     146        }
     147    }
    115148}
  • branches/4.2/tests/phpunit/tests/db.php

    r31372 r32307  
    748748                'charset' => $expected_charset,
    749749                'ascii' => false,
     750                'length' => $wpdb->get_col_length( $wpdb->posts, 'post_content' ),
    750751            )
    751752        );
Note: See TracChangeset for help on using the changeset viewer.