summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hellkamp <marc@gsites.de>2016-12-17 20:16:48 +0100
committerMarcel Hellkamp <marc@gsites.de>2016-12-17 20:20:53 +0100
commit78f67d51965db11cb1ed0003f1eb7926458b5c2c (patch)
tree6644e23d149b3b170e411f312747a20af2d17640
parent860d5867cd2c1752bfdf4734e0c00ce564dd966f (diff)
downloadbottle-78f67d51965db11cb1ed0003f1eb7926458b5c2c.tar.gz
fix #913: redirect() doesn't filter "\r\n" leads to CRLF attack
The previous fix (6d7e13d) was incomplete.
-rw-r--r--bottle.py14
-rwxr-xr-xtest/test_environ.py13
2 files changed, 19 insertions, 8 deletions
diff --git a/bottle.py b/bottle.py
index 2dcf86f..2665957 100644
--- a/bottle.py
+++ b/bottle.py
@@ -1413,21 +1413,21 @@ def _hval(value):
class HeaderProperty(object):
- def __init__(self, name, reader=None, writer=str, default=''):
+ def __init__(self, name, reader=None, writer=None, default=''):
self.name, self.default = name, default
self.reader, self.writer = reader, writer
self.__doc__ = 'Current value of the %r header.' % name.title()
def __get__(self, obj, cls):
if obj is None: return self
- value = obj.headers.get(self.name, self.default)
+ value = obj.get_header(self.name, self.default)
return self.reader(value) if self.reader else value
def __set__(self, obj, value):
- obj.headers[self.name] = self.writer(value)
+ obj[self.name] = self.writer(value) if self.writer else value
def __delete__(self, obj):
- del obj.headers[self.name]
+ del obj[self.name]
class BaseResponse(object):
@@ -1534,7 +1534,7 @@ class BaseResponse(object):
def __contains__(self, name): return _hkey(name) in self._headers
def __delitem__(self, name): del self._headers[_hkey(name)]
def __getitem__(self, name): return self._headers[_hkey(name)][-1]
- def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)]
+ def __setitem__(self, name, value): self._headers[_hkey(name)] = [_hval(value)]
def get_header(self, name, default=None):
''' Return the value of a previously defined header. If there is no
@@ -1544,11 +1544,11 @@ class BaseResponse(object):
def set_header(self, name, value):
''' Create a new response header, replacing any previously defined
headers with the same name. '''
- self._headers[_hkey(name)] = [str(value)]
+ self._headers[_hkey(name)] = [_hval(value)]
def add_header(self, name, value):
''' Add an additional response header, not removing duplicates. '''
- self._headers.setdefault(_hkey(name), []).append(str(value))
+ self._headers.setdefault(_hkey(name), []).append(_hval(value))
def iter_headers(self):
''' Yield (header, value) tuples, skipping headers that are not
diff --git a/test/test_environ.py b/test/test_environ.py
index e9062ae..c37af51 100755
--- a/test/test_environ.py
+++ b/test/test_environ.py
@@ -669,9 +669,11 @@ class TestResponse(unittest.TestCase):
self.assertEqual('None', response['x-test'])
def test_prevent_control_characters_in_headers(self):
- apis = 'append', 'replace', '__setitem__', 'setdefault'
masks = '{}test', 'test{}', 'te{}st'
tests = '\n', '\r', '\n\r', '\0'
+
+ # Test HeaderDict
+ apis = 'append', 'replace', '__setitem__', 'setdefault'
for api, mask, test in product(apis, masks, tests):
hd = bottle.HeaderDict()
func = getattr(hd, api)
@@ -679,6 +681,15 @@ class TestResponse(unittest.TestCase):
self.assertRaises(ValueError, func, value, "test-value")
self.assertRaises(ValueError, func, "test-name", value)
+ # Test functions on BaseResponse
+ apis = 'add_header', 'set_header', '__setitem__'
+ for api, mask, test in product(apis, masks, tests):
+ rs = bottle.BaseResponse()
+ func = getattr(rs, api)
+ value = mask.replace("{}", test)
+ self.assertRaises(ValueError, func, value, "test-value")
+ self.assertRaises(ValueError, func, "test-name", value)
+
def test_expires_header(self):
import datetime
response = BaseResponse()