Permalink
Browse files

fix #913: redirect() doesn't filter "\r\n" leads to CRLF attack

The previous fix (6d7e13d) was incomplete.
  • Loading branch information...
1 parent f936dfa commit 3f838db73f7488a108dd8eea308fcc1188303371 @defnull defnull committed Dec 17, 2016
Showing with 19 additions and 11 deletions.
  1. +7 −10 bottle.py
  2. +12 −1 test/test_environ.py
View
@@ -1592,21 +1592,21 @@ def _hval(value):
return 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, _):
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):
@@ -1723,8 +1723,7 @@ def __getitem__(self, name):
return self._headers[_hkey(name)][-1]
def __setitem__(self, name, value):
- self._headers[_hkey(name)] = [value if isinstance(value, unicode) else
- str(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
@@ -1734,13 +1733,11 @@ def get_header(self, name, default=None):
def set_header(self, name, value):
""" Create a new response header, replacing any previously defined
headers with the same name. """
- self._headers[_hkey(name)] = [value if isinstance(value, unicode)
- else 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(
- value if isinstance(value, unicode) else str(value))
+ self._headers.setdefault(_hkey(name), []).append(_hval(value))
def iter_headers(self):
""" Yield (header, value) tuples, skipping headers that are not
View
@@ -699,16 +699,27 @@ def test_non_string_header(self):
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 itertools.product(apis, masks, tests):
hd = bottle.HeaderDict()
func = getattr(hd, api)
value = mask.replace("{}", test)
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 itertools.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()

0 comments on commit 3f838db

Please sign in to comment.