From: Edward Z. Yang Date: Thu, 26 Jan 2017 23:37:53 +0000 (-0800) Subject: WordPress 4.7.2 X-Git-Tag: wordpress-4.7.2 X-Git-Url: https://scripts.mit.edu/gitweb/autoinstalls/wordpress.git/commitdiff_plain/refs/heads/pristine?ds=sidebyside WordPress 4.7.2 Signed-off-by: Edward Z. Yang --- diff --git a/wp-admin/about.php b/wp-admin/about.php index 2904ced5..72b20ba5 100644 --- a/wp-admin/about.php +++ b/wp-admin/about.php @@ -44,7 +44,10 @@ include( ABSPATH . 'wp-admin/admin-header.php' );
-

+

+

Version %s addressed some security issues.' ), '4.7.2' ); ?> + the release notes.' ), 'https://codex.wordpress.org/Version_4.7.2' ); ?> +

Version %1$s addressed some security issues and fixed %2$s bug.', 'Version %1$s addressed some security issues and fixed %2$s bugs.', 62 ), '4.7.1', number_format_i18n( 62 ) ); ?> the release notes.' ), 'https://codex.wordpress.org/Version_4.7.1' ); ?> diff --git a/wp-admin/includes/class-wp-posts-list-table.php b/wp-admin/includes/class-wp-posts-list-table.php index ae0f2441..4e1ba34f 100644 --- a/wp-admin/includes/class-wp-posts-list-table.php +++ b/wp-admin/includes/class-wp-posts-list-table.php @@ -955,7 +955,7 @@ class WP_Posts_List_Table extends WP_List_Table { echo "\n"; if ( ! is_post_type_hierarchical( $this->screen->post_type ) && 'excerpt' === $mode && current_user_can( 'read_post', $post->ID ) ) { - the_excerpt(); + echo esc_html( get_the_excerpt() ); } get_inline_data( $post ); diff --git a/wp-admin/includes/class-wp-press-this.php b/wp-admin/includes/class-wp-press-this.php index 71ce5153..9527ef58 100644 --- a/wp-admin/includes/class-wp-press-this.php +++ b/wp-admin/includes/class-wp-press-this.php @@ -119,10 +119,28 @@ class WP_Press_This { 'post_type' => 'post', 'post_status' => 'draft', 'post_format' => ( ! empty( $_POST['post_format'] ) ) ? sanitize_text_field( $_POST['post_format'] ) : '', - 'tax_input' => ( ! empty( $_POST['tax_input'] ) ) ? $_POST['tax_input'] : array(), - 'post_category' => ( ! empty( $_POST['post_category'] ) ) ? $_POST['post_category'] : array(), ); + // Only accept categories if the user actually can assign + $category_tax = get_taxonomy( 'category' ); + if ( current_user_can( $category_tax->cap->assign_terms ) ) { + $post_data['post_category'] = ( ! empty( $_POST['post_category'] ) ) ? $_POST['post_category'] : array(); + } + + // Only accept taxonomies if the user can actually assign + if ( ! empty( $_POST['tax_input'] ) ) { + $tax_input = $_POST['tax_input']; + foreach ( $tax_input as $tax => $_ti ) { + $tax_object = get_taxonomy( $tax ); + if ( ! $tax_object || ! current_user_can( $tax_object->cap->assign_terms ) ) { + unset( $tax_input[ $tax ] ); + } + } + + $post_data['tax_input'] = $tax_input; + } + + // Toggle status to pending if user cannot actually publish if ( ! empty( $_POST['post_status'] ) && 'publish' === $_POST['post_status'] ) { if ( current_user_can( 'publish_posts' ) ) { $post_data['post_status'] = 'publish'; @@ -453,7 +471,7 @@ class WP_Press_This { * @since 4.2.0 * * @param string $src Embed source URL. - * @return string If not from a supported provider, an empty string. Otherwise, a reformattd embed URL. + * @return string If not from a supported provider, an empty string. Otherwise, a reformatted embed URL. */ private function _limit_embed( $src ) { $src = $this->_limit_url( $src ); @@ -853,6 +871,12 @@ class WP_Press_This { public function categories_html( $post ) { $taxonomy = get_taxonomy( 'category' ); + // Bail if user cannot assign terms + if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) { + return; + } + + // Only show "add" if user can edit terms if ( current_user_can( $taxonomy->cap->edit_terms ) ) { ?> - - - + + + + + + +

@@ -1447,23 +1481,27 @@ class WP_Press_This { - + + + - + + + diff --git a/wp-includes/class-wp-comment.php b/wp-includes/class-wp-comment.php index 80ef9bb1..0e9f88a3 100644 --- a/wp-includes/class-wp-comment.php +++ b/wp-includes/class-wp-comment.php @@ -191,12 +191,11 @@ final class WP_Comment { public static function get_instance( $id ) { global $wpdb; - if ( ! is_numeric( $id ) || $id != floor( $id ) || ! $id ) { + $comment_id = (int) $id; + if ( ! $comment_id ) { return false; } - $comment_id = (int) $id; - $_comment = wp_cache_get( $comment_id, 'comment' ); if ( ! $_comment ) { diff --git a/wp-includes/class-wp-post.php b/wp-includes/class-wp-post.php index c9669348..edaf149a 100644 --- a/wp-includes/class-wp-post.php +++ b/wp-includes/class-wp-post.php @@ -210,12 +210,11 @@ final class WP_Post { public static function get_instance( $post_id ) { global $wpdb; - if ( ! is_numeric( $post_id ) || $post_id != floor( $post_id ) || ! $post_id ) { + $post_id = (int) $post_id; + if ( ! $post_id ) { return false; } - $post_id = (int) $post_id; - $_post = wp_cache_get( $post_id, 'posts' ); if ( ! $_post ) { diff --git a/wp-includes/class-wp-query.php b/wp-includes/class-wp-query.php index 7f3c22a7..c0de352c 100644 --- a/wp-includes/class-wp-query.php +++ b/wp-includes/class-wp-query.php @@ -2255,12 +2255,12 @@ class WP_Query { if ( empty( $in_search_post_types ) ) { $where .= ' AND 1=0 '; } else { - $where .= " AND {$wpdb->posts}.post_type IN ('" . join("', '", $in_search_post_types ) . "')"; + $where .= " AND {$wpdb->posts}.post_type IN ('" . join( "', '", array_map( 'esc_sql', $in_search_post_types ) ) . "')"; } } elseif ( !empty( $post_type ) && is_array( $post_type ) ) { - $where .= " AND {$wpdb->posts}.post_type IN ('" . join("', '", $post_type) . "')"; + $where .= " AND {$wpdb->posts}.post_type IN ('" . join("', '", esc_sql( $post_type ) ) . "')"; } elseif ( ! empty( $post_type ) ) { - $where .= " AND {$wpdb->posts}.post_type = '$post_type'"; + $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_type = %s", $post_type ); $post_type_object = get_post_type_object ( $post_type ); } elseif ( $this->is_attachment ) { $where .= " AND {$wpdb->posts}.post_type = 'attachment'"; diff --git a/wp-includes/class-wp-term.php b/wp-includes/class-wp-term.php index 6cb4a15b..8eb87efb 100644 --- a/wp-includes/class-wp-term.php +++ b/wp-includes/class-wp-term.php @@ -125,12 +125,11 @@ final class WP_Term { public static function get_instance( $term_id, $taxonomy = null ) { global $wpdb; - if ( ! is_numeric( $term_id ) || $term_id != floor( $term_id ) || ! $term_id ) { + $term_id = (int) $term_id; + if ( ! $term_id ) { return false; } - $term_id = (int) $term_id; - $_term = wp_cache_get( $term_id, 'terms' ); // If there isn't a cached version, hit the database. diff --git a/wp-includes/rest-api.php b/wp-includes/rest-api.php index e5efb74f..5b62d96f 100644 --- a/wp-includes/rest-api.php +++ b/wp-includes/rest-api.php @@ -46,6 +46,13 @@ function register_rest_route( $namespace, $route, $args = array(), $override = f return false; } + if ( isset( $args['args'] ) ) { + $common_args = $args['args']; + unset( $args['args'] ); + } else { + $common_args = array(); + } + if ( isset( $args['callback'] ) ) { // Upgrade a single set to multiple. $args = array( $args ); @@ -57,12 +64,13 @@ function register_rest_route( $namespace, $route, $args = array(), $override = f 'args' => array(), ); foreach ( $args as $key => &$arg_group ) { - if ( ! is_numeric( $arg_group ) ) { + if ( ! is_numeric( $key ) ) { // Route option, skip here. continue; } $arg_group = array_merge( $defaults, $arg_group ); + $arg_group['args'] = array_merge( $common_args, $arg_group['args'] ); } $full_route = '/' . trim( $namespace, '/' ) . '/' . trim( $route, '/' ); diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php index a3438c5f..79c3d571 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php @@ -63,6 +63,12 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the object.' ), + 'type' => 'integer', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), @@ -299,6 +305,36 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { return $response; } + /** + * Get the comment, if the ID is valid. + * + * @since 4.7.2 + * + * @param int $id Supplied ID. + * @return WP_Comment|WP_Error Comment object if ID is valid, WP_Error otherwise. + */ + protected function get_comment( $id ) { + $error = new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); + if ( (int) $id <= 0 ) { + return $error; + } + + $id = (int) $id; + $comment = get_comment( $id ); + if ( empty( $comment ) ) { + return $error; + } + + if ( ! empty( $comment->comment_post_ID ) ) { + $post = get_post( (int) $comment->comment_post_ID ); + if ( empty( $post ) ) { + return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) ); + } + } + + return $comment; + } + /** * Checks if a given request has access to read the comment. * @@ -309,12 +345,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { * @return WP_Error|bool True if the request has read access for the item, error object otherwise. */ public function get_item_permissions_check( $request ) { - $id = (int) $request['id']; - - $comment = get_comment( $id ); - - if ( ! $comment ) { - return true; + $comment = $this->get_comment( $request['id'] ); + if ( is_wp_error( $comment ) ) { + return $comment; } if ( ! empty( $request['context'] ) && 'edit' === $request['context'] && ! current_user_can( 'moderate_comments' ) ) { @@ -344,18 +377,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { * @return WP_Error|WP_REST_Response Response object on success, or error object on failure. */ public function get_item( $request ) { - $id = (int) $request['id']; - - $comment = get_comment( $id ); - if ( empty( $comment ) ) { - return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); - } - - if ( ! empty( $comment->comment_post_ID ) ) { - $post = get_post( $comment->comment_post_ID ); - if ( empty( $post ) ) { - return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) ); - } + $comment = $this->get_comment( $request['id'] ); + if ( is_wp_error( $comment ) ) { + return $comment; } $data = $this->prepare_item_for_response( $comment, $request ); @@ -624,12 +648,12 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { * @return WP_Error|bool True if the request has access to update the item, error object otherwise. */ public function update_item_permissions_check( $request ) { + $comment = $this->get_comment( $request['id'] ); + if ( is_wp_error( $comment ) ) { + return $comment; + } - $id = (int) $request['id']; - - $comment = get_comment( $id ); - - if ( $comment && ! $this->check_edit_permission( $comment ) ) { + if ( ! $this->check_edit_permission( $comment ) ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this comment.' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -646,14 +670,13 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { * @return WP_Error|WP_REST_Response Response object on success, or error object on failure. */ public function update_item( $request ) { - $id = (int) $request['id']; - - $comment = get_comment( $id ); - - if ( empty( $comment ) ) { - return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); + $comment = $this->get_comment( $request['id'] ); + if ( is_wp_error( $comment ) ) { + return $comment; } + $id = $comment->comment_ID; + if ( isset( $request['type'] ) && get_comment_type( $id ) !== $request['type'] ) { return new WP_Error( 'rest_comment_invalid_type', __( 'Sorry, you are not allowed to change the comment type.' ), array( 'status' => 404 ) ); } @@ -744,11 +767,9 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { * @return WP_Error|bool True if the request has access to delete the item, error object otherwise. */ public function delete_item_permissions_check( $request ) { - $id = (int) $request['id']; - $comment = get_comment( $id ); - - if ( ! $comment ) { - return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); + $comment = $this->get_comment( $request['id'] ); + if ( is_wp_error( $comment ) ) { + return $comment; } if ( ! $this->check_edit_permission( $comment ) ) { @@ -767,15 +788,13 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { * @return WP_Error|WP_REST_Response Response object on success, or error object on failure. */ public function delete_item( $request ) { - $id = (int) $request['id']; - $force = isset( $request['force'] ) ? (bool) $request['force'] : false; - - $comment = get_comment( $id ); - - if ( empty( $comment ) ) { - return new WP_Error( 'rest_comment_invalid_id', __( 'Invalid comment ID.' ), array( 'status' => 404 ) ); + $comment = $this->get_comment( $request['id'] ); + if ( is_wp_error( $comment ) ) { + return $comment; } + $force = isset( $request['force'] ) ? (bool) $request['force'] : false; + /** * Filters whether a comment can be trashed. * diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php index 0ec4bcd1..59575d56 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php @@ -48,6 +48,12 @@ class WP_REST_Post_Statuses_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( + 'args' => array( + 'status' => array( + 'description' => __( 'An alphanumeric identifier for the status.' ), + 'type' => 'string', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php index a1437dd5..45ef1552 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php @@ -48,6 +48,12 @@ class WP_REST_Post_Types_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( + 'args' => array( + 'type' => array( + 'description' => __( 'An alphanumeric identifier for the post type.' ), + 'type' => 'string', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php index c037ae21..7b27f60d 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php @@ -88,6 +88,12 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { ); } register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the object.' ), + 'type' => 'integer', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), @@ -349,6 +355,28 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { return $response; } + /** + * Get the post, if the ID is valid. + * + * @since 4.7.2 + * + * @param int $id Supplied ID. + * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. + */ + protected function get_post( $id ) { + $error = new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) ); + if ( (int) $id <= 0 ) { + return $error; + } + + $post = get_post( (int) $id ); + if ( empty( $post ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { + return $error; + } + + return $post; + } + /** * Checks if a given request has access to read a post. * @@ -359,8 +387,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return bool|WP_Error True if the request has read access for the item, WP_Error object otherwise. */ public function get_item_permissions_check( $request ) { - - $post = get_post( (int) $request['id'] ); + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; + } if ( 'edit' === $request['context'] && $post && ! $this->check_update_permission( $post ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this post.' ), array( 'status' => rest_authorization_required_code() ) ); @@ -428,18 +458,16 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_item( $request ) { - $id = (int) $request['id']; - $post = get_post( $id ); - - if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { - return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) ); + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; } $data = $this->prepare_item_for_response( $post, $request ); $response = rest_ensure_response( $data ); if ( is_post_type_viewable( get_post_type_object( $post->post_type ) ) ) { - $response->link_header( 'alternate', get_permalink( $id ), array( 'type' => 'text/html' ) ); + $response->link_header( 'alternate', get_permalink( $post->ID ), array( 'type' => 'text/html' ) ); } return $response; @@ -455,6 +483,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise. */ public function create_item_permissions_check( $request ) { + if ( ! empty( $request['id'] ) ) { + return new WP_Error( 'rest_post_exists', __( 'Cannot create existing post.' ), array( 'status' => 400 ) ); + } $post_type = get_post_type_object( $this->post_type ); @@ -591,8 +622,11 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise. */ public function update_item_permissions_check( $request ) { + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; + } - $post = get_post( $request['id'] ); $post_type = get_post_type_object( $this->post_type ); if ( $post && ! $this->check_update_permission( $post ) ) { @@ -624,11 +658,9 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function update_item( $request ) { - $id = (int) $request['id']; - $post = get_post( $id ); - - if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { - return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) ); + $valid_check = $this->get_post( $request['id'] ); + if ( is_wp_error( $valid_check ) ) { + return $valid_check; } $post = $this->prepare_item_for_database( $request ); @@ -714,8 +746,10 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. */ public function delete_item_permissions_check( $request ) { - - $post = get_post( $request['id'] ); + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; + } if ( $post && ! $this->check_delete_permission( $post ) ) { return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this post.' ), array( 'status' => rest_authorization_required_code() ) ); @@ -734,15 +768,14 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function delete_item( $request ) { - $id = (int) $request['id']; - $force = (bool) $request['force']; - - $post = get_post( $id ); - - if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) { - return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) ); + $post = $this->get_post( $request['id'] ); + if ( is_wp_error( $post ) ) { + return $post; } + $id = $post->ID; + $force = (bool) $request['force']; + $supports_trash = ( EMPTY_TRASH_DAYS > 0 ); if ( 'attachment' === $post->post_type ) { @@ -901,7 +934,12 @@ class WP_REST_Posts_Controller extends WP_REST_Controller { // Post ID. if ( isset( $request['id'] ) ) { - $prepared_post->ID = absint( $request['id'] ); + $existing_post = $this->get_post( $request['id'] ); + if ( is_wp_error( $existing_post ) ) { + return $existing_post; + } + + $prepared_post->ID = $existing_post->ID; } $schema = $this->get_item_schema(); diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php index 39ef8fe8..c38bd459 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php @@ -71,6 +71,12 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { public function register_routes() { register_rest_route( $this->namespace, '/' . $this->parent_base . '/(?P[\d]+)/' . $this->rest_base, array( + 'args' => array( + 'parent' => array( + 'description' => __( 'The ID for the parent of the object.' ), + 'type' => 'integer', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_items' ), @@ -81,6 +87,16 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->parent_base . '/(?P[\d]+)/' . $this->rest_base . '/(?P[\d]+)', array( + 'args' => array( + 'parent' => array( + 'description' => __( 'The ID for the parent of the object.' ), + 'type' => 'integer', + ), + 'id' => array( + 'description' => __( 'Unique identifier for the object.' ), + 'type' => 'integer', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), @@ -106,6 +122,28 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { } + /** + * Get the parent post, if the ID is valid. + * + * @since 4.7.2 + * + * @param int $id Supplied ID. + * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. + */ + protected function get_parent( $parent ) { + $error = new WP_Error( 'rest_post_invalid_parent', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) ); + if ( (int) $parent <= 0 ) { + return $error; + } + + $parent = get_post( (int) $parent ); + if ( empty( $parent ) || empty( $parent->ID ) || $this->parent_post_type !== $parent->post_type ) { + return $error; + } + + return $parent; + } + /** * Checks if a given request has access to get revisions. * @@ -116,11 +154,11 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { * @return true|WP_Error True if the request has read access, WP_Error object otherwise. */ public function get_items_permissions_check( $request ) { - - $parent = get_post( $request['parent'] ); - if ( ! $parent ) { - return true; + $parent = $this->get_parent( $request['parent'] ); + if ( is_wp_error( $parent ) ) { + return $parent; } + $parent_post_type_obj = get_post_type_object( $parent->post_type ); if ( ! current_user_can( $parent_post_type_obj->cap->edit_post, $parent->ID ) ) { return new WP_Error( 'rest_cannot_read', __( 'Sorry, you are not allowed to view revisions of this post.' ), array( 'status' => rest_authorization_required_code() ) ); @@ -129,6 +167,28 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { return true; } + /** + * Get the revision, if the ID is valid. + * + * @since 4.7.2 + * + * @param int $id Supplied ID. + * @return WP_Post|WP_Error Revision post object if ID is valid, WP_Error otherwise. + */ + protected function get_revision( $id ) { + $error = new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision ID.' ), array( 'status' => 404 ) ); + if ( (int) $id <= 0 ) { + return $error; + } + + $revision = get_post( (int) $id ); + if ( empty( $revision ) || empty( $revision->ID ) || 'revision' !== $revision->post_type ) { + return $error; + } + + return $revision; + } + /** * Gets a collection of revisions. * @@ -139,9 +199,9 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_items( $request ) { - $parent = get_post( $request['parent'] ); - if ( ! $request['parent'] || ! $parent || $this->parent_post_type !== $parent->post_type ) { - return new WP_Error( 'rest_post_invalid_parent', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) ); + $parent = $this->get_parent( $request['parent'] ); + if ( is_wp_error( $parent ) ) { + return $parent; } $revisions = wp_get_post_revisions( $request['parent'] ); @@ -177,14 +237,14 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_item( $request ) { - $parent = get_post( $request['parent'] ); - if ( ! $request['parent'] || ! $parent || $this->parent_post_type !== $parent->post_type ) { - return new WP_Error( 'rest_post_invalid_parent', __( 'Invalid post parent ID.' ), array( 'status' => 404 ) ); + $parent = $this->get_parent( $request['parent'] ); + if ( is_wp_error( $parent ) ) { + return $parent; } - $revision = get_post( $request['id'] ); - if ( ! $revision || 'revision' !== $revision->post_type ) { - return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision ID.' ), array( 'status' => 404 ) ); + $revision = $this->get_revision( $request['id'] ); + if ( is_wp_error( $revision ) ) { + return $revision; } $response = $this->prepare_item_for_response( $revision, $request ); @@ -201,18 +261,23 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { * @return bool|WP_Error True if the request has access to delete the item, WP_Error object otherwise. */ public function delete_item_permissions_check( $request ) { + $parent = $this->get_parent( $request['parent'] ); + if ( is_wp_error( $parent ) ) { + return $parent; + } + + $revision = $this->get_revision( $request['id'] ); + if ( is_wp_error( $revision ) ) { + return $revision; + } $response = $this->get_items_permissions_check( $request ); if ( ! $response || is_wp_error( $response ) ) { return $response; } - $post = get_post( $request['id'] ); - if ( ! $post ) { - return new WP_Error( 'rest_post_invalid_id', __( 'Invalid revision ID.' ), array( 'status' => 404 ) ); - } $post_type = get_post_type_object( 'revision' ); - return current_user_can( $post_type->cap->delete_post, $post->ID ); + return current_user_can( $post_type->cap->delete_post, $revision->ID ); } /** @@ -225,6 +290,11 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { * @return true|WP_Error True on success, or WP_Error object on failure. */ public function delete_item( $request ) { + $revision = $this->get_revision( $request['id'] ); + if ( is_wp_error( $revision ) ) { + return $revision; + } + $force = isset( $request['force'] ) ? (bool) $request['force'] : false; // We don't support trashing for revisions. @@ -232,7 +302,6 @@ class WP_REST_Revisions_Controller extends WP_REST_Controller { return new WP_Error( 'rest_trash_not_supported', __( 'Revisions do not support trashing. Set force=true to delete.' ), array( 'status' => 501 ) ); } - $revision = get_post( $request['id'] ); $previous = $this->prepare_item_for_response( $revision, $request ); $result = wp_delete_post( $request['id'], true ); diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php index 0fe91ee4..b46562c9 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php @@ -48,6 +48,12 @@ class WP_REST_Taxonomies_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( + 'args' => array( + 'taxonomy' => array( + 'description' => __( 'An alphanumeric identifier for the taxonomy.' ), + 'type' => 'string', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php index b8a1390d..7e8c3f89 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php @@ -96,6 +96,12 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the term.' ), + 'type' => 'integer', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), @@ -108,7 +114,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { 'methods' => WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_item_permissions_check' ), - 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), + 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ), ), array( 'methods' => WP_REST_Server::DELETABLE, @@ -287,6 +293,33 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { return $response; } + /** + * Get the term, if the ID is valid. + * + * @since 4.7.2 + * + * @param int $id Supplied ID. + * @return WP_Term|WP_Error Term object if ID is valid, WP_Error otherwise. + */ + protected function get_term( $id ) { + $error = new WP_Error( 'rest_term_invalid', __( 'Term does not exist.' ), array( 'status' => 404 ) ); + + if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { + return $error; + } + + if ( (int) $id <= 0 ) { + return $error; + } + + $term = get_term( (int) $id, $this->taxonomy ); + if ( empty( $term ) || $term->taxonomy !== $this->taxonomy ) { + return $error; + } + + return $term; + } + /** * Checks if a request has access to read or edit the specified term. * @@ -297,11 +330,12 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { * @return bool|WP_Error True if the request has read access for the item, otherwise false or WP_Error object. */ public function get_item_permissions_check( $request ) { - $tax_obj = get_taxonomy( $this->taxonomy ); - if ( ! $tax_obj || ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { - return false; + $term = $this->get_term( $request['id'] ); + if ( is_wp_error( $term ) ) { + return $term; } - if ( 'edit' === $request['context'] && ! current_user_can( 'edit_term', (int) $request['id'] ) ) { + + if ( 'edit' === $request['context'] && ! current_user_can( 'edit_term', $term->term_id ) ) { return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this term.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; @@ -317,12 +351,7 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_item( $request ) { - - $term = get_term( (int) $request['id'], $this->taxonomy ); - - if ( ! $term || $term->taxonomy !== $this->taxonomy ) { - return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) ); - } + $term = $this->get_term( $request['id'] ); if ( is_wp_error( $term ) ) { return $term; @@ -445,15 +474,9 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { * @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise. */ public function update_item_permissions_check( $request ) { - - if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { - return false; - } - - $term = get_term( (int) $request['id'], $this->taxonomy ); - - if ( ! $term ) { - return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) ); + $term = $this->get_term( $request['id'] ); + if ( is_wp_error( $term ) ) { + return $term; } if ( ! current_user_can( 'edit_term', $term->term_id ) ) { @@ -473,6 +496,11 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function update_item( $request ) { + $term = $this->get_term( $request['id'] ); + if ( is_wp_error( $term ) ) { + return $term; + } + if ( isset( $request['parent'] ) ) { if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set parent term, taxonomy is not hierarchical.' ), array( 'status' => 400 ) ); @@ -487,8 +515,6 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { $prepared_term = $this->prepare_item_for_database( $request ); - $term = get_term( (int) $request['id'], $this->taxonomy ); - // Only update the term if we haz something to update. if ( ! empty( $prepared_term ) ) { $update = wp_update_term( $term->term_id, $term->taxonomy, wp_slash( (array) $prepared_term ) ); @@ -498,14 +524,14 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { } } - $term = get_term( (int) $request['id'], $this->taxonomy ); + $term = get_term( $term->term_id, $this->taxonomy ); /* This action is documented in lib/endpoints/class-wp-rest-terms-controller.php */ do_action( "rest_insert_{$this->taxonomy}", $term, $request, false ); $schema = $this->get_item_schema(); if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { - $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); + $meta_update = $this->meta->update_value( $request['meta'], $term->term_id ); if ( is_wp_error( $meta_update ) ) { return $meta_update; @@ -535,14 +561,9 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { * @return bool|WP_Error True if the request has access to delete the item, otherwise false or WP_Error object. */ public function delete_item_permissions_check( $request ) { - if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { - return false; - } - - $term = get_term( (int) $request['id'], $this->taxonomy ); - - if ( ! $term ) { - return new WP_Error( 'rest_term_invalid', __( "Term doesn't exist." ), array( 'status' => 404 ) ); + $term = $this->get_term( $request['id'] ); + if ( is_wp_error( $term ) ) { + return $term; } if ( ! current_user_can( 'delete_term', $term->term_id ) ) { @@ -562,6 +583,10 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function delete_item( $request ) { + $term = $this->get_term( $request['id'] ); + if ( is_wp_error( $term ) ) { + return $term; + } $force = isset( $request['force'] ) ? (bool) $request['force'] : false; @@ -570,8 +595,6 @@ class WP_REST_Terms_Controller extends WP_REST_Controller { return new WP_Error( 'rest_trash_not_supported', __( 'Terms do not support trashing. Set force=true to delete.' ), array( 'status' => 501 ) ); } - $term = get_term( (int) $request['id'], $this->taxonomy ); - $request->set_param( 'context', 'view' ); $previous = $this->prepare_item_for_response( $term, $request ); diff --git a/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php b/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php index 3e44884a..454d4989 100644 --- a/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php +++ b/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php @@ -65,6 +65,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller { ) ); register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\d]+)', array( + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the user.' ), + 'type' => 'integer', + ), + ), array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), @@ -325,6 +331,28 @@ class WP_REST_Users_Controller extends WP_REST_Controller { return $response; } + /** + * Get the user, if the ID is valid. + * + * @since 4.7.2 + * + * @param int $id Supplied ID. + * @return WP_User|WP_Error True if ID is valid, WP_Error otherwise. + */ + protected function get_user( $id ) { + $error = new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) ); + if ( (int) $id <= 0 ) { + return $error; + } + + $user = get_userdata( (int) $id ); + if ( empty( $user ) || ! $user->exists() ) { + return $error; + } + + return $user; + } + /** * Checks if a given request has access to read a user. * @@ -335,22 +363,20 @@ class WP_REST_Users_Controller extends WP_REST_Controller { * @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object. */ public function get_item_permissions_check( $request ) { + $user = $this->get_user( $request['id'] ); + if ( is_wp_error( $user ) ) { + return $user; + } - $id = (int) $request['id']; - $user = get_userdata( $id ); $types = get_post_types( array( 'show_in_rest' => true ), 'names' ); - if ( empty( $id ) || empty( $user->ID ) ) { - return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) ); - } - - if ( get_current_user_id() === $id ) { + if ( get_current_user_id() === $user->ID ) { return true; } if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) ); - } elseif ( ! count_user_posts( $id, $types ) && ! current_user_can( 'edit_user', $id ) && ! current_user_can( 'list_users' ) ) { + } elseif ( ! count_user_posts( $user->ID, $types ) && ! current_user_can( 'edit_user', $user->ID ) && ! current_user_can( 'list_users' ) ) { return new WP_Error( 'rest_user_cannot_view', __( 'Sorry, you are not allowed to list users.' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -367,11 +393,9 @@ class WP_REST_Users_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function get_item( $request ) { - $id = (int) $request['id']; - $user = get_userdata( $id ); - - if ( empty( $id ) || empty( $user->ID ) ) { - return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) ); + $user = $this->get_user( $request['id'] ); + if ( is_wp_error( $user ) ) { + return $user; } $user = $this->prepare_item_for_response( $user, $request ); @@ -541,10 +565,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller { * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise. */ public function update_item_permissions_check( $request ) { + $user = $this->get_user( $request['id'] ); + if ( is_wp_error( $user ) ) { + return $user; + } - $id = (int) $request['id']; - - if ( ! current_user_can( 'edit_user', $id ) ) { + if ( ! current_user_can( 'edit_user', $user->ID ) ) { return new WP_Error( 'rest_cannot_edit', __( 'Sorry, you are not allowed to edit this user.' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -565,8 +591,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller { * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function update_item( $request ) { - $id = (int) $request['id']; - $user = get_userdata( $id ); + $user = $this->get_user( $request['id'] ); + if ( is_wp_error( $user ) ) { + return $user; + } + + $id = $user->ID; if ( ! $user ) { return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) ); @@ -681,10 +711,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller { * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. */ public function delete_item_permissions_check( $request ) { + $user = $this->get_user( $request['id'] ); + if ( is_wp_error( $user ) ) { + return $user; + } - $id = (int) $request['id']; - - if ( ! current_user_can( 'delete_user', $id ) ) { + if ( ! current_user_can( 'delete_user', $user->ID ) ) { return new WP_Error( 'rest_user_cannot_delete', __( 'Sorry, you are not allowed to delete this user.' ), array( 'status' => rest_authorization_required_code() ) ); } @@ -705,8 +737,12 @@ class WP_REST_Users_Controller extends WP_REST_Controller { if ( is_multisite() ) { return new WP_Error( 'rest_cannot_delete', __( 'The user cannot be deleted.' ), array( 'status' => 501 ) ); } + $user = $this->get_user( $request['id'] ); + if ( is_wp_error( $user ) ) { + return $user; + } - $id = (int) $request['id']; + $id = $user->ID; $reassign = false === $request['reassign'] ? null : absint( $request['reassign'] ); $force = isset( $request['force'] ) ? (bool) $request['force'] : false; @@ -715,12 +751,6 @@ class WP_REST_Users_Controller extends WP_REST_Controller { return new WP_Error( 'rest_trash_not_supported', __( 'Users do not support trashing. Set force=true to delete.' ), array( 'status' => 501 ) ); } - $user = get_userdata( $id ); - - if ( ! $user ) { - return new WP_Error( 'rest_user_invalid_id', __( 'Invalid user ID.' ), array( 'status' => 404 ) ); - } - if ( ! empty( $reassign ) ) { if ( $reassign === $id || ! get_userdata( $reassign ) ) { return new WP_Error( 'rest_user_invalid_reassign', __( 'Invalid user ID for reassignment.' ), array( 'status' => 400 ) ); diff --git a/wp-includes/version.php b/wp-includes/version.php index 0bbb467a..c11eb329 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -4,7 +4,7 @@ * * @global string $wp_version */ -$wp_version = '4.7.1'; +$wp_version = '4.7.2'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.