summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2022-07-17 16:26:54 -0400
committerJason R. Coombs <jaraco@jaraco.com>2022-07-17 16:26:54 -0400
commit78331575dacb67eaabf3e645533f1d05b861f00d (patch)
treee5155a17efb21baee13b38010edf5770410ff350
parent2296d0e885b9e575374c83724bebeb8a312c9a1e (diff)
downloadcherrypy-git-78331575dacb67eaabf3e645533f1d05b861f00d.tar.gz
Introduce SanitizedHost wrapper. Fixes #1974.
-rw-r--r--cherrypy/_cprequest.py3
-rw-r--r--cherrypy/lib/httputil.py30
-rw-r--r--cherrypy/test/test_request_obj.py3
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.