From 6042d35f1e14a628e8a7abe695a152f0fd9b871e Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 2 Aug 2024 11:55:37 +0300 Subject: [PATCH 1/7] Remove validator --- composer.json | 2 +- src/IpValidator.php | 109 ++++++++++++++++++ src/TrustedHostsNetworkResolver.php | 21 ++-- .../ProcessTest.php | 3 +- .../RuntimeExceptionTest.php | 15 ++- .../TrustedHostsNetworkResolver/TestCase.php | 5 +- 6 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 src/IpValidator.php diff --git a/composer.json b/composer.json index ce0dcf6..8af5157 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", "yiisoft/http": "^1.2", - "yiisoft/validator": "^1.0" + "yiisoft/network-utilities": "^1.0" }, "require-dev": { "httpsoft/http-message": "^1.0", diff --git a/src/IpValidator.php b/src/IpValidator.php new file mode 100644 index 0000000..eb7cd7e --- /dev/null +++ b/src/IpValidator.php @@ -0,0 +1,109 @@ + ['any'], + 'any' => ['0.0.0.0/0', '::/0'], + 'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'], + 'multicast' => ['224.0.0.0/4', 'ff00::/8'], + 'linklocal' => ['169.254.0.0/16', 'fe80::/10'], + 'localhost' => ['127.0.0.0/8', '::1'], + 'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'], + 'system' => ['multicast', 'linklocal', 'localhost', 'documentation'], + ]; + + private const IPV4_PATTERN = '((2(5[0-5]|[0-4]\d)|1\d{2}|[1-9]?\d)\.){3}(2(5[0-5]|[0-4]\d)|1\d{2}|[1-9]?\d)'; + private const IPV6_PATTERN = '(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:' . self::IPV4_PATTERN . ')'; + + public function validate(string $value, bool $allowIpv4 = true): bool + { + if (preg_match('/^' . self::IPV4_PATTERN . '$/', $value) === 1) { + $isIpV4 = true; + } elseif (preg_match('/^' . self::IPV6_PATTERN . '$/', $value) === 1) { + $isIpV4 = false; + } else { + return false; + } + + if ($isIpV4 && !$allowIpv4) { + return false; + } + + return true; + } + + /** + * @param string[] $ranges + */ + public function isAllowed(string $value, array $ranges): bool + { + $ranges = $this->prepareRanges($ranges); + + if (empty($ranges)) { + return true; + } + + foreach ($ranges as $string) { + [$isNegated, $range] = $this->parseNegatedRange($string); + if (IpHelper::inRange($value, $range)) { + return !$isNegated; + } + } + + return false; + } + + /** + * Parses IP address/range for the negation with {@see NEGATION_CHARACTER}. + * + * @return array The result array consists of 2 elements: + * - `boolean`: whether the string is negated + * - `string`: the string without negation (when the negation were present) + * + * @psalm-return array{0: bool, 1: string} + */ + private function parseNegatedRange(string $string): array + { + $isNegated = str_starts_with($string, '!'); + return [$isNegated, $isNegated ? substr($string, 1) : $string]; + } + + /** + * Prepares array to fill in {@see $ranges}: + * + * - Recursively substitutes aliases, described in {@see $networks} with their values. + * - Removes duplicates. + * + * @param string[] $ranges + * + * @return string[] + */ + private function prepareRanges(array $ranges): array + { + $result = []; + foreach ($ranges as $string) { + [$isRangeNegated, $range] = $this->parseNegatedRange($string); + if (isset(self::NETWORKS[$range])) { + $replacements = $this->prepareRanges(self::NETWORKS[$range]); + foreach ($replacements as &$replacement) { + [$isReplacementNegated, $replacement] = $this->parseNegatedRange($replacement); + $result[] = ($isRangeNegated && !$isReplacementNegated ? '!' : '') . $replacement; + } + } else { + $result[] = $string; + } + } + + return array_unique($result); + } +} diff --git a/src/TrustedHostsNetworkResolver.php b/src/TrustedHostsNetworkResolver.php index d55192e..221bc94 100644 --- a/src/TrustedHostsNetworkResolver.php +++ b/src/TrustedHostsNetworkResolver.php @@ -14,8 +14,6 @@ use Yiisoft\Http\HeaderValueHelper; use Yiisoft\ProxyMiddleware\Exception\InvalidConnectionChainItemException; use Yiisoft\ProxyMiddleware\Exception\RfcProxyParseException; -use Yiisoft\Validator\Rule\Ip; -use Yiisoft\Validator\ValidatorInterface; use function count; use function in_array; @@ -146,8 +144,11 @@ class TrustedHostsNetworkResolver implements MiddlewareInterface */ private ?string $connectionChainItemsAttribute = null; - public function __construct(private ValidatorInterface $validator) + private IpValidator $ipValidator; + + public function __construct() { + $this->ipValidator = new IpValidator(); } /** @@ -857,10 +858,7 @@ private function assertReverseObfuscatedIpData(?array $ipData): void */ private function isIp(string $value): bool { - return $this - ->validator - ->validate($value, [new Ip()]) - ->isValid(); + return $this->ipValidator->validate($value); } /** @@ -868,10 +866,7 @@ private function isIp(string $value): bool */ private function isIpv6(string $value): bool { - return $this - ->validator - ->validate($value, [new Ip(allowIpv4: false)]) - ->isValid(); + return $this->ipValidator->validate($value, allowIpv4: false); } /** @@ -879,7 +874,7 @@ private function isIpv6(string $value): bool */ private function isPrivateIp(string $value): bool { - return (new Ip(ranges: ['private']))->isAllowed($value); + return $this->ipValidator->isAllowed($value, ['private']); } /** @@ -887,7 +882,7 @@ private function isPrivateIp(string $value): bool */ private function isTrustedIp(string $value): bool { - return !empty($this->trustedIps) && (new Ip(ranges: $this->trustedIps))->isAllowed($value); + return !empty($this->trustedIps) && $this->ipValidator->isAllowed($value, $this->trustedIps); } /** diff --git a/tests/TrustedHostsNetworkResolver/ProcessTest.php b/tests/TrustedHostsNetworkResolver/ProcessTest.php index ac2c1b0..22f2205 100644 --- a/tests/TrustedHostsNetworkResolver/ProcessTest.php +++ b/tests/TrustedHostsNetworkResolver/ProcessTest.php @@ -9,7 +9,6 @@ use Yiisoft\Http\Status; use Yiisoft\ProxyMiddleware\Tests\Support\MockRequestHandler; use Yiisoft\ProxyMiddleware\TrustedHostsNetworkResolver; -use Yiisoft\Validator\Validator; final class ProcessTest extends TestCase { @@ -1326,7 +1325,7 @@ public function dataProcess(): iterable ], ], yield 'RFC header, IP related data, hidden IP, obfuscated, reverse-obfuscating' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, diff --git a/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php b/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php index 5f7c6e9..8ac15ad 100644 --- a/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php +++ b/tests/TrustedHostsNetworkResolver/RuntimeExceptionTest.php @@ -11,7 +11,6 @@ use Yiisoft\ProxyMiddleware\Exception\RfcProxyParseException; use Yiisoft\ProxyMiddleware\Tests\Support\MockRequestHandler; use Yiisoft\ProxyMiddleware\TrustedHostsNetworkResolver; -use Yiisoft\Validator\Validator; final class RuntimeExceptionTest extends TestCase { @@ -509,7 +508,7 @@ public function dataReverseObfuscateIpIdentifierException(): iterable { return [ yield 'empty array' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -533,7 +532,7 @@ protected function reverseObfuscateIpIdentifier( 'Reverse-obfuscated IP data can\'t be empty.', ], yield 'wrong items count' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -557,7 +556,7 @@ protected function reverseObfuscateIpIdentifier( 'Invalid array keys for reverse-obfuscated IP data. The allowed and required keys are: "0", "1".', ], yield 'IP: not a string' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -581,7 +580,7 @@ protected function reverseObfuscateIpIdentifier( 'IP returned from reverse-obfuscated IP data must be non-empty string.', ], yield 'IP: empty string' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -605,7 +604,7 @@ protected function reverseObfuscateIpIdentifier( 'IP returned from reverse-obfuscated IP data must be non-empty string.', ], yield 'IP: invalid' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -629,7 +628,7 @@ protected function reverseObfuscateIpIdentifier( 'IP returned from reverse-obfuscated IP data is not valid.', ], yield 'port: empty string' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, @@ -653,7 +652,7 @@ protected function reverseObfuscateIpIdentifier( 'Port returned from reverse-obfuscated IP data must be non-empty string.', ], yield 'IP: valid port instead of IP, port: invalid' => [ - (new class (new Validator()) extends TrustedHostsNetworkResolver { + (new class () extends TrustedHostsNetworkResolver { protected function reverseObfuscateIpIdentifier( string $ipIdentifier, array $validatedConnectionChainItems, diff --git a/tests/TrustedHostsNetworkResolver/TestCase.php b/tests/TrustedHostsNetworkResolver/TestCase.php index e62898f..efa2219 100644 --- a/tests/TrustedHostsNetworkResolver/TestCase.php +++ b/tests/TrustedHostsNetworkResolver/TestCase.php @@ -8,15 +8,12 @@ use PHPUnit\Framework\TestCase as PHPUnitTestCase; use Psr\Http\Message\ServerRequestInterface; use Yiisoft\ProxyMiddleware\TrustedHostsNetworkResolver; -use Yiisoft\Validator\Validator; class TestCase extends PHPUnitTestCase { protected function createMiddleware(): TrustedHostsNetworkResolver { - $validator = new Validator(); - - return new TrustedHostsNetworkResolver($validator); + return new TrustedHostsNetworkResolver(); } protected function createRequest( From 2ae899a0398fb709008a01d1a278e2e87dcfecd6 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Fri, 2 Aug 2024 11:57:26 +0300 Subject: [PATCH 2/7] improve --- src/IpValidator.php | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/IpValidator.php b/src/IpValidator.php index eb7cd7e..f78ea8b 100644 --- a/src/IpValidator.php +++ b/src/IpValidator.php @@ -22,14 +22,11 @@ final class IpValidator 'system' => ['multicast', 'linklocal', 'localhost', 'documentation'], ]; - private const IPV4_PATTERN = '((2(5[0-5]|[0-4]\d)|1\d{2}|[1-9]?\d)\.){3}(2(5[0-5]|[0-4]\d)|1\d{2}|[1-9]?\d)'; - private const IPV6_PATTERN = '(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:' . self::IPV4_PATTERN . ')'; - public function validate(string $value, bool $allowIpv4 = true): bool { - if (preg_match('/^' . self::IPV4_PATTERN . '$/', $value) === 1) { + if (preg_match('/^' . IpHelper::IPV4_PATTERN . '$/', $value) === 1) { $isIpV4 = true; - } elseif (preg_match('/^' . self::IPV6_PATTERN . '$/', $value) === 1) { + } elseif (preg_match('/^' . IpHelper::IPV6_PATTERN . '$/', $value) === 1) { $isIpV4 = false; } else { return false; @@ -63,21 +60,6 @@ public function isAllowed(string $value, array $ranges): bool return false; } - /** - * Parses IP address/range for the negation with {@see NEGATION_CHARACTER}. - * - * @return array The result array consists of 2 elements: - * - `boolean`: whether the string is negated - * - `string`: the string without negation (when the negation were present) - * - * @psalm-return array{0: bool, 1: string} - */ - private function parseNegatedRange(string $string): array - { - $isNegated = str_starts_with($string, '!'); - return [$isNegated, $isNegated ? substr($string, 1) : $string]; - } - /** * Prepares array to fill in {@see $ranges}: * @@ -106,4 +88,19 @@ private function prepareRanges(array $ranges): array return array_unique($result); } + + /** + * Parses IP address/range for the negation with `!`. + * + * @return array The result array consists of 2 elements: + * - `boolean`: whether the string is negated + * - `string`: the string without negation (when the negation were present) + * + * @psalm-return array{0: bool, 1: string} + */ + private function parseNegatedRange(string $string): array + { + $isNegated = str_starts_with($string, '!'); + return [$isNegated, $isNegated ? substr($string, 1) : $string]; + } } From ce31703f84c172c59ef20fc0c062d9bab88eeca7 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 6 Aug 2024 10:22:01 +0300 Subject: [PATCH 3/7] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0698cff..3437b31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 1.0.2 under development -- no changes in this release. +- Enh #42: Replace `yiisoft/validator` dependency to `yiisoft/network-utilities` (@vjik) ## 1.0.1 June 02, 2024 From 61ddc0c31e61432af4424a8522e2b125c61102bd Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 6 Aug 2024 10:30:25 +0300 Subject: [PATCH 4/7] refactor --- composer.json | 2 +- src/IpValidator.php | 89 ++++------------------------- src/TrustedHostsNetworkResolver.php | 30 +++------- 3 files changed, 18 insertions(+), 103 deletions(-) diff --git a/composer.json b/composer.json index 8af5157..1414345 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", "yiisoft/http": "^1.2", - "yiisoft/network-utilities": "^1.0" + "yiisoft/network-utilities": "^1.1" }, "require-dev": { "httpsoft/http-message": "^1.0", diff --git a/src/IpValidator.php b/src/IpValidator.php index f78ea8b..7c140c9 100644 --- a/src/IpValidator.php +++ b/src/IpValidator.php @@ -5,102 +5,33 @@ namespace Yiisoft\ProxyMiddleware; use Yiisoft\NetworkUtilities\IpHelper; +use Yiisoft\NetworkUtilities\IpRanges; /** * @internal */ final class IpValidator { - private const NETWORKS = [ - '*' => ['any'], - 'any' => ['0.0.0.0/0', '::/0'], - 'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'], - 'multicast' => ['224.0.0.0/4', 'ff00::/8'], - 'linklocal' => ['169.254.0.0/16', 'fe80::/10'], - 'localhost' => ['127.0.0.0/8', '::1'], - 'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'], - 'system' => ['multicast', 'linklocal', 'localhost', 'documentation'], - ]; - - public function validate(string $value, bool $allowIpv4 = true): bool + public function isIp(string $value): bool { - if (preg_match('/^' . IpHelper::IPV4_PATTERN . '$/', $value) === 1) { - $isIpV4 = true; - } elseif (preg_match('/^' . IpHelper::IPV6_PATTERN . '$/', $value) === 1) { - $isIpV4 = false; - } else { - return false; - } - - if ($isIpV4 && !$allowIpv4) { - return false; - } - - return true; + return $this->isIpV4($value) || $this->isIpV6($value); } - /** - * @param string[] $ranges - */ - public function isAllowed(string $value, array $ranges): bool + public function isIpV4(string $value): bool { - $ranges = $this->prepareRanges($ranges); - - if (empty($ranges)) { - return true; - } - - foreach ($ranges as $string) { - [$isNegated, $range] = $this->parseNegatedRange($string); - if (IpHelper::inRange($value, $range)) { - return !$isNegated; - } - } - - return false; + return preg_match(IpHelper::IPV4_REGEXP, $value) === 1; } - /** - * Prepares array to fill in {@see $ranges}: - * - * - Recursively substitutes aliases, described in {@see $networks} with their values. - * - Removes duplicates. - * - * @param string[] $ranges - * - * @return string[] - */ - private function prepareRanges(array $ranges): array + public function isIpV6(string $value): bool { - $result = []; - foreach ($ranges as $string) { - [$isRangeNegated, $range] = $this->parseNegatedRange($string); - if (isset(self::NETWORKS[$range])) { - $replacements = $this->prepareRanges(self::NETWORKS[$range]); - foreach ($replacements as &$replacement) { - [$isReplacementNegated, $replacement] = $this->parseNegatedRange($replacement); - $result[] = ($isRangeNegated && !$isReplacementNegated ? '!' : '') . $replacement; - } - } else { - $result[] = $string; - } - } - - return array_unique($result); + return preg_match(IpHelper::IPV6_REGEXP, $value) === 1; } /** - * Parses IP address/range for the negation with `!`. - * - * @return array The result array consists of 2 elements: - * - `boolean`: whether the string is negated - * - `string`: the string without negation (when the negation were present) - * - * @psalm-return array{0: bool, 1: string} + * @param string[] $ranges */ - private function parseNegatedRange(string $string): array + public function inRanges(string $value, array $ranges): bool { - $isNegated = str_starts_with($string, '!'); - return [$isNegated, $isNegated ? substr($string, 1) : $string]; + return (new IpRanges($ranges))->isAllowed($value); } } diff --git a/src/TrustedHostsNetworkResolver.php b/src/TrustedHostsNetworkResolver.php index 221bc94..2b99806 100644 --- a/src/TrustedHostsNetworkResolver.php +++ b/src/TrustedHostsNetworkResolver.php @@ -12,6 +12,7 @@ use Psr\Http\Server\RequestHandlerInterface; use RuntimeException; use Yiisoft\Http\HeaderValueHelper; +use Yiisoft\NetworkUtilities\IpRanges; use Yiisoft\ProxyMiddleware\Exception\InvalidConnectionChainItemException; use Yiisoft\ProxyMiddleware\Exception\RfcProxyParseException; @@ -128,7 +129,6 @@ class TrustedHostsNetworkResolver implements MiddlewareInterface /** * @psalm-var list - * @psalm-suppress PropertyNotSetInConstructor */ private array $trustedIps = []; /** @@ -166,7 +166,7 @@ public function withTrustedIps(array $trustedIps): self foreach ($trustedIps as $ip) { $this->assertIsNonEmptyString($ip, 'Trusted IP'); - if (!$this->isIp($ip)) { + if (!$this->ipValidator->isIp($ip)) { throw new InvalidArgumentException("\"$ip\" is not a valid IP."); } @@ -673,7 +673,7 @@ private function parseProxiesFromRfcHeader(array $proxyItems): array if (isset($matches['ipv6']) && !empty($matches['ipv6'])) { $ip = $matches['ipv6']; - if (!$this->isIpv6($ip)) { + if (!$this->ipValidator->isIpV6($ip)) { $message = "Enclosing in square brackets assumes presence of valid IPv6, \"$ip\" given."; throw new RfcProxyParseException($message); @@ -723,7 +723,7 @@ private function getConnectionChainItem( bool $validateIp = true, bool $validateProtocol = true, ): array { - if ($ip !== null && $validateIp && !$this->isIp($ip)) { + if ($ip !== null && $validateIp && !$this->ipValidator->isIp($ip)) { throw new InvalidConnectionChainItemException("\"$ip\" is not a valid IP."); } @@ -848,33 +848,17 @@ private function assertReverseObfuscatedIpData(?array $ipData): void } } - if (!$this->isIp($ipData[0])) { + if (!$this->ipValidator->isIp($ipData[0])) { throw new RuntimeException('IP returned from reverse-obfuscated IP data is not valid.'); } } - /** - * @psalm-assert non-empty-string $value - */ - private function isIp(string $value): bool - { - return $this->ipValidator->validate($value); - } - - /** - * @psalm-assert non-empty-string $value - */ - private function isIpv6(string $value): bool - { - return $this->ipValidator->validate($value, allowIpv4: false); - } - /** * @psalm-param non-empty-string $value */ private function isPrivateIp(string $value): bool { - return $this->ipValidator->isAllowed($value, ['private']); + return $this->ipValidator->inRanges($value, [IpRanges::PRIVATE]); } /** @@ -882,7 +866,7 @@ private function isPrivateIp(string $value): bool */ private function isTrustedIp(string $value): bool { - return !empty($this->trustedIps) && $this->ipValidator->isAllowed($value, $this->trustedIps); + return !empty($this->trustedIps) && $this->ipValidator->inRanges($value, $this->trustedIps); } /** From eb9234649a718cf8b5873b37d6c24a653a49843e Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 6 Aug 2024 10:32:42 +0300 Subject: [PATCH 5/7] fix --- src/IpValidator.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/IpValidator.php b/src/IpValidator.php index 7c140c9..d8a6cc6 100644 --- a/src/IpValidator.php +++ b/src/IpValidator.php @@ -17,11 +17,6 @@ public function isIp(string $value): bool return $this->isIpV4($value) || $this->isIpV6($value); } - public function isIpV4(string $value): bool - { - return preg_match(IpHelper::IPV4_REGEXP, $value) === 1; - } - public function isIpV6(string $value): bool { return preg_match(IpHelper::IPV6_REGEXP, $value) === 1; @@ -34,4 +29,9 @@ public function inRanges(string $value, array $ranges): bool { return (new IpRanges($ranges))->isAllowed($value); } + + private function isIpV4(string $value): bool + { + return preg_match(IpHelper::IPV4_REGEXP, $value) === 1; + } } From 7fbeafe4faa8242c2b9bfe202129917081977bc4 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 6 Aug 2024 13:56:00 +0300 Subject: [PATCH 6/7] use static --- src/IpValidator.php | 10 +++++----- src/TrustedHostsNetworkResolver.php | 19 ++++++------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/src/IpValidator.php b/src/IpValidator.php index d8a6cc6..05d9f13 100644 --- a/src/IpValidator.php +++ b/src/IpValidator.php @@ -12,12 +12,12 @@ */ final class IpValidator { - public function isIp(string $value): bool + public static function isIp(string $value): bool { - return $this->isIpV4($value) || $this->isIpV6($value); + return self::isIpV4($value) || self::isIpV6($value); } - public function isIpV6(string $value): bool + public static function isIpV6(string $value): bool { return preg_match(IpHelper::IPV6_REGEXP, $value) === 1; } @@ -25,12 +25,12 @@ public function isIpV6(string $value): bool /** * @param string[] $ranges */ - public function inRanges(string $value, array $ranges): bool + public static function inRanges(string $value, array $ranges): bool { return (new IpRanges($ranges))->isAllowed($value); } - private function isIpV4(string $value): bool + private static function isIpV4(string $value): bool { return preg_match(IpHelper::IPV4_REGEXP, $value) === 1; } diff --git a/src/TrustedHostsNetworkResolver.php b/src/TrustedHostsNetworkResolver.php index 2b99806..1f20e9b 100644 --- a/src/TrustedHostsNetworkResolver.php +++ b/src/TrustedHostsNetworkResolver.php @@ -144,13 +144,6 @@ class TrustedHostsNetworkResolver implements MiddlewareInterface */ private ?string $connectionChainItemsAttribute = null; - private IpValidator $ipValidator; - - public function __construct() - { - $this->ipValidator = new IpValidator(); - } - /** * Returns a new instance with changed list of connection chain trusted IPs * @@ -166,7 +159,7 @@ public function withTrustedIps(array $trustedIps): self foreach ($trustedIps as $ip) { $this->assertIsNonEmptyString($ip, 'Trusted IP'); - if (!$this->ipValidator->isIp($ip)) { + if (!IpValidator::isIp($ip)) { throw new InvalidArgumentException("\"$ip\" is not a valid IP."); } @@ -673,7 +666,7 @@ private function parseProxiesFromRfcHeader(array $proxyItems): array if (isset($matches['ipv6']) && !empty($matches['ipv6'])) { $ip = $matches['ipv6']; - if (!$this->ipValidator->isIpV6($ip)) { + if (!IpValidator::isIpV6($ip)) { $message = "Enclosing in square brackets assumes presence of valid IPv6, \"$ip\" given."; throw new RfcProxyParseException($message); @@ -723,7 +716,7 @@ private function getConnectionChainItem( bool $validateIp = true, bool $validateProtocol = true, ): array { - if ($ip !== null && $validateIp && !$this->ipValidator->isIp($ip)) { + if ($ip !== null && $validateIp && !IpValidator::isIp($ip)) { throw new InvalidConnectionChainItemException("\"$ip\" is not a valid IP."); } @@ -848,7 +841,7 @@ private function assertReverseObfuscatedIpData(?array $ipData): void } } - if (!$this->ipValidator->isIp($ipData[0])) { + if (!IpValidator::isIp($ipData[0])) { throw new RuntimeException('IP returned from reverse-obfuscated IP data is not valid.'); } } @@ -858,7 +851,7 @@ private function assertReverseObfuscatedIpData(?array $ipData): void */ private function isPrivateIp(string $value): bool { - return $this->ipValidator->inRanges($value, [IpRanges::PRIVATE]); + return IpValidator::inRanges($value, [IpRanges::PRIVATE]); } /** @@ -866,7 +859,7 @@ private function isPrivateIp(string $value): bool */ private function isTrustedIp(string $value): bool { - return !empty($this->trustedIps) && $this->ipValidator->inRanges($value, $this->trustedIps); + return !empty($this->trustedIps) && IpValidator::inRanges($value, $this->trustedIps); } /** From 32c35413ca7ae8b7a4d9ce694c13d8e19309b044 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 6 Aug 2024 14:48:40 +0300 Subject: [PATCH 7/7] Fix BC break --- src/TrustedHostsNetworkResolver.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/TrustedHostsNetworkResolver.php b/src/TrustedHostsNetworkResolver.php index 1f20e9b..444b4dd 100644 --- a/src/TrustedHostsNetworkResolver.php +++ b/src/TrustedHostsNetworkResolver.php @@ -144,6 +144,10 @@ class TrustedHostsNetworkResolver implements MiddlewareInterface */ private ?string $connectionChainItemsAttribute = null; + public function __construct() + { + } + /** * Returns a new instance with changed list of connection chain trusted IPs *