Skip to content

Commit

Permalink
[HttpFoundation] allow additinal characters in not raw cookies
Browse files Browse the repository at this point in the history
  • Loading branch information
marie authored and nicolas-grekas committed Sep 28, 2019
1 parent ec2a74a commit 35ffbbf
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 16 deletions.
21 changes: 16 additions & 5 deletions Cookie.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,24 @@
*/
class Cookie
{
const SAMESITE_NONE = 'none';
const SAMESITE_LAX = 'lax';
const SAMESITE_STRICT = 'strict';

protected $name;
protected $value;
protected $domain;
protected $expire;
protected $path;
protected $secure;
protected $httpOnly;

private $raw;
private $sameSite;

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

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

Expand Down Expand Up @@ -143,7 +148,13 @@ public function __construct($name, $value = null, $expire = 0, $path = '/', $dom
*/
public function __toString()
{
$str = ($this->isRaw() ? $this->getName() : urlencode($this->getName())).'=';
if ($this->isRaw()) {
$str = $this->getName();
} else {
$str = str_replace(self::$reservedCharsFrom, self::$reservedCharsTo, $this->getName());
}

$str .= '=';

if ('' === (string) $this->getValue()) {
$str .= 'deleted; expires='.gmdate('D, d-M-Y H:i:s T', time() - 31536001).'; Max-Age=0';
Expand Down
2 changes: 1 addition & 1 deletion Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ public function sendHeaders()

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

// status
Expand Down
23 changes: 18 additions & 5 deletions Tests/CookieTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
*/
class CookieTest extends TestCase
{
public function invalidNames()
public function namesWithSpecialCharacters()
{
return [
[''],
[',MyName'],
[';MyName'],
[' MyName'],
Expand All @@ -40,12 +39,26 @@ public function invalidNames()
}

/**
* @dataProvider invalidNames
* @dataProvider namesWithSpecialCharacters
*/
public function testInstantiationThrowsExceptionIfCookieNameContainsInvalidCharacters($name)
public function testInstantiationThrowsExceptionIfRawCookieNameContainsSpecialCharacters($name)
{
$this->expectException('InvalidArgumentException');
new Cookie($name);
new Cookie($name, null, 0, null, null, null, false, true);
}

/**
* @dataProvider namesWithSpecialCharacters
*/
public function testInstantiationSucceedNonRawCookieNameContainsSpecialCharacters($name)
{
$this->assertInstanceOf(Cookie::class, new Cookie($name));
}

public function testInstantiationThrowsExceptionIfCookieNameIsEmpty()
{
$this->expectException('InvalidArgumentException');
new Cookie('');
}

public function testInvalidExpiration()
Expand Down
3 changes: 2 additions & 1 deletion Tests/Fixtures/response-functional/cookie_urlencode.expected
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Array
[0] => Content-Type: text/plain; charset=utf-8
[1] => Cache-Control: no-cache, private
[2] => Date: Sat, 12 Nov 1955 20:04:00 GMT
[3] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
[3] => Set-Cookie: %3D%2C%3B%20%09%0D%0A%0B%0C=%3D%2C%3B%20%09%0D%0A%0B%0C; path=/
[4] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
[5] => Set-Cookie: ?*():@&+$/%#[]=%3F%2A%28%29%3A%40%26%2B%24%2F%25%23%5B%5D; path=/
)
shutdown
9 changes: 6 additions & 3 deletions Tests/Fixtures/response-functional/cookie_urlencode.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

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

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

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

$r->headers->setCookie(new Cookie($str2, $str2, 0, '', null, false, false, false, null));
$r->sendHeaders();

setcookie($str, $str, 0, '/');
setcookie($str2, $str2, 0, '/');
2 changes: 1 addition & 1 deletion Tests/Fixtures/response-functional/invalid_cookie_name.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
$r = require __DIR__.'/common.inc';

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

0 comments on commit 35ffbbf

Please sign in to comment.