Changeset 47650
- Timestamp:
- 04/29/2020 04:22:22 PM (4 years ago)
- Location:
- branches/4.7
- Files:
-
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/4.7
- Property svn:mergeinfo changed
/trunk merged: 47633-47635,47637-47638
- Property svn:mergeinfo changed
-
branches/4.7/src/wp-includes/cache.php
r39051 r47650 692 692 echo '<ul>'; 693 693 foreach ($this->cache as $group => $cache) { 694 echo "<li><strong>Group:</strong> $group - ( ". number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )</li>';694 echo '<li><strong>Group:</strong> ' . esc_html( $group ) . ' - ( ' . number_format( strlen( serialize( $cache ) ) / KB_IN_BYTES, 2 ) . 'k )</li>'; 695 695 } 696 696 echo '</ul>'; -
branches/4.7/src/wp-includes/class-wp-customize-manager.php
r41430 r47650 2526 2526 add_filter( 'wp_save_post_revision_post_has_changed', array( $this, '_filter_revision_post_has_changed' ), 5, 3 ); 2527 2527 2528 // Update the changeset post. The publish_customize_changeset action will cause the settings in the changeset to be saved via WP_Customize_Setting::save(). 2529 $has_kses = ( false !== has_filter( 'content_save_pre', 'wp_filter_post_kses' ) ); 2530 if ( $has_kses ) { 2531 kses_remove_filters(); // Prevent KSES from corrupting JSON in post_content. 2532 } 2533 2534 // Note that updating a post with publish status will trigger WP_Customize_Manager::publish_changeset_values(). 2528 /* 2529 * Update the changeset post. The publish_customize_changeset action will cause the settings in the 2530 * changeset to be saved via WP_Customize_Setting::save(). Updating a post with publish status will 2531 * trigger WP_Customize_Manager::publish_changeset_values(). 2532 */ 2533 add_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5, 3 ); 2535 2534 if ( $changeset_post_id ) { 2536 2535 $post_array['edit_date'] = true; // Prevent date clearing. … … 2542 2541 } 2543 2542 } 2544 if ( $has_kses ) { 2545 kses_init_filters();2546 } 2543 2544 remove_filter( 'wp_insert_post_data', array( $this, 'preserve_insert_changeset_post_content' ), 5 ); 2545 2547 2546 $this->_changeset_data = null; // Reset so WP_Customize_Manager::changeset_data() will re-populate with updated contents. 2548 2547 … … 2559 2558 2560 2559 return $response; 2560 } 2561 2562 /** 2563 * Preserve the initial JSON post_content passed to save into the post. 2564 * 2565 * This is needed to prevent KSES and other {@see 'content_save_pre'} filters 2566 * from corrupting JSON data. 2567 * 2568 * Note that WP_Customize_Manager::validate_setting_values() have already 2569 * run on the setting values being serialized as JSON into the post content 2570 * so it is pre-sanitized. 2571 * 2572 * Also, the sanitization logic is re-run through the respective 2573 * WP_Customize_Setting::sanitize() method when being read out of the 2574 * changeset, via WP_Customize_Manager::post_value(), and this sanitized 2575 * value will also be sent into WP_Customize_Setting::update() for 2576 * persisting to the DB. 2577 * 2578 * Multiple users can collaborate on a single changeset, where one user may 2579 * have the unfiltered_html capability but another may not. A user with 2580 * unfiltered_html may add a script tag to some field which needs to be kept 2581 * intact even when another user updates the changeset to modify another field 2582 * when they do not have unfiltered_html. 2583 * 2584 * @since 5.4.1 2585 * 2586 * @param array $data An array of slashed and processed post data. 2587 * @param array $postarr An array of sanitized (and slashed) but otherwise unmodified post data. 2588 * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as originally passed to wp_insert_post(). 2589 * @return array Filtered post data. 2590 */ 2591 public function preserve_insert_changeset_post_content( $data, $postarr, $unsanitized_postarr ) { 2592 if ( 2593 isset( $data['post_type'] ) && 2594 isset( $unsanitized_postarr['post_content'] ) && 2595 'customize_changeset' === $data['post_type'] || 2596 ( 2597 'revision' === $data['post_type'] && 2598 ! empty( $data['post_parent'] ) && 2599 'customize_changeset' === get_post_type( $data['post_parent'] ) 2600 ) 2601 ) { 2602 $data['post_content'] = $unsanitized_postarr['post_content']; 2603 } 2604 return $data; 2561 2605 } 2562 2606 -
branches/4.7/src/wp-includes/class-wp-query.php
r46495 r47650 807 807 } elseif ( $qv['p'] ) { 808 808 $this->is_single = true; 809 } elseif ( ('' !== $qv['hour']) && ('' !== $qv['minute']) &&('' !== $qv['second']) && ('' != $qv['year']) && ('' != $qv['monthnum']) && ('' != $qv['day']) ) {810 // If year, month, day, hour, minute, and second are set, a single811 // post is being queried.812 $this->is_single = true;813 809 } elseif ( '' != $qv['pagename'] || !empty($qv['page_id']) ) { 814 810 $this->is_page = true; -
branches/4.7/src/wp-includes/formatting.php
r45996 r47650 1762 1762 $filename_raw = $filename; 1763 1763 $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0)); 1764 1765 // Check for support for utf8 in the installed PCRE library once and store the result in a static. 1766 static $utf8_pcre = null; 1767 if ( ! isset( $utf8_pcre ) ) { 1768 // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged 1769 $utf8_pcre = @preg_match( '/^./u', 'a' ); 1770 } 1771 1772 if ( ! seems_utf8( $filename ) ) { 1773 $_ext = pathinfo( $filename, PATHINFO_EXTENSION ); 1774 $_name = pathinfo( $filename, PATHINFO_FILENAME ); 1775 $filename = sanitize_title_with_dashes( $_name ) . '.' . $_ext; 1776 } 1777 1778 if ( $utf8_pcre ) { 1779 $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename ); 1780 } 1781 1764 1782 /** 1765 1783 * Filters the list of characters to remove from a filename. … … 1771 1789 */ 1772 1790 $special_chars = apply_filters( 'sanitize_file_name_chars', $special_chars, $filename_raw ); 1773 $filename = preg_replace( "#\x{00a0}#siu", ' ', $filename );1774 1791 $filename = str_replace( $special_chars, '', $filename ); 1775 1792 $filename = str_replace( array( '%20', '+' ), '-', $filename ); -
branches/4.7/src/wp-includes/post.php
r43395 r47650 2969 2969 global $wpdb; 2970 2970 2971 // Capture original pre-sanitized array for passing into filters. 2972 $unsanitized_postarr = $postarr; 2973 2971 2974 $user_id = get_current_user_id(); 2972 2975 … … 3265 3268 * 3266 3269 * @since 3.9.0 3270 * @since 5.4.1 `$unsanitized_postarr` argument added. 3267 3271 * 3268 * @param array $data An array of sanitized attachment post data. 3269 * @param array $postarr An array of unsanitized attachment post data. 3272 * @param array $data An array of slashed, sanitized, and processed attachment post data. 3273 * @param array $postarr An array of slashed and sanitized attachment post data, but not processed. 3274 * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed attachment post data 3275 * as originally passed to wp_insert_post(). 3270 3276 */ 3271 $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr );3277 $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr, $unsanitized_postarr ); 3272 3278 } else { 3273 3279 /** … … 3275 3281 * 3276 3282 * @since 2.7.0 3283 * @since 5.4.1 `$unsanitized_postarr` argument added. 3277 3284 * 3278 * @param array $data An array of slashed post data. 3279 * @param array $postarr An array of sanitized, but otherwise unmodified post data. 3285 * @param array $data An array of slashed, sanitized, and processed post data. 3286 * @param array $postarr An array of sanitized (and slashed) but otherwise unmodified post data. 3287 * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as 3288 * originally passed to wp_insert_post(). 3280 3289 */ 3281 $data = apply_filters( 'wp_insert_post_data', $data, $postarr );3290 $data = apply_filters( 'wp_insert_post_data', $data, $postarr, $unsanitized_postarr ); 3282 3291 } 3283 3292 $data = wp_unslash( $data ); -
branches/4.7/src/wp-includes/user.php
r39326 r47650 1629 1629 1630 1630 if ( $update ) { 1631 if ( $user_email !== $old_user_data->user_email ) {1631 if ( $user_email !== $old_user_data->user_email || $user_pass !== $old_user_data->user_pass ) { 1632 1632 $data['user_activation_key'] = ''; 1633 1633 } -
branches/4.7/tests/phpunit/tests/customize/manager.php
r40338 r47650 887 887 888 888 /** 889 * Test saving changeset post without Kses or other content_save_pre filters mutating content. 890 * 891 * @covers WP_Customize_Manager::save_changeset_post() 892 */ 893 public function test_save_changeset_post_without_kses_corrupting_json() { 894 global $wp_customize; 895 $lesser_admin_user_id = self::factory()->user->create( array( 'role' => 'administrator' ) ); 896 897 $uuid = wp_generate_uuid4(); 898 $wp_customize = new WP_Customize_Manager( 899 array( 900 'changeset_uuid' => $uuid, 901 ) 902 ); 903 904 add_filter( 'map_meta_cap', array( $this, 'filter_map_meta_cap_to_disallow_unfiltered_html' ), 10, 2 ); 905 kses_init(); 906 add_filter( 'content_save_pre', 'capital_P_dangit' ); 907 add_post_type_support( 'customize_changeset', 'revisions' ); 908 909 $options = array( 910 'custom_html_1' => '<script>document.write(" Wordpress 1")</script>', 911 'custom_html_2' => '<script>document.write(" Wordpress 2")</script>', 912 'custom_html_3' => '<script>document.write(" Wordpress 3")</script>', 913 ); 914 915 // Populate setting as user who can bypass content_save_pre filter. 916 wp_set_current_user( self::$admin_user_id ); 917 $wp_customize = $this->get_manager_for_testing_json_corruption_protection( $uuid ); 918 $wp_customize->set_post_value( 'custom_html_1', $options['custom_html_1'] ); 919 $wp_customize->save_changeset_post( 920 array( 921 'status' => 'draft', 922 ) 923 ); 924 925 // Populate setting as user who cannot bypass content_save_pre filter. 926 wp_set_current_user( $lesser_admin_user_id ); 927 $wp_customize = $this->get_manager_for_testing_json_corruption_protection( $uuid ); 928 $wp_customize->set_post_value( 'custom_html_2', $options['custom_html_2'] ); 929 930 $wp_customize->save_changeset_post( 931 array( 932 'status' => 'draft', 933 ) 934 ); 935 936 /* 937 * Ensure that the unsanitized value (the "POST data") is preserved in the post content. 938 * The value is sent through the sanitize function when it is read from the changeset. 939 */ 940 $wp_customize = $this->get_manager_for_testing_json_corruption_protection( $uuid ); 941 $saved_data = json_decode( get_post( $wp_customize->changeset_post_id() )->post_content, true ); 942 $this->assertEquals( $options['custom_html_1'], $saved_data['custom_html_1']['value'] ); 943 $this->assertEquals( $options['custom_html_2'], $saved_data['custom_html_2']['value'] ); 944 945 /* 946 * Ensure that the unsanitized value (the "POST data") is preserved in the revisions' content. 947 * The value is sent through the sanitize function when it is read from the changeset. 948 */ 949 $revisions = wp_get_post_revisions( $wp_customize->changeset_post_id() ); 950 $revision = array_shift( $revisions ); 951 $saved_data = json_decode( $revision->post_content, true ); 952 $this->assertEquals( $options['custom_html_1'], $saved_data['custom_html_1']['value'] ); 953 $this->assertEquals( $options['custom_html_2'], $saved_data['custom_html_2']['value'] ); 954 955 /* 956 * Now when publishing the changeset, the unsanitized values will be read from the changeset 957 * and sanitized according to the capabilities of the users who originally updated each 958 * setting in the changeset to begin with. 959 */ 960 wp_set_current_user( $lesser_admin_user_id ); 961 $wp_customize = $this->get_manager_for_testing_json_corruption_protection( $uuid ); 962 $wp_customize->set_post_value( 'custom_html_3', $options['custom_html_3'] ); 963 $wp_customize->save_changeset_post( 964 array( 965 'status' => 'publish', 966 ) 967 ); 968 969 // User saved as one who can bypass content_save_pre filter. 970 $this->assertContains( '<script>', get_option( 'custom_html_1' ) ); 971 $this->assertContains( 'Wordpress', get_option( 'custom_html_1' ) ); // phpcs:ignore WordPress.WP.CapitalPDangit.Misspelled 972 973 // User saved as one who cannot bypass content_save_pre filter. 974 $this->assertNotContains( '<script>', get_option( 'custom_html_2' ) ); 975 $this->assertContains( 'WordPress', get_option( 'custom_html_2' ) ); 976 977 // User saved as one who also cannot bypass content_save_pre filter. 978 $this->assertNotContains( '<script>', get_option( 'custom_html_3' ) ); 979 $this->assertContains( 'WordPress', get_option( 'custom_html_3' ) ); 980 } 981 982 /** 983 * Get a manager for testing JSON corruption protection. 984 * 985 * @param string $uuid UUID. 986 * @return WP_Customize_Manager Manager. 987 */ 988 private function get_manager_for_testing_json_corruption_protection( $uuid ) { 989 global $wp_customize; 990 $wp_customize = new WP_Customize_Manager( 991 array( 992 'changeset_uuid' => $uuid, 993 ) 994 ); 995 for ( $i = 0; $i < 5; $i++ ) { 996 $wp_customize->add_setting( 997 sprintf( 'custom_html_%d', $i ), 998 array( 999 'type' => 'option', 1000 'sanitize_callback' => array( $this, 'apply_content_save_pre_filters_if_not_main_admin_user' ), 1001 ) 1002 ); 1003 } 1004 return $wp_customize; 1005 } 1006 1007 /** 1008 * Sanitize content with Kses if the current user is not the main admin. 1009 * 1010 * @since 5.4.1 1011 * 1012 * @param string $content Content to sanitize. 1013 * @return string Sanitized content. 1014 */ 1015 public function apply_content_save_pre_filters_if_not_main_admin_user( $content ) { 1016 if ( get_current_user_id() !== self::$admin_user_id ) { 1017 $content = apply_filters( 'content_save_pre', $content ); 1018 } 1019 return $content; 1020 } 1021 1022 /** 1023 * Filter map_meta_cap to disallow unfiltered_html. 1024 * 1025 * @since 5.4.1 1026 * 1027 * @param array $caps User's capabilities. 1028 * @param string $cap Requested cap. 1029 * @return array Caps. 1030 */ 1031 public function filter_map_meta_cap_to_disallow_unfiltered_html( $caps, $cap ) { 1032 if ( 'unfiltered_html' === $cap ) { 1033 $caps = array( 'do_not_allow' ); 1034 } 1035 return $caps; 1036 } 1037 1038 /** 889 1039 * Call count for customize_changeset_save_data filter. 890 1040 * -
branches/4.7/tests/phpunit/tests/formatting/SanitizeFileName.php
r37756 r47650 68 68 $this->assertEquals( 'no-extension', sanitize_file_name( '_.no-extension' ) ); 69 69 } 70 71 /** 72 * @dataProvider data_wp_filenames 73 */ 74 function test_replaces_invalid_utf8_characters( $input, $expected ) { 75 $this->assertEquals( $expected, sanitize_file_name( $input ) ); 76 } 77 78 function data_wp_filenames() { 79 return array( 80 array( urldecode( '%B1myfile.png' ), 'myfile.png' ), 81 array( urldecode( '%B1myfile' ), 'myfile' ), 82 array( 'demo bar.png', 'demo-bar.png' ), 83 array( 'demo' . json_decode( '"\u00a0"' ) . 'bar.png', 'demo-bar.png' ), 84 ); 85 } 70 86 } -
branches/4.7/tests/phpunit/tests/user.php
r38768 r47650 919 919 } 920 920 921 function test_changing_email_invalidates_password_reset_key() {921 public function test_changing_email_invalidates_password_reset_key() { 922 922 global $wpdb; 923 923 … … 944 944 'user_nicename' => 'cat', 945 945 'user_email' => 'foo@bar.dev', 946 ); 947 wp_update_user( $userdata ); 948 949 $user = get_userdata( $user->ID ); 950 $this->assertEmpty( $user->user_activation_key ); 951 } 952 953 public function test_changing_password_invalidates_password_reset_key() { 954 global $wpdb; 955 956 $user = $this->author; 957 $wpdb->update( $wpdb->users, array( 'user_activation_key' => 'key' ), array( 'ID' => $user->ID ) ); 958 clean_user_cache( $user ); 959 960 $user = get_userdata( $user->ID ); 961 $this->assertEquals( 'key', $user->user_activation_key ); 962 963 $userdata = array( 964 'ID' => $user->ID, 965 'user_pass' => 'password', 946 966 ); 947 967 wp_update_user( $userdata );
Note: See TracChangeset
for help on using the changeset viewer.