From 1e10a02e73c87dfadc394b361af33864d0e59e24 Mon Sep 17 00:00:00 2001 From: benoitc Date: Fri, 18 Mar 2016 23:15:26 +0100 Subject: [PATCH] check if the header contains control characters fix #1227 --- gunicorn/http/wsgi.py | 10 ++++++++++ tests/test_http.py | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/gunicorn/http/wsgi.py b/gunicorn/http/wsgi.py index 1e373bbcd..a0e218720 100644 --- a/gunicorn/http/wsgi.py +++ b/gunicorn/http/wsgi.py @@ -10,6 +10,8 @@ import sys from gunicorn._compat import unquote_to_wsgi_str +from gunicorn.http.message import HEADER_RE +from gunicorn.http.errors import InvalidHeader, InvalidHeaderName from gunicorn.six import string_types, binary_type, reraise from gunicorn import SERVER_SOFTWARE import gunicorn.util as util @@ -28,6 +30,7 @@ BLKSIZE = 0x3FFFFFFF NORMALIZE_SPACE = re.compile(r'(?:\r\n)?[ \t]+') +HEADER_VALUE_RE = re.compile(r'[\x00-\x1F\x7F]') log = logging.getLogger(__name__) @@ -264,6 +267,13 @@ def process_headers(self, headers): for name, value in headers: if not isinstance(name, string_types): raise TypeError('%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 index 973bc7d7f..7b4761c8d 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -1,10 +1,12 @@ # -*- encoding: utf-8 -*- import t +import pytest from gunicorn import util from gunicorn.http.body import Body from gunicorn.http.wsgi import Response from gunicorn.six import BytesIO +from gunicorn.http.errors import InvalidHeader, InvalidHeaderName try: import unittest.mock as mock @@ -94,3 +96,20 @@ def test_http_header_encoding(): mocked_socket.sendall(util.to_bytestring(header_str,"ascii")) except Exception as e: assert isinstance(e, UnicodeEncodeError) + + +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, None) + + with pytest.raises(InvalidHeader): + response.start_response("200 OK", [('foo', 'essai\r\n')]) + + response = Response(mocked_request, mocked_socket, None) + with pytest.raises(InvalidHeaderName): + response.start_response("200 OK", [('foo\r\n', 'essai')])