diff --git a/gunicorn/http/wsgi.py b/gunicorn/http/wsgi.py index feaf548a7..3ce3cd78b 100644 --- a/gunicorn/http/wsgi.py +++ b/gunicorn/http/wsgi.py @@ -8,6 +8,7 @@ import re import sys +from gunicorn.http.errors import InvalidHeader, InvalidHeaderName from gunicorn.six import unquote_to_wsgi_str, string_types, binary_type, reraise from gunicorn import SERVER_SOFTWARE import gunicorn.six as six @@ -23,6 +24,8 @@ sendfile = None NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+') +HEADER_RE = re.compile(r"[\x00-\x1F\x7F()<>@,;:\[\]={} \t\\\"]") +HEADER_VALUE_RE = re.compile(r'[\x00-\x1F\x7F]') log = logging.getLogger(__name__) @@ -227,6 +230,12 @@ def process_headers(self, headers): for name, value in headers: assert isinstance(name, string_types), "%r is not a string" % name + if HEADER_RE.search(name): + raise InvalidHeaderName('%r' % name) + + if HEADER_VALUE_RE.search(value): + raise InvalidHeader('%r' % value) + value = str(value).strip() lname = name.lower().strip() if lname == "content-length": diff --git a/tests/test_http.py b/tests/test_http.py new file mode 100644 index 000000000..85ba3a4f3 --- /dev/null +++ b/tests/test_http.py @@ -0,0 +1,28 @@ +# -*- encoding: utf-8 -*- + +import pytest +from gunicorn.http.wsgi import Response +from gunicorn.six import BytesIO +from gunicorn.http.errors import InvalidHeader, InvalidHeaderName + +try: + import unittest.mock as mock +except ImportError: + import mock + + +def test_http_inalid_response_header(): + """ tests whether http response headers are contains control chars """ + + mocked_socket = mock.MagicMock() + mocked_socket.sendall = mock.MagicMock() + + mocked_request = mock.MagicMock() + response = Response(mocked_request, mocked_socket) + + with pytest.raises(InvalidHeader): + response.start_response("200 OK", [('foo', 'essai\r\n')]) + + response = Response(mocked_request, mocked_socket) + with pytest.raises(InvalidHeaderName): + response.start_response("200 OK", [('foo\r\n', 'essai')])