Skip to content

Commit bdd3582

Browse files
committed
Merge branch '7.4' into 8.0
* 7.4: sync nb translations with no translations fix: added new indonesian translations Fix Norwegian translations - remove needs-review-translation status Update Luxembourgish translations by removing 'needs-review-translation' state from multiple entries in security and validator files. Improve Russian translations for video and image validators [Validator] it: approve video/image/Twig translations and fix spacing (refs #60464) [HttpFoundation] Deprecate HTTP method override for methods GET, HEAD, CONNECT and TRACE [Translation][sv] Remove needs-review on Swedish strings; align phrasing and punctuation [HttpFoundation] Add `Request::$allowedHttpMethodOverride` to list which HTTP methods can be overridden
2 parents 9a02ff3 + 8ef075a commit bdd3582

File tree

3 files changed

+98
-4
lines changed

3 files changed

+98
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ CHANGELOG
1818
* Add `#[WithHttpStatus]` to define status codes: 404 for `SignedUriException` and 403 for `ExpiredSignedUriException`
1919
* Add support for the `QUERY` HTTP method
2020
* Add support for structured MIME suffix
21+
* Add `Request::set/getAllowedHttpMethodOverride()` to list which HTTP methods can be overridden
2122
* Deprecate using `Request::sendHeaders()` after headers have already been sent; use a `StreamedResponse` instead
2223
* Deprecate method `Request::get()`, use properties `->attributes`, `query` or `request` directly instead
2324
* Make `Request::createFromGlobals()` parse the body of PUT, DELETE, PATCH and QUERY requests
25+
* Deprecate HTTP method override for methods GET, HEAD, CONNECT and TRACE; it will be ignored in Symfony 8.0
2426

2527
7.3
2628
---

Request.php

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ class Request
8181

8282
protected static bool $httpMethodParameterOverride = false;
8383

84+
/**
85+
* The HTTP methods that can be overridden.
86+
*
87+
* @var uppercase-string[]|null
88+
*/
89+
protected static ?array $allowedHttpMethodOverride = null;
90+
8491
/**
8592
* Custom parameters.
8693
*/
@@ -675,6 +682,30 @@ public static function getHttpMethodParameterOverride(): bool
675682
return self::$httpMethodParameterOverride;
676683
}
677684

