From 8fe31dc36af68e61ac87d141df394b0884994a63 Mon Sep 17 00:00:00 2001 From: Rachel Baker Date: Fri, 2 Dec 2016 22:04:06 +0000 Subject: [PATCH] REST API: Return a `WP_Error` if `meta` property is not an array. Fixes bug where a PHP Warning is currently thrown if a client sends a request where `meta` is not an array value. Merges [39436] onto 4.7 branch. Props timmydcrawford, jnylen0, rachelbaker, pento. Fixes #38989 for 4.7. git-svn-id: https://develop.svn.wordpress.org/branches/4.7@39437 602fd350-edb4-49c9-b593-d223f7449a82 --- .../fields/class-wp-rest-meta-fields.php | 35 +++++++++-- .../tests/rest-api/rest-post-meta-fields.php | 59 +++++++++++++++++++ 2 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php b/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php index e19a8129ea..eaf70a5781 100644 --- a/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php +++ b/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php @@ -117,15 +117,15 @@ abstract class WP_REST_Meta_Fields { * @since 4.7.0 * @access public * - * @param WP_REST_Request $request Full details about the request. + * @param array $meta Array of meta parsed from the request. * @param int $object_id Object ID to fetch meta for. * @return WP_Error|null WP_Error if one occurs, null on success. */ - public function update_value( $request, $object_id ) { + public function update_value( $meta, $object_id ) { $fields = $this->get_registered_fields(); foreach ( $fields as $meta_key => $args ) { $name = $args['name']; - if ( ! array_key_exists( $name, $request ) ) { + if ( ! array_key_exists( $name, $meta ) ) { continue; } @@ -133,7 +133,7 @@ abstract class WP_REST_Meta_Fields { * A null value means reset the field, which is essentially deleting it * from the database and then relying on the default value. */ - if ( is_null( $request[ $name ] ) ) { + if ( is_null( $meta[ $name ] ) ) { $result = $this->delete_meta_value( $object_id, $meta_key, $name ); if ( is_wp_error( $result ) ) { return $result; @@ -141,13 +141,13 @@ abstract class WP_REST_Meta_Fields { continue; } - $is_valid = rest_validate_value_from_schema( $request[ $name ], $args['schema'], 'meta.' . $name ); + $is_valid = rest_validate_value_from_schema( $meta[ $name ], $args['schema'], 'meta.' . $name ); if ( is_wp_error( $is_valid ) ) { $is_valid->add_data( array( 'status' => 400 ) ); return $is_valid; } - $value = rest_sanitize_value_from_schema( $request[ $name ], $args['schema'] ); + $value = rest_sanitize_value_from_schema( $meta[ $name ], $args['schema'] ); if ( $args['single'] ) { $result = $this->update_meta_value( $object_id, $meta_key, $name, $value ); @@ -391,6 +391,10 @@ abstract class WP_REST_Meta_Fields { 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array(), + 'arg_options' => array( + 'sanitize_callback' => null, + 'validate_callback' => array( $this, 'check_meta_is_array' ), + ), ); foreach ( $fields as $args ) { @@ -444,4 +448,23 @@ abstract class WP_REST_Meta_Fields { return $value; } + + /** + * Check the 'meta' value of a request is an associative array. + * + * @since 4.7.0 + * @access public + * + * @param mixed $value The meta value submitted in the request. + * @param WP_REST_Request $request Full details about the request. + * @param string $param The parameter name. + * @return WP_Error|string The meta array, if valid, otherwise an error. + */ + public function check_meta_is_array( $value, $request, $param ) { + if ( ! is_array( $value ) ) { + return false; + } + + return $value; + } } diff --git a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php index 6684cad95e..9aa5110fc3 100644 --- a/tests/phpunit/tests/rest-api/rest-post-meta-fields.php +++ b/tests/phpunit/tests/rest-api/rest-post-meta-fields.php @@ -786,6 +786,65 @@ class WP_Test_REST_Post_Meta_Fields extends WP_Test_REST_TestCase { $this->assertContains( 'graeme', $meta ); } + /** + * @ticket 38989 + */ + public function test_set_value_invalid_meta_string_request_type() { + update_post_meta( self::$post_id, 'test_single', 'So I tied an onion to my belt, which was the style at the time.' ); + $post_original = get_post( self::$post_id ); + + $this->grant_write_permission(); + + $data = array( + 'title' => 'Ignore this title', + 'meta' => 'Not an array.', + ); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + // The meta value should not have changed. + $current_value = get_post_meta( self::$post_id, 'test_single', true ); + $this->assertEquals( 'So I tied an onion to my belt, which was the style at the time.', $current_value ); + + // Ensure the post title update was not processed. + $post_updated = get_post( self::$post_id ); + $this->assertEquals( $post_original->post_title, $post_updated->post_title ); + } + + /** + * @ticket 38989 + */ + public function test_set_value_invalid_meta_float_request_type() { + update_post_meta( self::$post_id, 'test_single', 'Now, to take the ferry cost a nickel, and in those days, nickels had pictures of bumblebees on them.' ); + $post_original = get_post( self::$post_id ); + + $this->grant_write_permission(); + + $data = array( + 'content' => 'Ignore this content.', + 'meta' => 1.234, + ); + + $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/posts/%d', self::$post_id ) ); + $request->set_body_params( $data ); + + $response = $this->server->dispatch( $request ); + $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); + + // The meta value should not have changed. + $current_value = get_post_meta( self::$post_id, 'test_single', true ); + $this->assertEquals( 'Now, to take the ferry cost a nickel, and in those days, nickels had pictures of bumblebees on them.', $current_value ); + + // Ensure the post content update was not processed. + $post_updated = get_post( self::$post_id ); + $this->assertEquals( $post_original->post_content, $post_updated->post_content ); + } + public function test_remove_multi_value_db_error() { add_post_meta( self::$post_id, 'test_multi', 'val1' ); $values = get_post_meta( self::$post_id, 'test_multi', false );