From 6c17b0335fe0355119d28b362cb19b69cbfc18e3 Mon Sep 17 00:00:00 2001 From: David Baumwald Date: Tue, 27 Sep 2022 20:12:00 +0000 Subject: [PATCH] REST API: Ensure `args` is an array of arrays in `register_rest_route()`. When calling `register_rest_route()`, the `args` parameter for a route should be an array of arrays. However, some plugins/themes have passed an array of strings or key-value pairs which produces a PHP warning when `array_intersect_key` is used to filter the array keys based on an allowed list of schema keywords. This change adds a check of the `args` parameter to ensure it's an array of arrays, presenting a `_doing_it_wrong` if any element of `args` is not an array and restructuring to an array of arrays. This change also adds a unit test for the incorrect usage described above, expecting that a `_doing_it_wrong` is produced. Props slaFFik, desrosj, apermo, AndrewNZ, aristath, poena, dovyp, timothyblynjacobs, Hinjiriyo, johnmark8080, nateallen. Fixes #51986. git-svn-id: https://develop.svn.wordpress.org/trunk@54339 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/rest-api.php | 12 ++++++++++++ .../rest-api/class-wp-rest-server.php | 5 +++++ tests/phpunit/tests/rest-api.php | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 01a1a9a50c..47b922bcd8 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -103,6 +103,18 @@ function register_rest_route( $namespace, $route, $args = array(), $override = f '5.5.0' ); } + + if ( count( array_filter( $arg_group['args'], 'is_array' ) ) !== count( $arg_group['args'] ) ) { + _doing_it_wrong( + __FUNCTION__, + sprintf( + /* translators: %s: The REST API route being registered. */ + __( 'REST API $args should be an array of arrays. Non-array value detected for %s.' ), + '' . $clean_namespace . '/' . trim( $route, '/' ) . '' + ), + '6.1.0' + ); + } } $full_route = '/' . $clean_namespace . '/' . trim( $route, '/' ); 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 ddb3f6c01b..6327d5a539 100644 --- a/src/wp-includes/rest-api/class-wp-rest-server.php +++ b/src/wp-includes/rest-api/class-wp-rest-server.php @@ -1513,6 +1513,11 @@ class WP_REST_Server { $endpoint_data['args'] = array(); foreach ( $callback['args'] as $key => $opts ) { + if ( is_string( $opts ) ) { + $opts = array( $opts => 0 ); + } elseif ( ! is_array( $opts ) ) { + $opts = array(); + } $arg_data = array_intersect_key( $opts, $allowed_schema_keywords ); $arg_data['required'] = ! empty( $opts['required'] ); diff --git a/tests/phpunit/tests/rest-api.php b/tests/phpunit/tests/rest-api.php index fa25502959..854e9b8338 100644 --- a/tests/phpunit/tests/rest-api.php +++ b/tests/phpunit/tests/rest-api.php @@ -2518,4 +2518,23 @@ class Tests_REST_API extends WP_UnitTestCase { array( 'description', '_links' ) ); } + + /** + * @ticket 51986 + */ + public function test_route_args_is_array_of_arrays() { + $this->setExpectedIncorrectUsage( 'register_rest_route' ); + + $registered = register_rest_route( + 'my-ns/v1', + '/my-route', + array( + 'callback' => '__return_true', + 'permission_callback' => '__return_true', + 'args' => array( 'pattern' ), + ) + ); + + $this->assertTrue( $registered ); + } }