REST API: Return `WP_Error` when a client is attempting to update an option with a non-scalar value to `null`.

A `null` value is returned in the response for any option that has a non-scalar value.

To protect clients from accidentally including the `null` values from a response object in a request, we do not allow options with non-scalar values to be updated to `null`. Without this added protection a client could mistakenly delete all options that have non-scalar values from the database.

Props joehoyle, rachelbaker.
Fixes #38527.

git-svn-id: https://develop.svn.wordpress.org/trunk@38982 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Rachel Baker 2016-10-27 16:07:06 +00:00
parent f6c85e6b72
commit 3ffefccdf2
2 changed files with 90 additions and 2 deletions

View File

@ -95,6 +95,12 @@ class WP_REST_Settings_Controller extends WP_REST_Controller {
* @return mixed
*/
protected function prepare_value( $value, $schema ) {
// If the value is not a scalar, it's not possible to cast it to
// anything.
if ( ! is_scalar( $value ) ) {
return null;
}
switch ( $schema['type'] ) {
case 'string':
return (string) $value;
@ -141,9 +147,29 @@ class WP_REST_Settings_Controller extends WP_REST_Controller {
continue;
}
// A null value means reset the option, which is essentially deleting it
// from the database and then relying on the default value.
/**
* A `null` value for an option would have the same effect as
* deleting the option from the database, and relying on the
* default value.
*/
if ( is_null( $request[ $name ] ) ) {
/**
* A `null` value is returned in the response for any option
* that has a non-scalar value.
*
* To protect clients from accidentally including the `null`
* values from a response object in a request, we do not allow
* options with non-scalar values to be updated to `null`.
* Without this added protection a client could mistakenly
* delete all options that have non-scalar values from the
* database.
*/
if ( ! is_scalar( get_option( $args['option_name'], false ) ) ) {
return new WP_Error(
'rest_invalid_stored_value', sprintf( __( 'The %s property has an invalid stored value, and cannot be updated to null.' ), $name ), array( 'status' => 500 )
);
}
delete_option( $args['option_name'] );
} else {
update_option( $args['option_name'], $request[ $name ] );

View File

@ -159,6 +159,51 @@ class WP_Test_REST_Settings_Controller extends WP_Test_REST_Controller_Testcase
remove_all_filters( 'rest_pre_get_setting' );
}
public function test_get_item_with_invalid_value_array_in_options() {
wp_set_current_user( self::$administrator );
register_setting( 'somegroup', 'mycustomsetting', array(
'show_in_rest' => array(
'name' => 'mycustomsettinginrest',
'schema' => array(
'enum' => array( 'validvalue1', 'validvalue2' ),
'default' => 'validvalue1',
),
),
'type' => 'string',
) );
update_option( 'mycustomsetting', array( 'A sneaky array!' ) );
$request = new WP_REST_Request( 'GET', '/wp/v2/settings' );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( null, $data['mycustomsettinginrest'] );
}
public function test_get_item_with_invalid_object_array_in_options() {
wp_set_current_user( self::$administrator );
register_setting( 'somegroup', 'mycustomsetting', array(
'show_in_rest' => array(
'name' => 'mycustomsettinginrest',
'schema' => array(
'enum' => array( 'validvalue1', 'validvalue2' ),
'default' => 'validvalue1',
),
),
'type' => 'string',
) );
update_option( 'mycustomsetting', (object) array( 'A sneaky array!' ) );
$request = new WP_REST_Request( 'GET', '/wp/v2/settings' );
$response = $this->server->dispatch( $request );
$data = $response->get_data();
$this->assertEquals( null, $data['mycustomsettinginrest'] );
}
public function test_create_item() {
}
@ -248,6 +293,23 @@ class WP_Test_REST_Settings_Controller extends WP_Test_REST_Controller_Testcase
$this->assertErrorResponse( 'rest_invalid_param', $response, 400 );
}
public function test_update_item_with_invalid_stored_value_in_options() {
wp_set_current_user( self::$administrator );
register_setting( 'somegroup', 'mycustomsetting', array(
'show_in_rest' => true,
'type' => 'string',
) );
update_option( 'mycustomsetting', array( 'A sneaky array!' ) );
wp_set_current_user( self::$administrator );
$request = new WP_REST_Request( 'PUT', '/wp/v2/settings' );
$request->set_param( 'mycustomsetting', null );
$response = $this->server->dispatch( $request );
$this->assertErrorResponse( 'rest_invalid_stored_value', $response, 500 );
}
public function test_delete_item() {
}