From ba5a196d9ee240f34811a9234b0d87e565963f3a Mon Sep 17 00:00:00 2001 From: Joe Hoyle Date: Fri, 18 Nov 2016 19:32:03 +0000 Subject: [PATCH] =?UTF-8?q?REST=20API:=20Change=20=E2=80=9Cipv4=E2=80=9D?= =?UTF-8?q?=20types=20to=20=E2=80=9Cip=E2=80=9D=20to=20support=20ipv6.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop presuming IP address are IPv4, instead make the type “ip” to be agnostic of IP version. This fixes requests with ipv6 addresses for comments in core. Props dd32, schlessera, danielbachhuber. Fixes #38818. git-svn-id: https://develop.svn.wordpress.org/trunk@39296 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/rest-api.php | 22 +++++++++---------- .../class-wp-rest-comments-controller.php | 2 +- .../rest-api/rest-schema-sanitization.php | 11 ++++++++++ .../tests/rest-api/rest-schema-validation.php | 20 +++++++++++++++-- 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 741e76837f..ffc691916f 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -869,23 +869,23 @@ function rest_parse_request_arg( $value, $request, $param ) { } /** - * Determines if a IPv4 address is valid. + * Determines if an IP address is valid. * - * Does not handle IPv6 addresses. + * Handles both IPv4 and IPv6 addresses. * * @since 4.7.0 * - * @param string $ipv4 IP 32-bit address. - * @return string|false The valid IPv4 address, otherwise false. + * @param string $ip IP address. + * @return string|false The valid IP address, otherwise false. */ -function rest_is_ip_address( $ipv4 ) { - $pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/'; +function rest_is_ip_address( $ip ) { + $ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/'; - if ( ! preg_match( $pattern, $ipv4 ) ) { + if ( ! preg_match( $ipv4_pattern, $ip ) && ! Requests_IPv6::check_ipv6( $ip ) ) { return false; } - return $ipv4; + return $ip; } /** @@ -1053,9 +1053,9 @@ function rest_validate_value_from_schema( $value, $args, $param = '' ) { return new WP_Error( 'rest_invalid_email', __( 'Invalid email address.' ) ); } break; - case 'ipv4' : + case 'ip' : if ( ! rest_is_ip_address( $value ) ) { - /* translators: %s: IP address */ + /* translators: %s: IP address */ return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $value ) ); } break; @@ -1156,7 +1156,7 @@ function rest_sanitize_value_from_schema( $value, $args ) { case 'uri' : return esc_url_raw( $value ); - case 'ipv4' : + case 'ip' : return sanitize_text_field( $value ); } } diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php index bfa3159bf1..09223c06b9 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php @@ -1115,7 +1115,7 @@ class WP_REST_Comments_Controller extends WP_REST_Controller { 'author_ip' => array( 'description' => __( 'IP address for the object author.' ), 'type' => 'string', - 'format' => 'ipv4', + 'format' => 'ip', 'context' => array( 'edit' ), 'default' => '127.0.0.1', ), diff --git a/tests/phpunit/tests/rest-api/rest-schema-sanitization.php b/tests/phpunit/tests/rest-api/rest-schema-sanitization.php index 4c523df1e3..e1ae29d3fa 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-sanitization.php +++ b/tests/phpunit/tests/rest-api/rest-schema-sanitization.php @@ -65,6 +65,17 @@ class WP_Test_REST_Schema_Sanitization extends WP_UnitTestCase { $this->assertEquals( 'invalid', rest_sanitize_value_from_schema( 'invalid', $schema ) ); } + public function test_format_ip() { + $schema = array( + 'type' => 'string', + 'format' => 'ip', + ); + + $this->assertEquals( '127.0.0.1', rest_sanitize_value_from_schema( '127.0.0.1', $schema ) ); + $this->assertEquals( 'hello', rest_sanitize_value_from_schema( 'hello', $schema ) ); + $this->assertEquals( '2001:DB8:0:0:8:800:200C:417A', rest_sanitize_value_from_schema( '2001:DB8:0:0:8:800:200C:417A', $schema ) ); + } + public function test_type_array() { $schema = array( 'type' => 'array', diff --git a/tests/phpunit/tests/rest-api/rest-schema-validation.php b/tests/phpunit/tests/rest-api/rest-schema-validation.php index 4e4a111018..f504cb7ee1 100644 --- a/tests/phpunit/tests/rest-api/rest-schema-validation.php +++ b/tests/phpunit/tests/rest-api/rest-schema-validation.php @@ -83,14 +83,30 @@ class WP_Test_REST_Schema_Validation extends WP_UnitTestCase { $this->assertWPError( rest_validate_value_from_schema( '2016-06-30', $schema ) ); } - public function test_format_ipv4() { + public function test_format_ip() { $schema = array( 'type' => 'string', - 'format' => 'ipv4', + 'format' => 'ip', ); + + // IPv4. $this->assertTrue( rest_validate_value_from_schema( '127.0.0.1', $schema ) ); $this->assertWPError( rest_validate_value_from_schema( '3333.3333.3333.3333', $schema ) ); $this->assertWPError( rest_validate_value_from_schema( '1', $schema ) ); + + // IPv6. + $this->assertTrue( rest_validate_value_from_schema( '::1', $schema ) ); // Loopback, compressed, non-routable. + $this->assertTrue( rest_validate_value_from_schema( '::', $schema ) ); // Unspecified, compressed, non-routable. + $this->assertTrue( rest_validate_value_from_schema( '0:0:0:0:0:0:0:1', $schema ) ); // Loopback, full. + $this->assertTrue( rest_validate_value_from_schema( '0:0:0:0:0:0:0:0', $schema ) ); // Unspecified, full. + $this->assertTrue( rest_validate_value_from_schema( '2001:DB8:0:0:8:800:200C:417A', $schema ) ); // Unicast, full. + $this->assertTrue( rest_validate_value_from_schema( 'FF01:0:0:0:0:0:0:101', $schema ) ); // Multicast, full. + $this->assertTrue( rest_validate_value_from_schema( '2001:DB8::8:800:200C:417A', $schema ) ); // Unicast, compressed. + $this->assertTrue( rest_validate_value_from_schema( 'FF01::101', $schema ) ); // Multicast, compressed. + $this->assertTrue( rest_validate_value_from_schema( 'fe80::217:f2ff:fe07:ed62', $schema ) ); + $this->assertWPError( rest_validate_value_from_schema( '', $schema ) ); // Empty string. + $this->assertWPError( rest_validate_value_from_schema( '2001:DB8:0:0:8:800:200C:417A:221', $schema ) ); // Unicast, full. + $this->assertWPError( rest_validate_value_from_schema( 'FF01::101::2', $schema ) ); // Multicast, compressed. } public function test_type_array() {