8000 Merge branch '7.1' into 7.2 · symfony/symfony@dbacea3 · GitHub
[go: up one dir, main page]

Skip to content

Commit dbacea3

Browse files
Merge branch '7.1' into 7.2
* 7.1: fix merge fix merge Bump Symfony version to 7.1.9 Work around parse_url() bug (bis) Update VERSION for 7.1.8 Update CHANGELOG for 7.1.8 Bump Symfony version to 6.4.16 fix PHP 7.2 compatibility silence PHP warnings issued by Redis::connect() Update VERSION for 6.4.15 Update CHANGELOG for 6.4.15 Bump Symfony version to 5.4.48 Update VERSION for 5.4.47 Update CHANGELOG for 5.4.47 [Routing] Fix: lost priority when defining hosts in configuration fix dumping tests to skip with data providers
2 parents 9bb68e3 + 691b2e1 commit dbacea3

File tree

19 files changed

+182
-70
lines changed

19 files changed

+182
-70
lines changed

CHANGELOG-7.1.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ in 7.1 minor versions.
77
To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash
88
To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v7.1.0...v7.1.1
99

10+
* 7.1.8 (2024-11-13)
11+
12+
* security #cve-2024-50342 [HttpClient] Resolve hostnames in NoPrivateNetworkHttpClient (nicolas-grekas)
13+
* security #cve-2024-51996 [Security] Check owner of persisted remember-me cookie (jderusse)
14+
* bug #58799 [String] Fix some spellings in `EnglishInflector` (alexandre-daubois)
15+
* bug #58823 [TwigBridge] Fix emojify as function in Undefined Handler (smnandre)
16+
* bug #56868 [Serializer] fixed object normalizer for a class with `cancel` method (er1z)
17+
* bug #58601 [RateLimiter] Fix bucket size reduced when previously created with bigger size (Orkin)
18+
* bug #58659 [AssetMapper] Fix `JavaScriptImportPathCompiler` regex for non-latin characters (GregRbs92)
19+
* bug #58658 [Twitter][Notifier] Fix post INIT upload (matyo91)
20+
* bug #58705 [Serializer] Revert Default groups (mtarld)
21+
* bug #58763 [Messenger][RateLimiter] fix additional message handled when using a rate limiter (Jean-Beru)
22+
* bug #58791 [RateLimiter] handle error results of DateTime::modify() (xabbuh)
23+
* bug #58804 [Serializer][TypeInfo] fix support for phpstan/phpdoc-parser 2 (xabbuh)
24+
* bug #58800 [PropertyInfo] fix support for phpstan/phpdoc-parser 2 (xabbuh)
25+
1026
* 7.1.7 (2024-11-06)
1127

1228
* bug #58772 [DoctrineBridge] Backport detection fix of Xml/Yaml driver in DoctrineExtension (MatTheCat)

src/Symfony/Bridge/PhpUnit/Legacy/SymfonyTestsListenerTrait.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\Common\Annotations\AnnotationRegistry;
1515
use PHPUnit\Framework\AssertionFailedError;
16+
use PHPUnit\Framework\DataProviderTestSuite;
1617
use PHPUnit\Framework\RiskyTestError;
1718
use PHPUnit\Framework\TestCase;
1819
use PHPUnit\Framework\TestSuite;
@@ -193,7 +194,13 @@ public function startTestSuite($suite): void
193194
public function addSkippedTest($test, \Exception $e, $time): void
194195
{
195196
if (0 < $this->state) {
196-
$this->isSkipped[\get_class($test)][$test->getName()] = 1;
197+
if ($test instanceof DataProviderTestSuite) {
198+
foreach ($test->tests() as $testWithDataProvider) {
199+
$this->isSkipped[\get_class($testWithDataProvider)][$testWithDataProvider->getName()] = 1;
200+
}
201+
} else {
202+
$this->isSkipped[\get_class($test)][$test->getName()] = 1;
203+
}
197204
}
198205
}
199206

src/Symfony/Component/DomCrawler/Tests/UriResolverTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ public static function provideResolverTests()
8787

8888
['http://', 'http://localhost', 'http://'],
8989
['/foo:123', 'http://localhost', 'http://localhost/foo:123'],
90+
['foo:123', 'http://localhost/', 'foo:123'],
91+
['foo/bar:1/baz', 'http://localhost/', 'http://localhost/foo/bar:1/baz'],
9092
];
9193
}
9294
}

