WordPress.org

Make WordPress Core

Ticket #38583: 38583.5.diff

File 38583.5.diff, 9.8 KB (added by joehoyle, 2 years ago)
  • src/wp-includes/rest-api.php

    diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php
    index 697a7cc..82d856a 100644
    a b function rest_validate_value_from_schema( $value, $args, $param = '' ) { 
    11061106                }
    11071107
    11081108                foreach ( $value as $property => $v ) {
    1109                         if ( ! isset( $args['properties'][ $property ] ) ) {
    1110                                 continue;
    1111                         }
    1112                         $is_valid = rest_validate_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' );
    1113 
    1114                         if ( is_wp_error( $is_valid ) ) {
    1115                                 return $is_valid;
     1109                        if ( isset( $args['properties'][ $property ] ) ) {
     1110                                $is_valid = rest_validate_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' );
     1111                                if ( is_wp_error( $is_valid ) ) {
     1112                                        return $is_valid;
     1113                                }
     1114                        } elseif ( isset( $args['additionalProperties'] ) && false === $args['additionalProperties'] ) {
     1115                                return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not a valid property of Object.' ), $property ) );
    11161116                        }
    11171117                }
    11181118        }
    function rest_sanitize_value_from_schema( $value, $args ) { 
    12461246                }
    12471247
    12481248                foreach ( $value as $property => $v ) {
    1249                         if ( ! isset( $args['properties'][ $property ] ) ) {
     1249                        if ( isset( $args['properties'][ $property ] ) ) {
     1250                                $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ] );
     1251                        } elseif ( isset( $args['additionalProperties'] ) && false === $args['additionalProperties'] ) {
    12501252                                unset( $value[ $property ] );
    1251                                 continue;
    12521253                        }
    1253                         $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ] );
    12541254                }
    12551255
    12561256                return $value;
  • src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php

    diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-controller.php
    index 2fb5221..f26e876 100644
    a b abstract class WP_REST_Controller { 
    545545                                $endpoint_args[ $field_id ]['required'] = true;
    546546                        }
    547547
    548                         foreach ( array( 'type', 'format', 'enum', 'items', 'properties' ) as $schema_prop ) {
     548                        foreach ( array( 'type', 'format', 'enum', 'items', 'properties', 'additionalProperties' ) as $schema_prop ) {
    549549                                if ( isset( $params[ $schema_prop ] ) ) {
    550550                                        $endpoint_args[ $field_id ][ $schema_prop ] = $params[ $schema_prop ];
    551551                                }
  • src/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php

    diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php
    index 21e467f..0a612cd 100644
    a b class WP_REST_Settings_Controller extends WP_REST_Controller { 
    248248                                continue;
    249249                        }
    250250
     251                        $rest_args['schema'] = $this->add_additional_properties_false_recursive( $rest_args['schema'] );
     252
    251253                        $rest_options[ $rest_args['name'] ] = $rest_args;
    252254                }
    253255
    class WP_REST_Settings_Controller extends WP_REST_Controller { 
    301303                }
    302304                return rest_parse_request_arg( $value, $request, $param );
    303305        }
     306
     307        /**
     308         * Recursively add additionalProperties = false to all objects in a schema.
     309         *
     310         * @since 4.9.0
     311         *
     312         * @param array $schema The schema array.
     313         * @return array
     314         */
     315        protected function add_additional_properties_false_recursive( $schema ) {
     316                switch ( $schema['type'] ) {
     317                        case 'object':
     318                                foreach ( $schema['properties'] as $key => $child_schema ) {
     319                                        $schema['properties'][ $key ] = $this->add_additional_properties_false_recursive( $child_schema );
     320                                }
     321                                $schema['additionalProperties'] = false;
     322                                break;
     323                        case 'array':
     324                                $schema['items'] = $this->add_additional_properties_false_recursive( $schema['items'] );
     325                                break;
     326                }
     327
     328                return $schema;
     329        }
    304330}
  • tests/phpunit/tests/rest-api/rest-schema-sanitization.php

    diff --git a/tests/phpunit/tests/rest-api/rest-schema-sanitization.php b/tests/phpunit/tests/rest-api/rest-schema-sanitization.php
    index cb224b3..b6cf1e8 100644
    a b class WP_Test_REST_Schema_Sanitization extends WP_UnitTestCase { 
    157157                );
    158158                $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( array( 'a' => 1 ), $schema ) );
    159159                $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( array( 'a' => '1' ), $schema ) );
     160                $this->assertEquals( array( 'a' => 1, 'b' => 1 ), rest_sanitize_value_from_schema( array( 'a' => '1', 'b' => 1 ), $schema ) );
     161        }
     162
     163        public function test_type_object_strips_additional_properties() {
     164                $schema = array(
     165                        'type'       => 'object',
     166                        'properties' => array(
     167                                'a' => array(
     168                                        'type' => 'number',
     169                                ),
     170                        ),
     171                        'additionalProperties' => false,
     172                );
     173                $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( array( 'a' => 1 ), $schema ) );
     174                $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( array( 'a' => '1' ), $schema ) );
     175                $this->assertEquals( array( 'a' => 1 ), rest_sanitize_value_from_schema( array( 'a' => '1', 'b' => 1 ), $schema ) );
    160176        }
    161177
    162178        public function test_type_object_nested() {
    class WP_Test_REST_Schema_Sanitization extends WP_UnitTestCase { 
    195211                                'a' => array(
    196212                                        'b' => 1,
    197213                                        'c' => 3,
     214                                        'd' => '1',
    198215                                ),
     216                                'b' => 1,
    199217                        ),
    200218                        rest_sanitize_value_from_schema(
    201219                                array(
  • tests/phpunit/tests/rest-api/rest-schema-validation.php

    diff --git a/tests/phpunit/tests/rest-api/rest-schema-validation.php b/tests/phpunit/tests/rest-api/rest-schema-validation.php
    index 9932538..dc983b2 100644
    a b class WP_Test_REST_Schema_Validation extends WP_UnitTestCase { 
    186186                        'type'       => 'object',
    187187                        'properties' => array(
    188188                                'a' => array(
    189                                         'type' => 'number'
     189                                        'type' => 'number',
    190190                                ),
    191191                        ),
    192192                );
    193193                $this->assertTrue( rest_validate_value_from_schema( array( 'a' => 1 ), $schema ) );
     194                $this->assertTrue( rest_validate_value_from_schema( array( 'a' => 1, 'b' => 2 ), $schema ) );
    194195                $this->assertWPError( rest_validate_value_from_schema( array( 'a' => 'invalid' ), $schema ) );
    195196        }
    196197
     198        public function test_type_object_additional_properties_false() {
     199                $schema = array(
     200                        'type'       => 'object',
     201                        'properties' => array(
     202                                'a' => array(
     203                                        'type' => 'number',
     204                                ),
     205                        ),
     206                        'additionalProperties' => false,
     207                );
     208                $this->assertTrue( rest_validate_value_from_schema( array( 'a' => 1 ), $schema ) );
     209                $this->assertWPError( rest_validate_value_from_schema( array( 'a' => 1, 'b' => 2 ), $schema ) );
     210        }
     211
    197212        public function test_type_object_nested() {
    198213                $schema = array(
    199214                        'type' => 'object',
  • tests/phpunit/tests/rest-api/rest-settings-controller.php

    diff --git a/tests/phpunit/tests/rest-api/rest-settings-controller.php b/tests/phpunit/tests/rest-api/rest-settings-controller.php
    index b4d7c68..d91d8ba 100644
    a b class WP_Test_REST_Settings_Controller extends WP_Test_REST_Controller_Testcase 
    190190                        'type'         => 'object',
    191191                ) );
    192192
     193                // We have to re-register the route, as the args changes based off registered settings.
     194                $this->server->override_by_default = true;
     195                $this->endpoint->register_routes();
     196
    193197                // Object is cast to correct types.
    194198                update_option( 'mycustomsetting', array( 'a' => '1' ) );
    195199                $request = new WP_REST_Request( 'GET', '/wp/v2/settings' );
    class WP_Test_REST_Settings_Controller extends WP_Test_REST_Controller_Testcase 
    209213                $request = new WP_REST_Request( 'GET', '/wp/v2/settings' );
    210214                $response = $this->server->dispatch( $request );
    211215                $data = $response->get_data();
    212                 $this->assertEquals( array( 'a' => 1 ), $data['mycustomsetting'] );
     216                $this->assertEquals( null, $data['mycustomsetting'] );
    213217
    214218                unregister_setting( 'somegroup', 'mycustomsetting' );
    215219        }
    class WP_Test_REST_Settings_Controller extends WP_Test_REST_Controller_Testcase 
    372376                unregister_setting( 'somegroup', 'mycustomsetting' );
    373377        }
    374378
     379        public function test_update_item_with_nested_object() {
     380                register_setting( 'somegroup', 'mycustomsetting', array(
     381                        'show_in_rest' => array(
     382                                'schema' => array(
     383                                        'type'       => 'object',
     384                                        'properties' => array(
     385                                                'a' => array(
     386                                                        'type' => 'object',
     387                                                        'properties' => array(
     388                                                                'b' => array(
     389                                                                        'type' => 'number',
     390                                                                ),
     391                                                        ),
     392                                                ),
     393                                        ),
     394                                ),
     395                        ),
     396                        'type'         => 'object',
     397                ) );
     398
     399                // We have to re-register the route, as the args changes based off registered settings.
     400                $this->server->override_by_default = true;
     401                $this->endpoint->register_routes();
     402                wp_set_current_user( self::$administrator );
     403
     404                $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' );
     405                $request->set_param( 'mycustomsetting', array( 'a' => array( 'b' => 1, 'c' => 1 ) ) );
     406                $response = $this->server->dispatch( $request );
     407                $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
     408        }
     409
    375410        public function test_update_item_with_object() {
    376411                register_setting( 'somegroup', 'mycustomsetting', array(
    377412                        'show_in_rest' => array(
    class WP_Test_REST_Settings_Controller extends WP_Test_REST_Controller_Testcase 
    407442                $this->assertEquals( array(), $data['mycustomsetting'] );
    408443                $this->assertEquals( array(), get_option( 'mycustomsetting' ) );
    409444
     445                // Provide more keys.
     446                $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' );
     447                $request->set_param( 'mycustomsetting', array( 'a' => 1, 'b' => 2 ) );
     448                $response = $this->server->dispatch( $request );
     449
     450                $this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
     451
    410452                // Setting an invalid object.
    411453                $request = new WP_REST_Request( 'PUT', '/wp/v2/settings' );
    412454                $request->set_param( 'mycustomsetting', array( 'a' => 'invalid' ) );