Skip to content

Commit 6fc981f

Browse files
committed
REST API: Fix object/array validation for JSON-encoded GET parameters.This commit aligns GET parameter handling with POST requests by allowingJSON-encoded strings to pass 'object' and 'array' validation andsanitization.- Added JSON coercion in rest_validate_value_from_schema().- Added JSON coercion in rest_sanitize_value_from_schema().- Supports multi-type schemas and uses json_last_error() for safety.Fixes #64926
1 parent 4d3b0b9 commit 6fc981f

File tree

1 file changed

+50
-0
lines changed

1 file changed

+50
-0
lines changed

src/wp-includes/rest-api.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2182,6 +2182,7 @@ function rest_get_allowed_schema_keywords() {
21822182
* @return true|WP_Error
21832183
*/
21842184
function rest_validate_value_from_schema( $value, $args, $param = '' ) {
2185+
21852186
if ( isset( $args['anyOf'] ) ) {
21862187
$matching_schema = rest_find_any_matching_schema( $value, $args, $param );
21872188
if ( is_wp_error( $matching_schema ) ) {
@@ -2243,9 +2244,32 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) {
22432244
$is_valid = rest_validate_boolean_value_from_schema( $value, $param );
22442245
break;
22452246
case 'object':
2247+
/*
2248+
* A JSON-encoded string (e.g. from a GET query parameter) should be
2249+
* decoded before validation, mirroring what parse_json_params() does
2250+
* for application/json request bodies.
2251+
*/
2252+
if ( is_string( $value ) ) {
2253+
$decoded = json_decode( $value, true );
2254+
if ( null !== $decoded && JSON_ERROR_NONE === json_last_error() ) {
2255+
$value = $decoded;
2256+
}
2257+
}
22462258
$is_valid = rest_validate_object_value_from_schema( $value, $args, $param );
22472259
break;
22482260
case 'array':
2261+
/*
2262+
* A JSON-encoded string (e.g. ?ids=[1,2,3]) should be decoded before
2263+
* validation. This takes priority over the comma-separated-value
2264+
* fallback in rest_is_array() / wp_parse_list(), which cannot
2265+
* preserve value types.
2266+
*/
2267+
if ( is_string( $value ) && str_starts_with( ltrim( $value ), '[' ) ) {
2268+
$decoded = json_decode( $value, true );
2269+
if ( is_array( $decoded ) && JSON_ERROR_NONE === json_last_error() ) {
2270+
$value = $decoded;
2271+
}
2272+
}
22492273
$is_valid = rest_validate_array_value_from_schema( $value, $args, $param );
22502274
break;
22512275
case 'number':
@@ -2780,6 +2804,7 @@ function rest_validate_integer_value_from_schema( $value, $args, $param ) {
27802804
* @return mixed|WP_Error The sanitized value or a WP_Error instance if the value cannot be safely sanitized.
27812805
*/
27822806
function rest_sanitize_value_from_schema( $value, $args, $param = '' ) {
2807+
27832808
if ( isset( $args['anyOf'] ) ) {
27842809
$matching_schema = rest_find_any_matching_schema( $value, $args, $param );
27852810
if ( is_wp_error( $matching_schema ) ) {
@@ -2833,6 +2858,19 @@ function rest_sanitize_value_from_schema( $value, $args, $param = '' ) {
28332858
}
28342859

28352860
if ( 'array' === $args['type'] ) {
2861+
/*
2862+
* A JSON-encoded string (e.g. ?ids=[1,2,3]) should be decoded before
2863+
* sanitization. This takes priority over the comma-separated-value
2864+
* fallback in rest_sanitize_array() / wp_parse_list(), which cannot
2865+
* preserve value types.
2866+
*/
2867+
if ( is_string( $value ) && str_starts_with( ltrim( $value ), '[' ) ) {
2868+
$decoded = json_decode( $value, true );
2869+
if ( is_array( $decoded ) && JSON_ERROR_NONE === json_last_error() ) {
2870+
$value = $decoded;
2871+
}
2872+
}
2873+
28362874
$value = rest_sanitize_array( $value );
28372875

28382876
if ( ! empty( $args['items'] ) ) {
@@ -2850,6 +2888,18 @@ function rest_sanitize_value_from_schema( $value, $args, $param = '' ) {
28502888
}
28512889

28522890
if ( 'object' === $args['type'] ) {
2891+
/*
2892+
* A JSON-encoded string (e.g. from a GET query parameter) should be
2893+
* decoded before sanitization, mirroring what parse_json_params() does
2894+
* for application/json request bodies.
2895+
*/
2896+
if ( is_string( $value ) ) {
2897+
$decoded = json_decode( $value, true );
2898+
if ( null !== $decoded && JSON_ERROR_NONE === json_last_error() ) {
2899+
$value = $decoded;
2900+
}
2901+
}
2902+
28532903
$value = rest_sanitize_object( $value );
28542904

28552905
foreach ( $value as $property => $v ) {

0 commit comments

Comments
 (0)