Skip to content

Commit 35ffbbf

Browse files
marienicolas-grekas
authored andcommitted
[HttpFoundation] allow additinal characters in not raw cookies
1 parent ec2a74a commit 35ffbbf

File tree

6 files changed

+44
-16
lines changed

6 files changed

+44
-16
lines changed

Cookie.php

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,24 @@
1818
*/
1919
class Cookie
2020
{
21+
const SAMESITE_NONE = 'none';
22+
const SAMESITE_LAX = 'lax';
23+
const SAMESITE_STRICT = 'strict';
24+
2125
protected $name;
2226
protected $value;
2327
protected $domain;
2428
protected $expire;
2529
protected $path;
2630
protected $secure;
2731
protected $httpOnly;
32+
2833
private $raw;
2934
private $sameSite;
3035

31-
const SAMESITE_NONE = 'none';
32-
const SAMESITE_LAX = 'lax';
33-
const SAMESITE_STRICT = 'strict';
36+
private static $reservedCharsList = "=,; \t\r\n\v\f";
37+
private static $reservedCharsFrom = ['=', ',', ';', ' ', "\t", "\r", "\n", "\v", "\f"];
38+
private static $reservedCharsTo = ['%3D', '%2C', '%3B', '%20', '%09', '%0D', '%0A', '%0B', '%0C'];
3439

3540
/**
3641
* Creates cookie from raw header string.
@@ -97,7 +102,7 @@ public static function fromString($cookie, $decode = false)
97102
public function __construct($name, $value = null, $expire = 0, $path = '/', $domain = null, $secure = false, $httpOnly = true, $raw = false, $sameSite = null)
98103
{
99104
// from PHP source code
100-
if (preg_match("/[=,; \t\r\n\013\014]/", $name)) {
105+
if ($raw && false !== strpbrk($name, self::$reservedCharsList)) {
101106
throw new \InvalidArgumentException(sprintf('The cookie name "%s" contains invalid characters.', $name));
102107
}
103108

@@ -143,7 +148,13 @@ public function __construct($name, $value = null, $expire = 0, $path = '/', $dom
143148
*/
144149
public function __toString()
145150
{
146-
$str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
151+
if ($this->isRaw()) {
152+
$str = $this->getName();
153+
} else {
154+
$str = str_replace(self::$reservedCharsFrom, self::$reservedCharsTo, $this->getName());
155+
}
156+
157+
$str .= '=';
147158

148159
if ('' === (string) $this->getValue()) {
149160
$str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';

Response.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ public function sendHeaders()
342342

343343
// cookies
344344
foreach ($this->headers->getCookies() as $cookie) {
345-
header('Set-Cookie: '.$cookie->getName().strstr($cookie, '='), false, $this->statusCode);
345+
header('Set-Cookie: '.$cookie, false, $this->statusCode);
346346
}
347347

348348
// status

Tests/CookieTest.php

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,9 @@
2424
*/
2525
class CookieTest extends TestCase
2626
{
27-
public function invalidNames()
27+
public function namesWithSpecialCharacters()
2828
{
2929
return [
30-
[''],
3130
[',MyName'],
3231
[';MyName'],
3332
[' MyName'],
@@ -40,12 +39,26 @@ public function invalidNames()
4039
}
4140

4241
/**
43-
* @dataProvider invalidNames
42+
* @dataProvider namesWithSpecialCharacters
4443
*/
45-
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
44+
public function testInstantiationThrowsExceptionIfRawCookieNameContainsSpecialCharacters($name)
4645
{
4746
$this->expectException('InvalidArgumentException');
48-
new Cookie($name);
47+
new Cookie($name, null, 0, null, null, null, false, true);
48+
}
49+
50+
/**
51+
* @dataProvider namesWithSpecialCharacters
52+
*/
53+
public function testInstantiationSucceedNonRawCookieNameContainsSpecialCharacters($name)
54+
{
55+
$this->assertInstanceOf(Cookie::class, new Cookie($name));
56+
}
57+
58+
public function testInstantiationThrowsExceptionIfCookieNameIsEmpty()
59+
{
60+
$this->expectException('InvalidArgumentException');
61+
new Cookie('');
4962
}
5063

5164
public function testInvalidExpiration()

Tests/Fixtures/response-functional/cookie_urlencode.expected

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ Array
44
[0] => Content-Type: text/plain; charset=utf-8
55
[1] => Cache-Control: no-cache, private
66
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
7-
[3] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
7+
[3] => Set-Cookie: %3D%2C%3B%20%09%0D%0A%0B%0C=%3D%2C%3B%20%09%0D%0A%0B%0C; path=/
88
[4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
9+
[5] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
910
)
1011
shutdown

Tests/Fixtures/response-functional/cookie_urlencode.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
$r = require __DIR__.'/common.inc';
66

7-
$str = '?*():@&+$/%#[]';
7+
$str1 = "=,; \t\r\n\v\f";
8+
$r->headers->setCookie(new Cookie($str1, $str1, 0, '', null, false, false, false, null));
89

9-
$r->headers->setCookie(new Cookie($str, $str, 0, '', null, false, false));
10+
$str2 = '?*():@&+$/%#[]';
11+
12+
$r->headers->setCookie(new Cookie($str2, $str2, 0, '', null, false, false, false, null));
1013
$r->sendHeaders();
1114

12-
setcookie($str, $str, 0, '/');
15+
setcookie($str2, $str2, 0, '/');

Tests/Fixtures/response-functional/invalid_cookie_name.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
$r = require __DIR__.'/common.inc';
66

77
try {
8-
$r->headers->setCookie(new Cookie('Hello + world', 'hodor'));
8+
$r->headers->setCookie(new Cookie('Hello + world', 'hodor', 0, null, null, null, false, true));
99
} catch (\InvalidArgumentException $e) {
1010
echo $e->getMessage();
1111
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy