From 1ca43d9e0211d179b5d0e814d71cfec3b0c59243 Mon Sep 17 00:00:00 2001
From: lucha <lucha@paranoici.org>
Date: Thu, 2 Feb 2017 18:30:10 +0100
Subject: [PATCH] Wordpress 4.7.2

---
 wp-admin/about.php                            |   5 +-
 .../includes/class-wp-posts-list-table.php    |   2 +-
 wp-admin/includes/class-wp-press-this.php     |  98 +++++++++++-----
 wp-includes/class-wp-comment.php              |   5 +-
 wp-includes/class-wp-post.php                 |   5 +-
 wp-includes/class-wp-query.php                |   6 +-
 wp-includes/class-wp-term.php                 |   5 +-
 wp-includes/rest-api.php                      |  10 +-
 .../class-wp-rest-comments-controller.php     | 101 ++++++++++-------
 ...class-wp-rest-post-statuses-controller.php |   6 +
 .../class-wp-rest-post-types-controller.php   |   6 +
 .../class-wp-rest-posts-controller.php        |  86 ++++++++++----
 .../class-wp-rest-revisions-controller.php    | 107 ++++++++++++++----
 .../class-wp-rest-taxonomies-controller.php   |   6 +
 .../class-wp-rest-terms-controller.php        |  91 +++++++++------
 .../class-wp-rest-users-controller.php        |  86 +++++++++-----
 wp-includes/version.php                       |   2 +-
 17 files changed, 435 insertions(+), 192 deletions(-)

diff --git a/wp-admin/about.php b/wp-admin/about.php
index 2904ced58..72b20ba50 100644
--- a/wp-admin/about.php
+++ b/wp-admin/about.php
@@ -44,7 +44,10 @@ include( ABSPATH . 'wp-admin/admin-header.php' );
 		</h2>
 
 		<div class="changelog point-releases">
-			<h3><?php _e( 'Maintenance and Security Release' ); ?></h3>
+			<h3><?php _e( 'Maintenance and Security Releases' ); ?></h3>
+			<p><?php printf( __( '<strong>Version %s</strong> addressed some security issues.' ), '4.7.2' ); ?>
+				<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), 'https://codex.wordpress.org/Version_4.7.2' ); ?>
+			</p>
 			<p><?php printf( _n( '<strong>Version %1$s</strong> addressed some security issues and fixed %2$s bug.',
 					'<strong>Version %1$s</strong> addressed some security issues and fixed %2$s bugs.', 62 ), '4.7.1', number_format_i18n( 62 ) ); ?>
 				<?php printf( __( 'For more information, see <a href="%s">the release notes</a>.' ), '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 ae0f2441f..4e1ba34f1 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 "</strong>\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 71ce5153d..9527ef585 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 ) ) {
 			?>
 			<button type="button" class="add-cat-toggle button-link" aria-expanded="false">
@@ -1272,6 +1296,12 @@ class WP_Press_This {
 		wp_enqueue_script( 'json2' );
 		wp_enqueue_script( 'editor' );
 
+		$categories_tax   = get_taxonomy( 'category' );
+		$show_categories  = current_user_can( $categories_tax->cap->assign_terms ) || current_user_can( $categories_tax->cap->edit_terms );
+
+		$tag_tax          = get_taxonomy( 'post_tag' );
+		$show_tags        = current_user_can( $tag_tax->cap->assign_terms );
+
 		$supports_formats = false;
 		$post_format      = 0;
 
@@ -1423,17 +1453,21 @@ class WP_Press_This {
 					</button>
 				<?php endif; ?>
 
-				<button type="button" class="button-link post-option">
-					<span class="dashicons dashicons-category"></span>
-					<span class="post-option-title"><?php _e( 'Categories' ); ?></span>
-					<span class="dashicons post-option-forward"></span>
-				</button>
-
-				<button type="button" class="button-link post-option">
-					<span class="dashicons dashicons-tag"></span>
-					<span class="post-option-title"><?php _e( 'Tags' ); ?></span>
-					<span class="dashicons post-option-forward"></span>
-				</button>
+				<?php if ( $show_categories ) : ?>
+					<button type="button" class="button-link post-option">
+						<span class="dashicons dashicons-category"></span>
+						<span class="post-option-title"><?php _e( 'Categories' ); ?></span>
+						<span class="dashicons post-option-forward"></span>
+					</button>
+				<?php endif; ?>
+
+				<?php if ( $show_tags ) : ?>
+					<button type="button" class="button-link post-option">
+						<span class="dashicons dashicons-tag"></span>
+						<span class="post-option-title"><?php _e( 'Tags' ); ?></span>
+						<span class="dashicons post-option-forward"></span>
+					</button>
+				<?php endif; ?>
 			</div>
 
 			<?php if ( $supports_formats ) : ?>
@@ -1447,23 +1481,27 @@ class WP_Press_This {
 				</div>
 			<?php endif; ?>
 
-			<div class="setting-modal is-off-screen is-hidden">
-				<button type="button" class="button-link modal-close">
-					<span class="dashicons post-option-back"></span>
-					<span class="setting-title" aria-hidden="true"><?php _e( 'Categories' ); ?></span>
-					<span class="screen-reader-text"><?php _e( 'Back to post options' ) ?></span>
-				</button>
-				<?php $this->categories_html( $post ); ?>
-			</div>
+			<?php if ( $show_categories ) : ?>
+				<div class="setting-modal is-off-screen is-hidden">
+					<button type="button" class="button-link modal-close">
+						<span class="dashicons post-option-back"></span>
+						<span class="setting-title" aria-hidden="true"><?php _e( 'Categories' ); ?></span>
+						<span class="screen-reader-text"><?php _e( 'Back to post options' ) ?></span>
+					</button>
+					<?php $this->categories_html( $post ); ?>
+				</div>
+			<?php endif; ?>
 
-			<div class="setting-modal tags is-off-screen is-hidden">
-				<button type="button" class="button-link modal-close">
-					<span class="dashicons post-option-back"></span>
-					<span class="setting-title" aria-hidden="true"><?php _e( 'Tags' ); ?></span>
-					<span class="screen-reader-text"><?php _e( 'Back to post options' ) ?></span>
-				</button>
-				<?php $this->tags_html( $post ); ?>
-			</div>
+			<?php if ( $show_tags ) : ?>
+				<div class="setting-modal tags is-off-screen is-hidden">
+					<button type="button" class="button-link modal-close">
+						<span class="dashicons post-option-back"></span>
+						<span class="setting-title" aria-hidden="true"><?php _e( 'Tags' ); ?></span>
+						<span class="screen-reader-text"><?php _e( 'Back to post options' ) ?></span>
+					</button>
+					<?php $this->tags_html( $post ); ?>
+				</div>
+			<?php endif; ?>
 		</div><!-- .options-panel -->
 	</div><!-- .wrapper -->
 
diff --git a/wp-includes/class-wp-comment.php b/wp-includes/class-wp-comment.php
index 80ef9bb1d..0e9f88a35 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 c96693489..edaf149a3 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 7f3c22a78..c0de352c2 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 6cb4a15bd..8eb87efbe 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 e5efb74fb..5b62d96fc 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 a3438c5f7..79c3d5711 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<id>[\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 0ec4bcd1a..59575d566 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<status>[\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 a1437dd59..45ef15520 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<type>[\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 c037ae210..7b27f60de 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<id>[\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 39ef8fe81..c38bd4592 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<parent>[\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<parent>[\d]+)/' . $this->rest_base . '/(?P<id>[\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 0fe91ee43..b46562c9c 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<taxonomy>[\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 b8a1390d3..7e8c3f89e 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<id>[\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 3e44884a3..454d4989f 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<id>[\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 0bbb467a9..c11eb329d 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.
-- 
GitLab