Commit 191f1e47 authored by Tres Seaver's avatar Tres Seaver

Ensure that response header values cannot embed CRLF pairs, which violate the

HTTP spec (RFC 2616).
parent 276e7731
...@@ -8,6 +8,9 @@ Zope Changes ...@@ -8,6 +8,9 @@ Zope Changes
Bugs fixed Bugs fixed
- Ensure that response header values cannot embed CRLF pairs, which
violate the HTTP spec (RFC 2616).
- Launchpad #282677: fixed implementation of guarded_map and - Launchpad #282677: fixed implementation of guarded_map and
provided tests and implementation for guarded_zip provided tests and implementation for guarded_zip
(RestrictedPython). (RestrictedPython).
......
...@@ -122,6 +122,10 @@ otherTypes = os.environ.get('DONT_GZIP_MAJOR_MIME_TYPES','').lower() ...@@ -122,6 +122,10 @@ otherTypes = os.environ.get('DONT_GZIP_MAJOR_MIME_TYPES','').lower()
if otherTypes: if otherTypes:
uncompressableMimeMajorTypes += tuple(otherTypes.split(',')) uncompressableMimeMajorTypes += tuple(otherTypes.split(','))
_CRLF = re.compile(r'\r[\n]?')
def _scrubHeader(name, value):
return ''.join(_CRLF.split(str(name))), ''.join(_CRLF.split(str(value)))
class HTTPResponse(BaseResponse): class HTTPResponse(BaseResponse):
"""\ """\
...@@ -249,8 +253,7 @@ class HTTPResponse(BaseResponse): ...@@ -249,8 +253,7 @@ class HTTPResponse(BaseResponse):
literal flag is true, the case of the header name is preserved, literal flag is true, the case of the header name is preserved,
otherwise word-capitalization will be performed on the header otherwise word-capitalization will be performed on the header
name on output.''' name on output.'''
name = str(name) name, value = _scrubHeader(name, value)
value = str(value)
key = name.lower() key = name.lower()
if accumulate_header(key): if accumulate_header(key):
self.accumulated_headers = ( self.accumulated_headers = (
...@@ -263,8 +266,7 @@ class HTTPResponse(BaseResponse): ...@@ -263,8 +266,7 @@ class HTTPResponse(BaseResponse):
'''\ '''\
Set a new HTTP return header with the given value, while retaining Set a new HTTP return header with the given value, while retaining
any previously set headers with the same name.''' any previously set headers with the same name.'''
name = str(name) name, value = _scrubHeader(name, value)
value = str(value)
self.accumulated_headers = ( self.accumulated_headers = (
"%s%s: %s\n" % (self.accumulated_headers, name, value)) "%s%s: %s\n" % (self.accumulated_headers, name, value))
...@@ -559,8 +561,8 @@ class HTTPResponse(BaseResponse): ...@@ -559,8 +561,8 @@ class HTTPResponse(BaseResponse):
Sets an HTTP return header "name" with value "value", Sets an HTTP return header "name" with value "value",
appending it following a comma if there was a previous value appending it following a comma if there was a previous value
set for the header. ''' set for the header. '''
name = str(name).lower() name, value = _scrubHeader(name, value)
value = str(value) name = name.lower()
headers = self.headers headers = self.headers
if headers.has_key(name): if headers.has_key(name):
......
...@@ -121,6 +121,40 @@ class HTTPResponseTests(unittest.TestCase): ...@@ -121,6 +121,40 @@ class HTTPResponseTests(unittest.TestCase):
self.assertEqual(response.body, unicode('rger', self.assertEqual(response.body, unicode('rger',
'iso-8859-15').encode('utf-8')) 'iso-8859-15').encode('utf-8'))
def test_addHeader_drops_CRLF(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.addHeader('Location',
'http://www.ietf.org/rfc/\r\nrfc2616.txt')
self.assertEqual(response.accumulated_headers,
'Location: http://www.ietf.org/rfc/rfc2616.txt\n')
def test_appendHeader_drops_CRLF(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.appendHeader('Location',
'http://www.ietf.org/rfc/\r\nrfc2616.txt')
self.assertEqual(response.headers['location'],
'http://www.ietf.org/rfc/rfc2616.txt')
def test_setHeader_drops_CRLF(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.setHeader('Location',
'http://www.ietf.org/rfc/\r\nrfc2616.txt')
self.assertEqual(response.headers['location'],
'http://www.ietf.org/rfc/rfc2616.txt')
def test_setHeader_drops_CRLF_when_accumulating(self):
# RFC2616 disallows CRLF in a header value.
response = self._makeOne()
response.setHeader('Set-Cookie', 'allowed="OK"')
response.setHeader('Set-Cookie',
'violation="http://www.ietf.org/rfc/\r\nrfc2616.txt"')
self.assertEqual(response.accumulated_headers,
'Set-Cookie: allowed="OK"\n' +
'Set-Cookie: violation="http://www.ietf.org/rfc/rfc2616.txt"\n')
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment