From ebcaeeafc48b69f497f82b9700ddf54bfe975f71 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 25 Oct 2024 10:13:01 +0200 Subject: [PATCH 1/3] [HttpClient] Filter private IPs before connecting when Host == IP --- NoPrivateNetworkHttpClient.php | 13 +++++++++++- Tests/NoPrivateNetworkHttpClientTest.php | 27 ++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/NoPrivateNetworkHttpClient.php b/NoPrivateNetworkHttpClient.php index 757a9e8..c252fce 100644 --- a/NoPrivateNetworkHttpClient.php +++ b/NoPrivateNetworkHttpClient.php @@ -77,9 +77,20 @@ public function request(string $method, string $url, array $options = []): Respo } $subnets = $this->subnets; + $lastUrl = ''; $lastPrimaryIp = ''; - $options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastPrimaryIp): void { + $options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastUrl, &$lastPrimaryIp): void { + if ($info['url'] !== $lastUrl) { + $host = trim(parse_url($info['url'], PHP_URL_HOST) ?: '', '[]'); + + if ($host && IpUtils::checkIp($host, $subnets ?? self::PRIVATE_SUBNETS)) { + throw new TransportException(sprintf('Host "%s" is blocked for "%s".', $host, $info['url'])); + } + + $lastUrl = $info['url']; + } + if ($info['primary_ip'] !== $lastPrimaryIp) { if ($info['primary_ip'] && IpUtils::checkIp($info['primary_ip'], $subnets ?? self::PRIVATE_SUBNETS)) { throw new TransportException(sprintf('IP "%s" is blocked for "%s".', $info['primary_ip'], $info['url'])); diff --git a/Tests/NoPrivateNetworkHttpClientTest.php b/Tests/NoPrivateNetworkHttpClientTest.php index 8c51e9e..7130c09 100644 --- a/Tests/NoPrivateNetworkHttpClientTest.php +++ b/Tests/NoPrivateNetworkHttpClientTest.php @@ -65,10 +65,10 @@ public static function getExcludeData(): array /** * @dataProvider getExcludeData */ - public function testExclude(string $ipAddr, $subnets, bool $mustThrow) + public function testExcludeByIp(string $ipAddr, $subnets, bool $mustThrow) { $content = 'foo'; - $url = sprintf('http://%s/', 0 < substr_count($ipAddr, ':') ? sprintf('[%s]', $ipAddr) : $ipAddr); + $url = sprintf('http://%s/', strtr($ipAddr, '.:', '--')); if ($mustThrow) { $this->expectException(TransportException::class); @@ -85,6 +85,29 @@ public function testExclude(string $ipAddr, $subnets, bool $mustThrow) } } + /** + * @dataProvider getExcludeData + */ + public function testExcludeByHost(string $ipAddr, $subnets, bool $mustThrow) + { + $content = 'foo'; + $url = sprintf('http://%s/', str_contains($ipAddr, ':') ? sprintf('[%s]', $ipAddr) : $ipAddr); + + if ($mustThrow) { + $this->expectException(TransportException::class); + $this->expectExceptionMessage(sprintf('Host "%s" is blocked for "%s".', $ipAddr, $url)); + } + + $previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content); + $client = new NoPrivateNetworkHttpClient($previousHttpClient, $subnets); + $response = $client->request('GET', $url); + + if (!$mustThrow) { + $this->assertEquals($content, $response->getContent()); + $this->assertEquals(200, $response->getStatusCode()); + } + } + public function testCustomOnProgressCallback() { $ipAddr = '104.26.14.6'; From e9b08469ecd82b5416c3dd9988457530058c1746 Mon Sep 17 00:00:00 2001 From: "Dr. Gianluigi \"Zane\" Zanettini" Date: Tue, 29 Oct 2024 09:30:25 +0100 Subject: [PATCH 2/3] fix for HttpClientDataCollector fails if proc_open is disabled via php.ini . Closes #58700 --- DataCollector/HttpClientDataCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataCollector/HttpClientDataCollector.php b/DataCollector/HttpClientDataCollector.php index 8e85462..8afddcb 100644 --- a/DataCollector/HttpClientDataCollector.php +++ b/DataCollector/HttpClientDataCollector.php @@ -252,7 +252,7 @@ private function escapePayload(string $payload): string { static $useProcess; - if ($useProcess ??= class_exists(Process::class)) { + if ($useProcess ??= function_exists('proc_open') && class_exists(Process::class)) { return (new Process([$payload]))->getCommandLine(); } From aa7bebe6521febb1d828d9217489af604c711271 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 30 Oct 2024 22:35:56 +0100 Subject: [PATCH 3/3] [HttpClient] Fix Process-based escaping in HttpClientDataCollector --- DataCollector/HttpClientDataCollector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataCollector/HttpClientDataCollector.php b/DataCollector/HttpClientDataCollector.php index 8afddcb..a749aa6 100644 --- a/DataCollector/HttpClientDataCollector.php +++ b/DataCollector/HttpClientDataCollector.php @@ -253,7 +253,7 @@ private function escapePayload(string $payload): string static $useProcess; if ($useProcess ??= function_exists('proc_open') && class_exists(Process::class)) { - return (new Process([$payload]))->getCommandLine(); + return substr((new Process(['', $payload]))->getCommandLine(), 3); } if ('\\' === \DIRECTORY_SEPARATOR) {