Skip to content

Commit

Permalink
Merge pull request #258 from gsteel/v2/email-validator-reserved-ips
Browse files Browse the repository at this point in the history
Replace dated and flawed reserved ip checks in Email validator
  • Loading branch information
gsteel committed Jun 14, 2024
2 parents 1e24e70 + 55d12d1 commit 512e133
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 178 deletions.
1 change: 0 additions & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
<php>
<ini name="date.timezone" value="UTC"/>
<ini name="xdebug.max_nesting_level" value="3000"/>
<env name="TESTS_LAMINAS_VALIDATOR_ONLINE_ENABLED" value="true"/>
</php>
<source>
<include>
Expand Down
58 changes: 3 additions & 55 deletions src/EmailAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
117 changes: 17 additions & 100 deletions test/EmailAddressTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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()),
),
);
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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());
}
Expand All @@ -753,8 +744,6 @@ public function testEqualsMessageVariables(): void
#[Group('Laminas-130')]
public function testUseMxCheckBasicValid(): void
{
$this->skipIfOnlineTestsDisabled();

$validator = new EmailAddress([
'useMxCheck' => true,
'useDeepMxCheck' => true,
Expand Down Expand Up @@ -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
Expand Down
22 changes: 0 additions & 22 deletions test/TestAsset/EmailValidatorWithExposedIsReserved.php

This file was deleted.

0 comments on commit 512e133

Please sign in to comment.