From 55d12d135096427f72ba81fb673ff8ffbe137dcf Mon Sep 17 00:00:00 2001 From: George Steel Date: Fri, 14 Jun 2024 15:38:58 +0100 Subject: [PATCH] Replace dated and flawed reserved ip checks in Email validator Removes the regexes used to validate reserved/private ips in favour of using the `HostWithPublicIPv4Address` validator Signed-off-by: George Steel --- phpunit.xml.dist | 1 - src/EmailAddress.php | 58 +-------- test/EmailAddressTest.php | 117 +++--------------- .../EmailValidatorWithExposedIsReserved.php | 22 ---- 4 files changed, 20 insertions(+), 178 deletions(-) delete mode 100644 test/TestAsset/EmailValidatorWithExposedIsReserved.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index b67401222..993202e27 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -24,7 +24,6 @@ - diff --git a/src/EmailAddress.php b/src/EmailAddress.php index ac34ea11b..c65e5f02e 100644 --- a/src/EmailAddress.php +++ b/src/EmailAddress.php @@ -280,67 +280,15 @@ public function useDomainCheck($domain = true) } /** - * Returns if the given host is reserved - * - * The following addresses are seen as reserved - * '0.0.0.0/8', '10.0.0.0/8', '127.0.0.0/8' - * '100.64.0.0/10' - * '172.16.0.0/12' - * '198.18.0.0/15' - * '169.254.0.0/16', '192.168.0.0/16' - * '192.0.2.0/24', '192.88.99.0/24', '198.51.100.0/24', '203.0.113.0/24' - * '224.0.0.0/4', '240.0.0.0/4' - * - * @see http://en.wikipedia.org/wiki/Reserved_IP_addresses - * - * As of RFC5753 (JAN 2010), the following blocks are no longer reserved: - * - 128.0.0.0/16 - * - 191.255.0.0/16 - * - 223.255.255.0/24 - * @see http://tools.ietf.org/html/rfc5735#page-6 - * - * As of RFC6598 (APR 2012), the following blocks are now reserved: - * - 100.64.0.0/10 - * @see http://tools.ietf.org/html/rfc6598#section-7 + * Returns whether the given host is a reserved IP, or a hostname that resolves to a reserved IP * * @param string $host * @return bool Returns false when minimal one of the given addresses is not reserved */ protected function isReserved($host) { - if (! preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $host)) { - $host = gethostbynamel($host); - } else { - $host = [$host]; - } - - if (! is_array($host) || $host === []) { - return false; - } - - foreach ($host as $server) { - // @codingStandardsIgnoreStart - // Search for 0.0.0.0/8, 10.0.0.0/8, 127.0.0.0/8 - if (!preg_match('/^(0|10|127)(\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))){3}$/', $server) && - // Search for 100.64.0.0/10 - !preg_match('/^100\.(6[0-4]|[7-9][0-9]|1[0-1][0-9]|12[0-7])(\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))){2}$/', $server) && - // Search for 172.16.0.0/12 - !preg_match('/^172\.(1[6-9]|2[0-9]|3[0-1])(\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))){2}$/', $server) && - // Search for 198.18.0.0/15 - !preg_match('/^198\.(1[8-9])(\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))){2}$/', $server) && - // Search for 169.254.0.0/16, 192.168.0.0/16 - !preg_match('/^(169\.254|192\.168)(\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))){2}$/', $server) && - // Search for 192.0.2.0/24, 192.88.99.0/24, 198.51.100.0/24, 203.0.113.0/24 - !preg_match('/^(192\.0\.2|192\.88\.99|198\.51\.100|203\.0\.113)\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))$/', $server) && - // Search for 224.0.0.0/4, 240.0.0.0/4 - !preg_match('/^(2(2[4-9]|[3-4][0-9]|5[0-5]))(\.([0-9]|[1-9][0-9]|1([0-9][0-9])|2([0-4][0-9]|5[0-5]))){3}$/', $server) - ) { - return false; - } - // @codingStandardsIgnoreEnd - } - - return true; + $validator = new HostWithPublicIPv4Address(); + return ! $validator->isValid($host); } /** diff --git a/test/EmailAddressTest.php b/test/EmailAddressTest.php index d7178a521..41d1b721c 100644 --- a/test/EmailAddressTest.php +++ b/test/EmailAddressTest.php @@ -8,6 +8,7 @@ use Laminas\Validator\Exception\InvalidArgumentException; use Laminas\Validator\Hostname; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\TestCase; @@ -17,7 +18,6 @@ use function count; use function current; use function extension_loaded; -use function getenv; use function implode; use function next; use function preg_replace; @@ -259,8 +259,8 @@ public function testBasicValid(string $value): void sprintf( '%s failed validation: %s', $value, - implode("\n", $this->validator->getMessages()) - ) + implode("\n", $this->validator->getMessages()), + ), ); } @@ -359,8 +359,6 @@ public static function emailAddressesForMxChecks(): array #[DataProvider('emailAddressesForMxChecks')] public function testMXRecords(string $emailAddress, bool $expect): void { - $this->skipIfOnlineTestsDisabled(); - $validator = new EmailAddress([ 'allow' => Hostname::ALLOW_DNS, 'useMxCheck' => true, @@ -389,8 +387,6 @@ public function testMXRecords(string $emailAddress, bool $expect): void */ public function testNoMxRecordARecordFallback(): void { - $this->skipIfOnlineTestsDisabled(); - $validator = new EmailAddress([ 'allow' => Hostname::ALLOW_DNS, 'useMxCheck' => true, @@ -700,13 +696,8 @@ public function testIsMxSupported(): void self::assertIsBool($validator->isMxSupported()); } - /** - * Test getMXRecord - */ public function testGetMXRecord(): void { - $this->skipIfOnlineTestsDisabled(); - $validator = new EmailAddress(['useMxCheck' => true, 'allow' => Hostname::ALLOW_ALL]); if (! $validator->isMxSupported()) { @@ -734,7 +725,7 @@ public function testEqualsMessageTemplates(): void EmailAddress::INVALID_LOCAL_PART, EmailAddress::LENGTH_EXCEEDED, ], - array_keys($this->validator->getMessageTemplates()) + array_keys($this->validator->getMessageTemplates()), ); self::assertSame($this->validator->getOption('messageTemplates'), $this->validator->getMessageTemplates()); } @@ -753,8 +744,6 @@ public function testEqualsMessageVariables(): void #[Group('Laminas-130')] public function testUseMxCheckBasicValid(): void { - $this->skipIfOnlineTestsDisabled(); - $validator = new EmailAddress([ 'useMxCheck' => true, 'useDeepMxCheck' => true, @@ -823,95 +812,23 @@ public function testUseMxRecordsBasicInvalid(): void } } - #[Group('Laminas-12349')] - public function testReservedIpRangeValidation(): void - { - $validator = new TestAsset\EmailValidatorWithExposedIsReserved(); - - // 0.0.0.0/8 - self::assertTrue($validator->isReserved('0.0.0.0')); - self::assertTrue($validator->isReserved('0.255.255.255')); - - // 10.0.0.0/8 - self::assertTrue($validator->isReserved('10.0.0.0')); - self::assertTrue($validator->isReserved('10.255.255.255')); - - // 127.0.0.0/8 - self::assertTrue($validator->isReserved('127.0.0.0')); - self::assertTrue($validator->isReserved('127.255.255.255')); - - // 100.64.0.0/10 - self::assertTrue($validator->isReserved('100.64.0.0')); - self::assertTrue($validator->isReserved('100.127.255.255')); - - // 172.16.0.0/12 - self::assertTrue($validator->isReserved('172.16.0.0')); - self::assertTrue($validator->isReserved('172.31.255.255')); - - // 198.18.0.0./15 - self::assertTrue($validator->isReserved('198.18.0.0')); - self::assertTrue($validator->isReserved('198.19.255.255')); - - // 169.254.0.0/16 - self::assertTrue($validator->isReserved('169.254.0.0')); - self::assertTrue($validator->isReserved('169.254.255.255')); - - // 192.168.0.0/16 - self::assertTrue($validator->isReserved('192.168.0.0')); - self::assertTrue($validator->isReserved('192.168.255.25')); - - // 192.0.2.0/24 - self::assertTrue($validator->isReserved('192.0.2.0')); - self::assertTrue($validator->isReserved('192.0.2.255')); - - // 192.88.99.0/24 - self::assertTrue($validator->isReserved('192.88.99.0')); - self::assertTrue($validator->isReserved('192.88.99.255')); - - // 198.51.100.0/24 - self::assertTrue($validator->isReserved('198.51.100.0')); - self::assertTrue($validator->isReserved('198.51.100.255')); - - // 203.0.113.0/24 - self::assertTrue($validator->isReserved('203.0.113.0')); - self::assertTrue($validator->isReserved('203.0.113.255')); - - // 224.0.0.0/4 - self::assertTrue($validator->isReserved('224.0.0.0')); - self::assertTrue($validator->isReserved('239.255.255.255')); - - // 240.0.0.0/4 - self::assertTrue($validator->isReserved('240.0.0.0')); - self::assertTrue($validator->isReserved('255.255.255.254')); - - // 255.255.255.255/32 - self::assertTrue($validator->isReserved('255.255.55.255')); - } - - #[Group('Laminas-12349')] - public function testIpRangeValidationOnRangesNoLongerMarkedAsReserved(): void + public function testRootAtLocalhostIsValid(): void { - $validator = new TestAsset\EmailValidatorWithExposedIsReserved(); - - // 128.0.0.0/16 - self::assertFalse($validator->isReserved('128.0.0.0')); - self::assertFalse($validator->isReserved('128.0.255.255')); - - // 191.255.0.0/16 - self::assertFalse($validator->isReserved('191.255.0.0')); - self::assertFalse($validator->isReserved('191.255.255.255')); - - // 223.255.255.0/24 - self::assertFalse($validator->isReserved('223.255.255.0')); - self::assertFalse($validator->isReserved('223.255.255.255')); + $validator = new EmailAddress([ + 'allow' => Hostname::ALLOW_ALL, + ]); + self::assertTrue($validator->isValid('root@localhost')); } - private function skipIfOnlineTestsDisabled(): void + #[Depends('testRootAtLocalhostIsValid')] + public function testRootAtLocalhostIsNotValidWhenDeepMxChecksAreActive(): void { - $enabled = getenv('TESTS_LAMINAS_VALIDATOR_ONLINE_ENABLED'); - if ($enabled === false || $enabled === '') { - self::markTestSkipped('Testing MX records has been disabled'); - } + $validator = new EmailAddress([ + 'allow' => Hostname::ALLOW_ALL, + 'useMxCheck' => true, + 'useDeepMxCheck' => true, + ]); + self::assertFalse($validator->isValid('root@localhost')); } public function testCanSetDomainCheckFlag(): void diff --git a/test/TestAsset/EmailValidatorWithExposedIsReserved.php b/test/TestAsset/EmailValidatorWithExposedIsReserved.php deleted file mode 100644 index ca292647d..000000000 --- a/test/TestAsset/EmailValidatorWithExposedIsReserved.php +++ /dev/null @@ -1,22 +0,0 @@ -