Skip to content

Commit

Permalink
Class Ipv6: add input validation
Browse files Browse the repository at this point in the history
The documented accepted parameter type for all methods in the `Ipv6` class is `string`, but no input validation was done on the parameter, which could lead to various PHP errors, most notably a "passing null to non-nullable" deprecation notice on PHP 8.1.

This commit adds input validation to all public methods in the class, allowing only for strings and _stringable_ objects.

As this class was until now only indirectly tested via the `IriTest` class, a new `Ipv6Test` class is being introduced containing - for now - only perfunctory tests for the input validation.
This test class should be expanded to cover the actual logic in the methods at a later date.

**Open questions:**
* It is up for discussion whether _stringable objects_ should even be allowed as input for these methods. Opinions welcome.
* As both the `compress()` as well as the `check_ipv6()` method call the `uncompress()` method at the very start of the method logic, it could be argued that adding input validation to those methods is not needed as adding input validation to `uncompress()` is sufficient.
    I've chosen to add the (duplicate) input validation anyway as it will make the error message more descriptive by pointing to the actual method which initially received the invalid input.
    Again: opinions welcome.
  • Loading branch information
jrfnl committed Nov 7, 2021
1 parent 96f082a commit 53511fc
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/Ipv6.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

namespace WpOrg\Requests;

use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Utility\InputValidator;

/**
* Class to validate and to work with IPv6 addresses
*
Expand All @@ -33,10 +36,19 @@ final class Ipv6 {
* @author Josh Peck <jmp at joshpeck dot org>
* @copyright 2003-2005 The PHP Group
* @license https://opensource.org/licenses/bsd-license.php
*
* @param string $ip An IPv6 address
* @return string The uncompressed IPv6 address
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
*/
public static function uncompress($ip) {
if (InputValidator::is_string_or_stringable($ip) === false) {
throw InvalidArgument::create(1, '$ip', 'string|Stringable', gettype($ip));
}

$ip = (string) $ip;

if (substr_count($ip, '::') !== 1) {
return $ip;
}
Expand Down Expand Up @@ -81,10 +93,17 @@ public static function uncompress($ip) {
* 0:0:0:0:0:0:0:1 -> ::1
*
* @see \WpOrg\Requests\IPv6::uncompress()
*
* @param string $ip An IPv6 address
* @return string The compressed IPv6 address
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
*/
public static function compress($ip) {
if (InputValidator::is_string_or_stringable($ip) === false) {
throw InvalidArgument::create(1, '$ip', 'string|Stringable', gettype($ip));
}

// Prepare the IP to be compressed
$ip = self::uncompress($ip);
$ip_parts = self::split_v6_v4($ip);
Expand Down Expand Up @@ -145,8 +164,14 @@ private static function split_v6_v4($ip) {
*
* @param string $ip An IPv6 address
* @return bool true if $ip is a valid IPv6 address
*
* @throws \WpOrg\Requests\Exception\InvalidArgument When the passed argument is not a string or a stringable object.
*/
public static function check_ipv6($ip) {
if (InputValidator::is_string_or_stringable($ip) === false) {
throw InvalidArgument::create(1, '$ip', 'string|Stringable', gettype($ip));
}

$ip = self::uncompress($ip);
list($ipv6, $ipv4) = self::split_v6_v4($ip);
$ipv6 = explode(':', $ipv6);
Expand Down
138 changes: 138 additions & 0 deletions tests/Ipv6Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<?php

namespace WpOrg\Requests\Tests;

use WpOrg\Requests\Exception\InvalidArgument;
use WpOrg\Requests\Ipv6;
use WpOrg\Requests\Tests\Fixtures\StringableObject;
use WpOrg\Requests\Tests\TestCase;

/**
* Test for the Ipv6 class.
*
* Note: the "valid input type" tests can be removed once actual tests for the functionality
* of the methods have been added.
*
* @coversDefaultClass \WpOrg\Requests\Ipv6
*/
final class Ipv6Test extends TestCase {

/**
* Tests that the Ipv6::uncompress() method accepts string/stringable as an $ip parameter.
*
* @covers ::uncompress
* @dataProvider dataValidInputType
*
* @param string $ip An IPv6 address.
*
* @return void
*/
public function testUncompressValidInputType($ip) {
$this->assertIsString(Ipv6::uncompress($ip));
}

/**
* Tests that the Ipv6::compress() method accepts string/stringable as an $ip parameter.
*
* @covers ::compress
* @dataProvider dataValidInputType
*
* @param string $ip An IPv6 address.
*
* @return void
*/
public function testCompressValidInputType($ip) {
$this->assertIsString(Ipv6::compress($ip));
}

/**
* Tests that the Ipv6::check_ipv6() method accepts string/stringable as an $ip parameter.
*
* @covers ::check_ipv6
* @dataProvider dataValidInputType
*
* @param string $ip An IPv6 address
*
* @return void
*/
public function testCheckIpv6ValidInputType($ip) {
$this->assertIsBool(Ipv6::check_ipv6($ip));
}

/**
* Data Provider.
*
* @return array
*/
public function dataValidInputType() {
return array(
'string' => array('::1'),
'stringable' => array(new StringableObject('0:1234:dc0:41:216:3eff:fe67:3e01')),
);
}

/**
* Tests receiving an exception when an invalid input type is passed to the Ipv6::uncompress() method.
*
* @covers ::uncompress
* @dataProvider dataInvalidInputType
*
* @param mixed $ip Parameter to test input validation with.
*
* @return void
*/
public function testUncompressInvalidInputType($ip) {
$this->expectException(InvalidArgument::class);
$this->expectExceptionMessage('Argument #1 ($ip) must be of type string');

Ipv6::uncompress($ip);
}

/**
* Tests receiving an exception when an invalid input type is passed to the Ipv6::compress() method.
*
* @covers ::compress
* @dataProvider dataInvalidInputType
*
* @param mixed $ip Parameter to test input validation with.
*
* @return void
*/
public function testCompressInvalidInputType($ip) {
$this->expectException(InvalidArgument::class);
$this->expectExceptionMessage('Argument #1 ($ip) must be of type string');

Ipv6::compress($ip);
}

/**
* Tests receiving an exception when an invalid input type is passed to the Ipv6::check_ipv6() method.
*
* @covers ::check_ipv6
* @dataProvider dataInvalidInputType
*
* @param mixed $ip Parameter to test input validation with.
*
* @return void
*/
public function testCheckIpv6InvalidInputType($ip) {
$this->expectException(InvalidArgument::class);
$this->expectExceptionMessage('Argument #1 ($ip) must be of type string');

Ipv6::check_ipv6($ip);
}

/**
* Data Provider.
*
* @return array
*/
public function dataInvalidInputType() {
return array(
'null' => array(null),
'boolean false' => array(false),
'integer' => array(12345),
'array' => array(array(1, 2, 3)),
);
}
}

0 comments on commit 53511fc

Please sign in to comment.