src/Symfony/Component/DomCrawler/UriResolver.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,8 @@ public static function resolve(string $uri, ?string $baseUri): string
3232
{
3333
$uri = trim($uri);
3434

35-
if (false === ($scheme = parse_url($uri, \PHP_URL_SCHEME)) && '/' === ($uri[0] ?? '')) {
36-
$scheme = parse_url($uri.'#', \PHP_URL_SCHEME);
37-
}
38-
3935
// absolute URL?
40-
if (null !== $scheme) {
36+
if (null !== parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#', \PHP_URL_SCHEME)) {
4137
return $uri;
4238
}
4339

src/Symfony/Component/HttpClient/CurlHttpClient.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,8 +417,9 @@ private static function createRedirectResolver(array $options, string $host, int
417417
}
418418
}
419419

420-
return static function ($ch, string $location, bool $noContent) use (&$redirectHeaders, $options) {
420+
return static function ($ch, string $location, bool $noContent, bool &$locationHasHost) use (&$redirectHeaders, $options) {
421421
try {
422+
$locationHasHost = false;
422423
$location = self::parseUrl($location);
423424
} catch (InvalidArgumentException) {
424425
return null;
@@ -430,9 +431,11 @@ private static function createRedirectResolver(array $options, string $host, int
430431
$redirectHeaders['with_auth'] = array_filter($redirectHeaders['with_auth'], $filterContentHeaders);
431432
}
432433

433-
if ($redirectHeaders && $host = parse_url('http:'.$location['authority'], \PHP_URL_HOST)) {
434-
$port = parse_url('http:'.$location['authority'], \PHP_URL_PORT) ?: ('http:' === $location['scheme'] ? 80 : 443);
435-
$requestHeaders = $redirectHeaders['host'] === $host && $redirectHeaders['port'] === $port ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
434+
$locationHasHost = isset($location['authority']);
435+
436+
if ($redirectHeaders && $locationHasHost) {
437+
$port = parse_url($location['authority'], \PHP_URL_PORT) ?: ('http:' === $location['scheme'] ? 80 : 443);
438+
$requestHeaders = parse_url($location['authority'], \PHP_URL_HOST) === $redirectHeaders['host'] && $redirectHeaders['port'] === $port ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
436439
curl_setopt($ch, \CURLOPT_HTTPHEADER, $requestHeaders);
437440
} elseif ($noContent && $redirectHeaders) {
438441
curl_setopt($ch, \CURLOPT_HTTPHEADER, $redirectHeaders['with_auth']);

src/Symfony/Component/HttpClient/HttpClientTrait.php

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -635,29 +635,37 @@ private static function parseUrl(string $url, array $query = [], array $allowedS
635635
throw new InvalidArgumentException(\sprintf('Malformed URL "%s": leading/trailing ASCII control characters or spaces are not allowed.', $url));
636636
}
637637

638-
if (false === $parts = parse_url($url)) {
639-
if ('/' !== ($url[0] ?? '') || false === $parts = parse_url($url.'#')) {
640-
throw new InvalidArgumentException(\sprintf('Malformed URL "%s".', $url));
641-
}
642-
unset($parts['fragment']);
638+
$tail = '';
639+
640+
if (false === $parts = parse_url(\strlen($url) !== strcspn($url, '?#') ? $url : $url.$tail = '#')) {
641+
throw new InvalidArgumentException(sprintf('Malformed URL "%s".', $url));
643642
}
644643

645644
if ($query) {
646645
$parts['query'] = self::mergeQueryString($parts['query'] ?? null, $query, true);
647646
}
648647

648+
$scheme = $parts['scheme'] ?? null;
649+
$host = $parts['host'] ?? null;
650+
651+
if (!$scheme && $host && !str_starts_with($url, '//')) {
652+
$parts = parse_url(':/'.$url.$tail);
653+
$parts['path'] = substr($parts['path'], 2);
654+
$scheme = $host = null;
655+
}
656+
649657
$port = $parts['port'] ?? 0;
650658

651-
if (null !== $scheme = $parts['scheme'] ?? null) {
659+
if (null !== $scheme) {
652660
if (!isset($allowedSchemes[$scheme = strtolower($scheme)])) {
653-
throw new InvalidArgumentException(\sprintf('Unsupported scheme in "%s".', $url));
661+
throw new InvalidArgumentException(\sprintf('Unsupported scheme in "%s": "%s" expected.', $url, implode('" or "', array_keys($allowedSchemes))));
654662
}
655663

656664
$port = $allowedSchemes[$scheme] === $port ? 0 : $port;
657665
$scheme .= ':';
658666
}
659667

660-
if (null !== $host = $parts['host'] ?? null) {
668+
if (null !== $host) {
661669
if (!\defined('INTL_IDNA_VARIANT_UTS46') && preg_match('/[\x80-\xFF]/', $host)) {
662670
throw new InvalidArgumentException(\sprintf('Unsupported IDN "%s", try enabling the "intl" PHP extension or running "composer require symfony/polyfill-intl-idn".', $host));
663671
}
@@ -685,7 +693,7 @@ private static function parseUrl(string $url, array $query = [], array $allowedS
685693
'authority' => null !== $host ? '//'.(isset($parts['user']) ? $parts['user'].(isset($parts['pass']) ? ':'.$parts['pass'] : '').'@' : '').$host : null,
686694
'path' => isset($parts['path'][0]) ? $parts['path'] : null,
687695
'query' => isset($parts['query']) ? '?'.$parts['query'] : null,
688-
'fragment' => isset($parts['fragment']) ? '#'.$parts['fragment'] : null,
696+
'fragment' => isset($parts['fragment']) && !$tail ? '#'.$parts['fragment'] : null,
689697
];
690698
}
691699

src/Symfony/Component/HttpClient/NativeHttpClient.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ private static function createRedirectResolver(array $options, string $host, str
389389
return null;
390390
}
391391

392+
$locationHasHost = isset($url['authority']);
392393
$url = self::resolveUrl($url, $info['url']);
393394
$info['redirect_url'] = implode('', $url);
394395

@@ -422,7 +423,7 @@ private static function createRedirectResolver(array $options, string $host, str
422423

423424
[$host, $port] = self::parseHostPort($url, $info);
424425

425-
if (false !== (parse_url($location.'#', \PHP_URL_HOST) ?? false)) {
426+
if ($locationHasHost) {
426427
// Authorization and Cookie headers MUST NOT follow except for the initial host name
427428
$requestHeaders = $redirectHeaders['host'] === $host && $redirectHeaders['port'] === $port ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
428429
$requestHeaders[] = 'Host: '.$host.$port;

src/Symfony/Component/HttpClient/Response/CurlResponse.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,17 +425,18 @@ private static function parseHeaderLine($ch, string $data, array &$info, array &
425425
$info['http_method'] = 'HEAD' === $info['http_method'] ? 'HEAD' : 'GET';
426426
curl_setopt($ch, \CURLOPT_CUSTOMREQUEST, $info['http_method']);
427427
}
428+
$locationHasHost = false;
428429

429-
if (null === $info['redirect_url'] = $resolveRedirect($ch, $location, $noContent)) {
430+
if (null === $info['redirect_url'] = $resolveRedirect($ch, $location, $noContent, $locationHasHost)) {
430431
$options['max_redirects'] = curl_getinfo($ch, \CURLINFO_REDIRECT_COUNT);
431432
curl_setopt($ch, \CURLOPT_FOLLOWLOCATION, false);
432433
curl_setopt($ch, \CURLOPT_MAXREDIRS, $options['max_redirects']);
433-
} else {
434-
$url = parse_url($location ?? ':');
434+
} elseif ($locationHasHost) {
435+
$url = parse_url($info['redirect_url']);
435436

436-
if (isset($url['host']) && null !== $ip = $multi->dnsCache->hostnames[$url['host'] = strtolower($url['host'])] ?? null) {
437+
if (null !== $ip = $multi->dnsCache->hostnames[$url['host'] = strtolower($url['host'])] ?? null) {
437438
// Populate DNS cache for redirects if needed
438-
$port = $url['port'] ?? ('http' === ($url['scheme'] ?? parse_url(curl_getinfo($ch, \CURLINFO_EFFECTIVE_URL), \PHP_URL_SCHEME)) ? 80 : 443);
439+
$port = $url['port'] ?? ('http' === $url['scheme'] ? 80 : 443);
439440
curl_setopt($ch, \CURLOPT_RESOLVE, ["{$url['host']}:$port:$ip"]);
440441
$multi->dnsCache->removals["-{$url['host']}:$port"] = "-{$url['host']}:$port";
441442
}

src/Symfony/Component/HttpClient/Tests/HttpClientTestCase.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
325D
@@ -490,6 +490,15 @@ public function testNoPrivateNetworkWithResolve()
490490
$client->request('GET', 'http://symfony.com', ['resolve' => ['symfony.com' => '127.0.0.1']]);
491491
}
492492

493+
public function testNoRedirectWithInvalidLocation()
494+
{
495+
$client = $this->getHttpClient(__FUNCTION__);
496+
497+
$response = $client->request('GET', 'http://localhost:8057/302-no-scheme');
498+
499+
$this->assertSame(302, $response->getStatusCode());
500+
}
501+
493502
/**
494503
* @dataProvider getRedirectWithAuthTests
495504
*/

src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ public static function provideResolveUrl(): array
210210
[self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'],
211211
[self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'],
212212
[self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'],
213+
[self::RFC3986_BASE, 'g/h:123/i', 'http://a/b/c/g/h:123/i'],
213214
// dot-segments in the query or fragment
214215
[self::RFC3986_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x'],
215216
[self::RFC3986_BASE, 'g?y/../x', 'http://a/b/c/g?y/../x'],
@@ -235,14 +236,14 @@ public static function provideResolveUrl(): array
235236
public function testResolveUrlWithoutScheme()
236237
{
237238
$this->expectException(InvalidArgumentException::class);
238-
$this->expectExceptionMessage('Invalid URL: scheme is missing in "//localhost:8080". Did you forget to add "http(s)://"?');
239+
$this->expectExceptionMessage('Unsupported scheme in "localhost:8080": "http" or "https" expected.');
239240
self::resolveUrl(self::parseUrl('localhost:8080'), null);
240241
}
241242

242243
public function testResolveBaseUrlWithoutScheme()
243244
{
244245
$this->expectException(InvalidArgumentException::class);
245-
$this->expectExceptionMessage('Invalid URL: scheme is missing in "//localhost:8081". Did you forget to add "http(s)://"?');
246+
$this->expectExceptionMessage('Unsupported scheme in "localhost:8081": "http" or "https" expected.');
246247
self::resolveUrl(self::parseUrl(' 10000 /foo'), self::parseUrl('localhost:8081'));
247248
}
248249

src/Symfony/Component/HttpClient/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"php": ">=8.2",
2626
"psr/log": "^1|^2|^3",
2727
"symfony/deprecation-contracts": "^2.5|^3",
28-
"symfony/http-client-contracts": "^3.4.1",
28+
"symfony/http-client-contracts": "^3.4.3",
2929
"symfony/service-contracts": "^2.5|^3"
3030
},
3131
"require-dev": {

src/Symfony/Component/HttpFoundation/Request.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,7 @@ public static function create(string $uri, string $method = 'GET', array $parame
300300
$server['PATH_INFO'] = '';
301301
$server['REQUEST_METHOD'] = strtoupper($method);
302302

303-
$components = parse_url($uri);
304-
if (false === $components) {
303+
if (false === $components = parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#')) {
305304
throw new BadRequestException('Invalid URI.');
306305
}
307306

@@ -324,9 +323,11 @@ public static function create(string $uri, string $method = 'GET', array $parame
324323
if ('https' === $components['scheme']) {
325324
$server['HTTPS'] = 'on';
326325
$server['SERVER_PORT'] = 443;
327-
} else {
326+
} elseif ('http' === $components['scheme']) {
328327
unset($server['HTTPS']);
329328
$server[&# B94F 39;SERVER_PORT'] = 80;
329+
} else {
330+
throw new BadRequestException('Invalid URI: http(s) scheme expected.');
330331
}
331332
}
332333

src/Symfony/Component/HttpFoundation/Tests/RequestTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,8 @@ public function testCreateWithRequestUri()
311311
* ["foo\u0000"]
312312
* [" foo"]
313313
* ["foo "]
314-
* [":"]
314+
* ["//"]
315+
* ["foo:bar"]
315316
*/
316317
public function testCreateWithBadRequestUri(string $uri)
317318
{

0 commit comments

Comments
 (0)
0