From 433b4fbfba454698b749e9fe0ad0eb2a97c761b0 Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Wed, 14 Sep 2016 15:49:37 +0000 Subject: [PATCH] REST API: Enable sanitize_callback to return WP_Error. Give developers the opportunity to reject incoming data without using the validation callback. It also enables us to do sanitization and validation in one function in instances where this could be useful. Props websupporter, rmccue. Fixes #37560. git-svn-id: https://develop.svn.wordpress.org/trunk@38601 602fd350-edb4-49c9-b593-d223f7449a82 --- .../rest-api/class-wp-rest-request.php | 25 ++++++++++++++----- .../rest-api/class-wp-rest-server.php | 7 ++++-- tests/phpunit/tests/rest-api/rest-request.php | 21 ++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/wp-includes/rest-api/class-wp-rest-request.php b/src/wp-includes/rest-api/class-wp-rest-request.php index 80c58bf5b1..15c4728941 100644 --- a/src/wp-includes/rest-api/class-wp-rest-request.php +++ b/src/wp-includes/rest-api/class-wp-rest-request.php @@ -780,10 +780,9 @@ class WP_REST_Request implements ArrayAccess { * @since 4.4.0 * @access public * - * @return true|null True if there are no parameters to sanitize, null otherwise. + * @return true|WP_Error True if parameters were sanitized, WP_Error if an error occurred during sanitization. */ public function sanitize_params() { - $attributes = $this->get_attributes(); // No arguments set, skip sanitizing. @@ -793,18 +792,33 @@ class WP_REST_Request implements ArrayAccess { $order = $this->get_parameter_order(); + $invalid_params = array(); + foreach ( $order as $type ) { if ( empty( $this->params[ $type ] ) ) { continue; } foreach ( $this->params[ $type ] as $key => $value ) { // Check if this param has a sanitize_callback added. - if ( isset( $attributes['args'][ $key ] ) && ! empty( $attributes['args'][ $key ]['sanitize_callback'] ) ) { - $this->params[ $type ][ $key ] = call_user_func( $attributes['args'][ $key ]['sanitize_callback'], $value, $this, $key ); + if ( ! isset( $attributes['args'][ $key ] ) || empty( $attributes['args'][ $key ]['sanitize_callback'] ) ) { + continue; + } + + $sanitized_value = call_user_func( $attributes['args'][ $key ]['sanitize_callback'], $value, $this, $key ); + + if ( is_wp_error( $sanitized_value ) ) { + $invalid_params[ $key ] = $sanitized_value->get_error_message(); + } else { + $this->params[ $type ][ $key ] = $sanitized_value; } } } - return null; + + if ( $invalid_params ) { + return new WP_Error( 'rest_invalid_param', sprintf( __( 'Invalid parameter(s): %s' ), implode( ', ', array_keys( $invalid_params ) ) ), array( 'status' => 400, 'params' => $invalid_params ) ); + } + + return true; } /** @@ -817,7 +831,6 @@ class WP_REST_Request implements ArrayAccess { * WP_Error if required parameters are missing. */ public function has_valid_params() { - $attributes = $this->get_attributes(); $required = array(); diff --git a/src/wp-includes/rest-api/class-wp-rest-server.php b/src/wp-includes/rest-api/class-wp-rest-server.php index 7a45906690..1e5914bfe7 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -866,9 +866,12 @@ class WP_REST_Server { $check_required = $request->has_valid_params(); if ( is_wp_error( $check_required ) ) { $response = $check_required; + } else { + $check_sanitized = $request->sanitize_params(); + if ( is_wp_error( $check_sanitized ) ) { + $response = $check_sanitized; + } } - - $request->sanitize_params(); } if ( ! is_wp_error( $response ) ) { diff --git a/tests/phpunit/tests/rest-api/rest-request.php b/tests/phpunit/tests/rest-api/rest-request.php index bd6548341e..925853c8cd 100644 --- a/tests/phpunit/tests/rest-api/rest-request.php +++ b/tests/phpunit/tests/rest-api/rest-request.php @@ -309,6 +309,27 @@ class Tests_REST_Request extends WP_UnitTestCase { $this->assertEquals( 0, $this->request->get_param( 'somestring' ) ); } + public function test_sanitize_params_error() { + $this->request->set_url_params( array( + 'successparam' => '123', + 'failparam' => '123', + )); + $this->request->set_attributes( array( + 'args' => array( + 'successparam' => array( + 'sanitize_callback' => 'absint', + ), + 'failparam' => array( + 'sanitize_callback' => array( $this, '_return_wp_error_on_validate_callback' ), + ), + ), + )); + + $valid = $this->request->sanitize_params(); + $this->assertWPError( $valid ); + $this->assertEquals( 'rest_invalid_param', $valid->get_error_code() ); + } + public function test_has_valid_params_required_flag() { $this->request->set_attributes( array( 'args' => array(