Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ def cookie_app(environ, start_response):
app.get('/')
app.reset()

def test_set_cookie_value_is_unescaped_in_cookies_property(self):
app = webtest.TestApp(debug_app)
app.set_cookie('foo', 'bar')
app.set_cookie('fizz', ';bar=baz') # Make sure we're unescaping.
self.assertEqual(app.cookies.get('foo'), 'bar')
self.assertEqual(app.cookies.get('fizz'), ';bar=baz')

def test_preserves_cookies(self):
def cookie_app(environ, start_response):
req = Request(environ)
Expand Down
6 changes: 5 additions & 1 deletion webtest/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from webtest.compat import urlparse
from webtest.compat import to_bytes
from webtest.compat import escape_cookie_value
from webtest.compat import unescape_cookie_value
from webtest.response import TestResponse
from webtest import forms
from webtest import lint
Expand Down Expand Up @@ -228,7 +229,10 @@ def set_authorization(self, value):

@property
def cookies(self):
return {cookie.name: cookie.value for cookie in self.cookiejar}
return {
cookie.name: unescape_cookie_value(cookie.value)
for cookie in self.cookiejar
}

def set_cookie(self, name, value):
"""
Expand Down
38 changes: 38 additions & 0 deletions webtest/compat.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import re
import sys
from http import cookies

Expand Down Expand Up @@ -31,6 +32,43 @@ def escape_cookie_value(value):
) + '"'


_OCTAL_ESCAPE_RE = re.compile(r'\\[0-3][0-7][0-7]')
_QUOTED_PAIR_RE = re.compile(r'\\.')


def unescape_cookie_value(value):
"""
Reverses the transformation performed by ``escape_cookie_value``.

If the value isn't wrapped in double quotes it is returned unchanged,
since it can't have been produced by ``escape_cookie_value`` (this also
covers values that a cookiejar has already unquoted for us).

"""
if value is None or len(value) < 2 or value[0] != '"' or value[-1] != '"':
return value

value = value[1:-1]

result = []
i = 0
length = len(value)
while i < length:
octal_match = _OCTAL_ESCAPE_RE.match(value, i)
if octal_match:
result.append(chr(int(value[i + 1:i + 4], 8)))
i += 4
continue
quoted_pair_match = _QUOTED_PAIR_RE.match(value, i)
if quoted_pair_match:
result.append(value[i + 1])
i += 2
continue
result.append(value[i])
i += 1
return ''.join(result)


# A list of illegal characters in a cookie and the escaped equivalent.
# Taken from Python's cookie module.
COOKIE_ESCAPE_CHAR_MAP = {
Expand Down