685+
/**
686+
* Sets the list of HTTP methods that can be overridden.
687+
*
688+
* Set to null to allow all methods to be overridden (default). Set to an
689+
* empty array to disallow overrides entirely. Otherwise, provide the list
690+
* of uppercased method names that are allowed.
691+
*
692+
* @param uppercase-string[]|null $methods
693+
*/
694+
public static function setAllowedHttpMethodOverride(?array $methods): void
695+
{
696+
self::$allowedHttpMethodOverride = $methods;
697+
}
698+
699+
/**
700+
* Gets the list of HTTP methods that can be overridden.
701+
*
702+
* @return uppercase-string[]|null
703+
*/
704+
public static function getAllowedHttpMethodOverride(): ?array
705+
{
706+
return self::$allowedHttpMethodOverride;
707+
}
708+
678709
/**
679710
* Gets the Session.
680711
*
@@ -1162,7 +1193,7 @@ public function getMethod(): string
11621193

11631194
$this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
11641195

1165-
if ('POST' !== $this->method) {
1196+
if ('POST' !== $this->method || !(self::$allowedHttpMethodOverride ?? true)) {
11661197
return $this->method;
11671198
}
11681199

@@ -1178,11 +1209,15 @@ public function getMethod(): string
11781209

11791210
$method = strtoupper($method);
11801211

1181-
if (\in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE', 'QUERY'], true)) {
1182-
return $this->method = $method;
1212+
if (\in_array($method, ['GET', 'HEAD', 'CONNECT', 'TRACE'], true)) {
1213+
trigger_deprecation('symfony/http-foundation', '7.4', 'HTTP method override is deprecated for methods GET, HEAD, CONNECT and TRACE; it will be ignored in Symfony 8.0.', $method);
1214+
}
1215+
1216+
if (self::$allowedHttpMethodOverride && !\in_array($method, self::$allowedHttpMethodOverride, true)) {
1217+
return $this->method;
11831218
}
11841219

1185-
if (!preg_match('/^[A-Z]++$/D', $method)) {
1220+
if (\strlen($method) !== strspn($method, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')) {
11861221
throw new SuspiciousOperationException('Invalid HTTP method override.');
11871222
}
11881223

Tests/RequestTest.php

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ protected function tearDown(): void
3030
{
3131
Request::setTrustedProxies([], -1);
3232
Request::setTrustedHosts([]);
33+
Request::setAllowedHttpMethodOverride(null);
3334
}
3435

3536
public function testInitialize()
@@ -252,6 +253,50 @@ public function testCreate()
252253
$this->assertEquals('http://test.com/foo', $request->getUri());
253254
}
254255

256+
public function testHttpMethodOverrideRespectsAllowedListWithHeader()
257+
{
258+
$request = Request::create('http://example.com/', 'POST');
259+
$request->headers->set('X-HTTP-METHOD-OVERRIDE', 'PATCH');
260+
261+
Request::setAllowedHttpMethodOverride(['PUT', 'PATCH']);
262+
263+
$this->assertSame('PATCH', $request->getMethod());
264+
}
265+
266+
public function testHttpMethodOverrideDisallowedSkipsOverrideWithHeader()
267+
{
268+
$request = Request::create('http://example.com/', 'POST');
269+
$request->headers->set('X-HTTP-METHOD-OVERRIDE', 'DELETE');
270+
271+
Request::setAllowedHttpMethodOverride(['PUT', 'PATCH']);
272+
273+
$this->assertSame('POST', $request->getMethod());
274+
}
275+
276+
public function testHttpMethodOverrideDisabledWithEmptyAllowedList()
277+
{
278+
$request = Request::create('http://example.com/', 'POST');
279+
$request->headers->set('X-HTTP-METHOD-OVERRIDE', 'PUT');
280+
281+
Request::setAllowedHttpMethodOverride([]);
282+
283+
$this->assertSame('POST', $request->getMethod());
284+
}
285+
286+
public function testHttpMethodOverrideRespectsAllowedListWithParameter()
287+
{
288+
Request::enableHttpMethodParameterOverride();
289+
Request::setAllowedHttpMethodOverride(['PUT']);
290+
291+
try {
292+
$request = Request::create('http://example.com/', 'POST', ['_method' => 'PUT']);
293+
294+
$this->assertSame('PUT', $request->getMethod());
295+
} finally {
296+
(new \ReflectionProperty(Request::class, 'httpMethodParameterOverride'))->setValue(null, false);
297+
}
298+
}
299+
255300
public function testCreateWithRequestUri()
256301
{
257302
$request = Request::create('http://test.com:80/foo');
@@ -1031,6 +1076,18 @@ public function testGetSetMethod()
10311076
$this->assertSame('POST', $request->getMethod(), '->getMethod() returns the request method if invalid type is defined in query');
10321077
}
10331078

1079+
#[IgnoreDeprecations]
1080+
#[Group('legacy')]
1081+
public function testUnsafeMethodOverride()
1082+
{
1083+
$request = new Request();
1084+
$request->setMethod('POST');
1085+
$request->headers->set('X-HTTP-METHOD-OVERRIDE', 'get');
1086+
1087+
$this->expectUserDeprecationMessage('Since symfony/http-foundation 7.4: HTTP method override is deprecated for methods GET, HEAD, CONNECT and TRACE; it will be ignored in Symfony 8.0.');
1088+
$this->assertSame('GET', $request->getMethod());
1089+
}
1090+
10341091
#[DataProvider('getClientIpsProvider')]
10351092
public function testGetClientIp($expected, $remoteAddr, $httpForwardedFor, $trustedProxies)
10361093
{

0 commit comments

Comments
 (0)