Make WordPress Core


Ignore:
Timestamp:
01/24/2021 04:50:39 PM (4 years ago)
Author:
TimothyBlynJacobs
Message:

REST API: Support type coercion when validating the enum JSON Schema keyword.

Previously, the enum keyword was validated by perform a strict equality check. For string types this is generally ok, but it prevented using alternative types like number when rich type support isn't available.

Now the same level of type coercion/sanitization is applied when validating enum as all other validation checks. This means that a value of "1" will be accepted for an enum of [ 0, 1 ]. Additionally, object types now properly ignore key order when checking for equality.

Props yakimun.
Fixes #51911.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/rest-api.php

    r50007 r50010  
    18731873
    18741874    return $matching_schemas[0]['schema_object'];
     1875}
     1876
     1877/**
     1878 * Checks the equality of two values, following JSON Schema semantics.
     1879 *
     1880 * Property order is ignored for objects.
     1881 *
     1882 * Values must have been previously sanitized/coerced to their native types.
     1883 *
     1884 * @since 5.7.0
     1885 *
     1886 * @param mixed $value1 The first value to check.
     1887 * @param mixed $value2 The second value to check.
     1888 * @return bool True if the values are equal or false otherwise.
     1889 */
     1890function rest_are_values_equal( $value1, $value2 ) {
     1891    if ( is_array( $value1 ) && is_array( $value2 ) ) {
     1892        if ( count( $value1 ) !== count( $value2 ) ) {
     1893            return false;
     1894        }
     1895
     1896        foreach ( $value1 as $index => $value ) {
     1897            if ( ! array_key_exists( $index, $value2 ) || ! rest_are_values_equal( $value, $value2[ $index ] ) ) {
     1898                return false;
     1899            }
     1900        }
     1901
     1902        return true;
     1903    }
     1904
     1905    return $value1 === $value2;
     1906}
     1907
     1908/**
     1909 * Validates that the given value is a member of the JSON Schema "enum".
     1910 *
     1911 * @since 5.7.0
     1912 *
     1913 * @param mixed  $value  The value to validate.
     1914 * @param array  $args   The schema array to use.
     1915 * @param string $param  The parameter name, used in error messages.
     1916 * @return true|WP_Error True if the "enum" contains the value or a WP_Error instance otherwise.
     1917 */
     1918function rest_validate_enum( $value, $args, $param ) {
     1919    $sanitized_value = rest_sanitize_value_from_schema( $value, $args, $param );
     1920    if ( is_wp_error( $sanitized_value ) ) {
     1921        return $sanitized_value;
     1922    }
     1923
     1924    foreach ( $args['enum'] as $enum_value ) {
     1925        if ( rest_are_values_equal( $sanitized_value, $enum_value ) ) {
     1926            return true;
     1927        }
     1928    }
     1929
     1930    $encoded_enum_values = array();
     1931    foreach ( $args['enum'] as $enum_value ) {
     1932        $encoded_enum_values[] = is_scalar( $enum_value ) ? $enum_value : wp_json_encode( $enum_value );
     1933    }
     1934
     1935    if ( count( $encoded_enum_values ) === 1 ) {
     1936        /* translators: 1: Parameter, 2: Valid values. */
     1937        return new WP_Error( 'rest_not_in_enum', wp_sprintf( __( '%1$s is not %2$s.' ), $param, $encoded_enum_values[0] ) );
     1938    }
     1939
     1940    /* translators: 1: Parameter, 2: List of valid values. */
     1941    return new WP_Error( 'rest_not_in_enum', wp_sprintf( __( '%1$s is not one of %2$l.' ), $param, $encoded_enum_values ) );
    18751942}
    18761943
     
    21542221    }
    21552222
    2156     if ( ! empty( $args['enum'] ) ) {
    2157         if ( ! in_array( $value, $args['enum'], true ) ) {
    2158             /* translators: 1: Parameter, 2: List of valid values. */
    2159             return new WP_Error( 'rest_not_in_enum', sprintf( __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) );
    2160         }
    2161     }
    2162 
    21632223    if ( in_array( $args['type'], array( 'integer', 'number' ), true ) ) {
    21642224        if ( ! is_numeric( $value ) ) {
     
    22322292            /* translators: 1: Parameter, 2: Pattern. */
    22332293            return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) );
     2294        }
     2295    }
     2296
     2297    if ( ! empty( $args['enum'] ) ) {
     2298        $enum_contains_value = rest_validate_enum( $value, $args, $param );
     2299        if ( is_wp_error( $enum_contains_value ) ) {
     2300            return $enum_contains_value;
    22342301        }
    22352302    }
Note: See TracChangeset for help on using the changeset viewer.