diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2022-07-17 16:26:54 -0400 |
---|---|---|
committer | Jason R. Coombs <jaraco@jaraco.com> | 2022-07-17 16:26:54 -0400 |
commit | 78331575dacb67eaabf3e645533f1d05b861f00d (patch) | |
tree | e5155a17efb21baee13b38010edf5770410ff350 | |
parent | 2296d0e885b9e575374c83724bebeb8a312c9a1e (diff) | |
download | cherrypy-git-78331575dacb67eaabf3e645533f1d05b861f00d.tar.gz |
Introduce SanitizedHost wrapper. Fixes #1974.
-rw-r--r-- | cherrypy/_cprequest.py | 3 | ||||
-rw-r--r-- | cherrypy/lib/httputil.py | 30 | ||||
-rw-r--r-- | cherrypy/test/test_request_obj.py | 3 |
3 files changed, 33 insertions, 3 deletions
diff --git a/cherrypy/_cprequest.py b/cherrypy/_cprequest.py index b380bb75..a661112c 100644 --- a/cherrypy/_cprequest.py +++ b/cherrypy/_cprequest.py @@ -742,6 +742,9 @@ class Request(object): if self.protocol >= (1, 1): msg = "HTTP/1.1 requires a 'Host' request header." raise cherrypy.HTTPError(400, msg) + else: + headers['Host'] = httputil.SanitizedHost(dict.get(headers, 'Host')) + host = dict.get(headers, 'Host') if not host: host = self.local.name or self.local.ip diff --git a/cherrypy/lib/httputil.py b/cherrypy/lib/httputil.py index eedf8d89..ced310a0 100644 --- a/cherrypy/lib/httputil.py +++ b/cherrypy/lib/httputil.py @@ -516,3 +516,33 @@ class Host(object): def __repr__(self): return 'httputil.Host(%r, %r, %r)' % (self.ip, self.port, self.name) + + +class SanitizedHost(str): + r""" + Wraps a raw host header received from the network in + a sanitized version that elides dangerous characters. + + >>> SanitizedHost('foo\nbar') + 'foobar' + >>> SanitizedHost('foo\nbar').raw + 'foo\nbar' + + A SanitizedInstance is only returned if sanitization was performed. + + >>> isinstance(SanitizedHost('foobar'), SanitizedHost) + False + """ + dangerous = re.compile(r'[\n\r]') + + def __new__(cls, raw): + sanitized = cls._sanitize(raw) + if sanitized == raw: + return raw + instance = super().__new__(cls, sanitized) + instance.raw = raw + return instance + + @classmethod + def _sanitize(cls, raw): + return cls.dangerous.sub('', raw) diff --git a/cherrypy/test/test_request_obj.py b/cherrypy/test/test_request_obj.py index 482027be..2478aabe 100644 --- a/cherrypy/test/test_request_obj.py +++ b/cherrypy/test/test_request_obj.py @@ -7,8 +7,6 @@ import types import uuid from http.client import IncompleteRead -import pytest - import cherrypy from cherrypy._cpcompat import ntou from cherrypy.lib import httputil @@ -758,7 +756,6 @@ class RequestObjectTests(helper.CPWebCase): headers=[('Content-type', 'application/json')]) self.assertBody('application/json') - @pytest.mark.xfail(reason="#1974") def test_dangerous_host(self): """ Dangerous characters like newlines should be elided. |