From 1dd69c5c5982fae7c87a620d487c2ebf7a6b436b Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Mon, 17 Feb 2020 15:34:48 -0600 Subject: [PATCH] Raise ValueError if method contains control characters (#1800) CVE: CVE-2020-26137 Upstream-Status: Backport [https://github.com/urllib3/urllib3/commit/1dd69c5c5982fae7c87a620d487c2ebf7a6b436b.patch] Signed-off-by: Nikhil R Signed-off-by: Ranjitsinh Rathod Comment: Removed one hunk in CHANGES.rst and refresh other to remove patch fuzz warnings --- src/urllib3/connection.py | 14 ++++++++++++++ test/with_dummyserver/test_connectionpool.py | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/src/urllib3/connection.py b/src/urllib3/connection.py index 71e6790b1b..f7b1760938 100644 --- a/src/urllib3/connection.py +++ b/src/urllib3/connection.py @@ -1,4 +1,5 @@ from __future__ import absolute_import +import re import datetime import logging import os @@ -58,6 +59,8 @@ port_by_scheme = {"http": 80, "https": 443} # (ie test_recent_date is failing) update it to ~6 months before the current date. RECENT_DATE = datetime.date(2019, 1, 1) +_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]") + class DummyConnection(object): """Used to detect a failed ConnectionCls import.""" @@ -184,6 +187,17 @@ class HTTPConnection(_HTTPConnection, object): conn = self._new_conn() self._prepare_conn(conn) + def putrequest(self, method, url, *args, **kwargs): + """Send a request to the server""" + match = _CONTAINS_CONTROL_CHAR_RE.search(method) + if match: + raise ValueError( + "Method cannot contain non-token characters %r (found at least %r)" + % (method, match.group()) + ) + + return _HTTPConnection.putrequest(self, method, url, *args, **kwargs) + def request_chunked(self, method, url, body=None, headers=None): """ Alternative to the common request method, which sends the diff --git a/test/with_dummyserver/test_connectionpool.py b/test/with_dummyserver/test_connectionpool.py index 57f0dbd2f4..79cbd27185 100644 --- a/test/with_dummyserver/test_connectionpool.py +++ b/test/with_dummyserver/test_connectionpool.py @@ -677,6 +677,12 @@ class TestConnectionPool(HTTPDummyServerTestCase): with pytest.raises(MaxRetryError): pool.request("GET", "/test", retries=2) + @pytest.mark.parametrize("char", [" ", "\r", "\n", "\x00"]) + def test_invalid_method_not_allowed(self, char): + with pytest.raises(ValueError): + with HTTPConnectionPool(self.host, self.port) as pool: + pool.request("GET" + char, "/") + def test_percent_encode_invalid_target_chars(self): with HTTPConnectionPool(self.host, self.port) as pool: r = pool.request("GET", "/echo_params?q=\r&k=\n \n")