Make WordPress Core


Ignore:
Timestamp:
09/17/2026 09:15:04 PM (10 hours ago)
Author:
desrosj
Message:

Security: Backport the WordPress 7.1.1 security fixes to the 4.7 branch.

  • Posts, Post Types: Reject a supplied post ID on the create path in _wp_translate_postdata().
  • XML-RPC: Reject writes to internal-only builtin post types.
  • Administration: Add authorization check to wp_ajax_sample_permalink().
  • Customize: Improve header_image_data theme mod sanitization.
  • Plugins: Require network plugin authority to Ajax-activate a network-only plugin.
  • Formatting: Prevent wpautop() moving a paragraph into an attribute of a blockquote.

Merges r63657, r63659, r63660, r63665, r63669, r63672 to the 4.7 branch.

Props xknown, westonruter, jorbin, vortfu, batmoo, davidbinda, jeremyfelt, johnbillion, peterwilsoncc, lancewillett, jonsurrell, dmsnell, whyisjake, buffer1024, joehoyle, rafiem.

Location:
branches/4.7
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • branches/4.7

  • branches/4.7/src/wp-includes/customize/class-wp-customize-header-image-setting.php

    r39412 r63715  
    1616 *
    1717 * @see WP_Customize_Setting
     18 *
     19 * @phpstan-type Header_Image_Data array{
     20 *     attachment_id?: int,
     21 *     url?: string,
     22 *     thumbnail_url?: string,
     23 *     timestamp?: int,
     24 *     width?: int,
     25 *     height?: int,
     26 *     alt_text?: string,
     27 *     attachment_parent?: int,
     28 * }
    1829 */
    1930final class WP_Customize_Header_Image_Setting extends WP_Customize_Setting {
     
    4960                        $custom_image_header->set_header_image( $value );
    5061        }
     62
     63        /**
     64         * Sanitizes a header value.
     65         *
     66         * The value is expected to be one of the following:
     67         *
     68         * - An array of header image data, with the keys `attachment_id`, `url`, `thumbnail_url`, `timestamp`, `width`,
     69         *   `height`, `alt_text`, and `attachment_parent`, as supplied by {@see get_uploaded_header_images()}. Any other
     70         *   key is discarded.
     71         * - An array with a `choice` key, being the legacy format in which any of the other accepted values is nested.
     72         * - The string `remove-header`, `random-default-image`, or `random-uploaded-image`.
     73         * - A string corresponding to one of the keys for the array returned by {@see get_uploaded_header_images()}, or
     74         *   one of the keys for the array passed into {@see register_default_headers()}.
     75         *
     76         * @since 7.1.1
     77         *
     78         * @see WP_Customize_Header_Image_Setting::update()
     79         * @see Custom_Image_Header::set_header_image()
     80         *
     81         * @param mixed $value Value to sanitize.
     82         * @return array|string|WP_Error|null Sanitized value, or `null`/`WP_Error` if invalid. The array holds
     83         *                                    the header image data, or that data nested under a `choice` key,
     84         *                                    before the `customize_sanitize_header_image_data` filter, which
     85         *                                    may return anything, is applied to it.
     86         *
     87         * @phpstan-return array<mixed, mixed>|string|WP_Error|null
     88         */
     89        public function sanitize( $value ) {
     90                /*
     91                 * The update() method unwraps the legacy `choice` format before handing the value off to
     92                 * Custom_Image_Header::set_header_image(), so the nested value is what must be sanitized.
     93                 */
     94                if ( is_array( $value ) && isset( $value['choice'] ) ) {
     95                        $choice = $this->sanitize_choice( $value['choice'] );
     96                        if ( is_null( $choice ) || is_wp_error( $choice ) ) {
     97                                return $choice;
     98                        }
     99                        $value = array( 'choice' => $choice );
     100                } else {
     101                        $value = $this->sanitize_choice( $value );
     102                        if ( is_null( $value ) || is_wp_error( $value ) ) {
     103                                return $value;
     104                        }
     105                }
     106
     107                return parent::sanitize( $value );
     108        }
     109
     110        /**
     111         * Sanitizes a header image choice.
     112         *
     113         * This is the value which is ultimately passed to {@see Custom_Image_Header::set_header_image()}, whether
     114         * supplied at the top level of the setting value or nested under its legacy `choice` key.
     115         *
     116         * @since 7.1.1
     117         *
     118         * @param mixed $value Value to sanitize.
     119         * @return array|string|WP_Error|null Sanitized value, or `null`/`WP_Error` if invalid.
     120         *
     121         * @phpstan-return Header_Image_Data|string|WP_Error|null
     122         */
     123        private function sanitize_choice( $value ) {
     124                // Custom_Image_Header::set_header_image() accepts an object in place of an array.
     125                if ( is_object( $value ) ) {
     126                        $value = (array) $value;
     127                }
     128
     129                if ( is_string( $value ) ) {
     130                        return sanitize_text_field( $value );
     131                }
     132
     133                if ( ! is_array( $value ) ) {
     134                        return null;
     135                }
     136
     137                /*
     138                 * The sanitized value is assembled member by member rather than filtered down from the
     139                 * supplied one, so that nothing but the members below can end up in it.
     140                 */
     141                $sanitized = array();
     142
     143                if ( isset( $value['attachment_id'] ) ) {
     144                        if ( ! is_scalar( $value['attachment_id'] ) ) {
     145                                return null;
     146                        }
     147                        $attachment_id = absint( $value['attachment_id'] );
     148
     149                        /*
     150                         * A supplied attachment must be an existing image, since its ID is written to postmeta and its
     151                         * data displayed. Note that an ID of zero must be skipped rather than looked up, as
     152                         * get_post_mime_type() falls back to the global post when passed an empty value.
     153                         */
     154                        if ( $attachment_id > 0 ) {
     155                                $mime_type = get_post_mime_type( $attachment_id );
     156                                if ( ! is_string( $mime_type ) || ! str_starts_with( $mime_type, 'image/' ) ) {
     157                                        return null;
     158                                }
     159                        }
     160
     161                        $sanitized['attachment_id'] = $attachment_id;
     162                }
     163
     164                if ( isset( $value['url'] ) ) {
     165                        if ( ! is_string( $value['url'] ) ) {
     166                                return null;
     167                        }
     168                        $sanitized['url'] = esc_url_raw( $value['url'] );
     169                        if ( '' === $sanitized['url'] ) {
     170                                return new WP_Error( 'invalid_url', __( 'Invalid URL.' ) );
     171                        }
     172                }
     173
     174                if ( isset( $value['thumbnail_url'] ) ) {
     175                        if ( ! is_string( $value['thumbnail_url'] ) ) {
     176                                return null;
     177                        }
     178                        $sanitized['thumbnail_url'] = esc_url_raw( $value['thumbnail_url'] );
     179                        if ( '' === $sanitized['thumbnail_url'] ) {
     180                                return new WP_Error( 'invalid_url', __( 'Invalid URL.' ) );
     181                        }
     182                }
     183
     184                if ( isset( $value['timestamp'] ) ) {
     185                        if ( ! is_scalar( $value['timestamp'] ) ) {
     186                                return null;
     187                        }
     188                        $sanitized['timestamp'] = absint( $value['timestamp'] );
     189                }
     190
     191                if ( isset( $value['width'] ) ) {
     192                        if ( ! is_scalar( $value['width'] ) ) {
     193                                return null;
     194                        }
     195                        $sanitized['width'] = absint( $value['width'] );
     196                }
     197
     198                if ( isset( $value['height'] ) ) {
     199                        if ( ! is_scalar( $value['height'] ) ) {
     200                                return null;
     201                        }
     202                        $sanitized['height'] = absint( $value['height'] );
     203                }
     204
     205                if ( isset( $value['alt_text'] ) ) {
     206                        if ( ! is_string( $value['alt_text'] ) ) {
     207                                return null;
     208                        }
     209                        $sanitized['alt_text'] = sanitize_text_field( $value['alt_text'] );
     210                }
     211
     212                if ( isset( $value['attachment_parent'] ) ) {
     213                        if ( ! is_scalar( $value['attachment_parent'] ) ) {
     214                                return null;
     215                        }
     216                        $sanitized['attachment_parent'] = absint( $value['attachment_parent'] );
     217                }
     218
     219                return $sanitized;
     220        }
    51221}
Note: See TracChangeset for help on using the changeset viewer.