Skip to content

Commit

Permalink
allow more characters in unquoted cookie values (#2663)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidism authored Apr 26, 2023
2 parents 367bc5d + 0822143 commit 1d66126
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Version 2.3.1
Unreleased

- Percent-encode plus (+) when building URLs and in test requests. :issue:`2657`
- Cookie values don't quote characters defined in RFC 6265. :issue:`2659`


Version 2.3.0
Expand Down
8 changes: 4 additions & 4 deletions src/werkzeug/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,7 @@ def parse_cookie(
)


_token_re = re.compile(r"[\w!#$%&'*+\-.^`|~]*", re.A)
_cookie_no_quote_re = re.compile(r"[\w!#$%&'()*+\-./:<=>?@\[\]^`{|}~]*", re.A)
_cookie_slash_re = re.compile(rb"[\x00-\x19\",;\\\x7f-\xff]", re.A)
_cookie_slash_map = {b'"': b'\\"', b"\\": b"\\\\"}
_cookie_slash_map.update(
Expand Down Expand Up @@ -1454,9 +1454,9 @@ def dump_cookie(
if samesite not in {"Strict", "Lax", "None"}:
raise ValueError("SameSite must be 'Strict', 'Lax', or 'None'.")

# This doesn't match RFC 6265. Use quoted-string for non-token values as with header
# parameters. Slash-escape controls, comma, and semicolon with three octal digits.
if not _token_re.fullmatch(value):
# Quote value if it contains characters not allowed by RFC 6265. Slash-escape with
# three octal digits, which matches http.cookies, although the RFC suggests base64.
if not _cookie_no_quote_re.fullmatch(value):
# Work with bytes here, since a UTF-8 character could be multiple bytes.
value = _cookie_slash_re.sub(
lambda m: _cookie_slash_map[m.group()], value.encode(charset)
Expand Down
6 changes: 3 additions & 3 deletions tests/test_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,8 @@ def test_dump_cookie(self):
"Max-Age=360",
'foo="bar baz blub"',
}
assert http.dump_cookie("key", "xxx/") == 'key="xxx/"'
assert http.dump_cookie("key", "xxx=") == 'key="xxx="'
assert http.dump_cookie("key", "xxx/") == "key=xxx/"
assert http.dump_cookie("key", "xxx=") == "key=xxx="

def test_bad_cookies(self):
cookies = http.parse_cookie(
Expand All @@ -456,7 +456,7 @@ def test_empty_keys_are_ignored(self):

def test_cookie_quoting(self):
val = http.dump_cookie("foo", "?foo")
assert val == 'foo="?foo"'
assert val == "foo=?foo"
assert http.parse_cookie(val)["foo"] == "?foo"
assert http.parse_cookie(r'foo="foo\054bar"')["foo"] == "foo,bar"

Expand Down

0 comments on commit 1d66126

Please sign in to comment.