File CVE-2023-45803.patch of Package python-urllib3.34894
Index: urllib3-1.25.10/dummyserver/handlers.py
===================================================================
--- urllib3-1.25.10.orig/dummyserver/handlers.py
+++ urllib3-1.25.10/dummyserver/handlers.py
@@ -29,7 +29,10 @@ class Response(object):
self.headers = headers or [("Content-type", "text/plain")]
def __call__(self, request_handler):
- status, reason = self.status.split(" ", 1)
+ if hasattr(self.status, "decode"):
+ status, reason = self.status.decode().split(" ", 1)
+ else:
+ status, reason = self.status.split(" ", 1)
request_handler.set_status(int(status), reason)
for header, value in self.headers:
request_handler.add_header(header, value)
@@ -52,6 +55,13 @@ class Response(object):
RETRY_TEST_NAMES = collections.defaultdict(int)
+def request_params(request):
+ params = {}
+ for k, v in request.arguments.items():
+ params[k] = next(iter(v))
+ return params
+
+
class TestingApp(RequestHandler):
"""
Simple app that performs various operations, useful for testing an HTTP
@@ -260,6 +270,12 @@ class TestingApp(RequestHandler):
def headers(self, request):
return Response(json.dumps(dict(request.headers)))
+ def headers_and_params(self, request):
+ params = request_params(request)
+ return Response(
+ json.dumps({"headers": dict(request.headers), "params": params})
+ )
+
def successful_retry(self, request):
""" Handler which will return an error and then success
Index: urllib3-1.25.10/src/urllib3/_collections.py
===================================================================
--- urllib3-1.25.10.orig/src/urllib3/_collections.py
+++ urllib3-1.25.10/src/urllib3/_collections.py
@@ -267,6 +267,24 @@ class HTTPHeaderDict(MutableMapping):
else:
return vals[1:]
+ def _prepare_for_method_change(self):
+ """
+ Remove content-specific header fields before changing the request
+ method to GET or HEAD according to RFC 9110, Section 15.4.
+ """
+ content_specific_headers = [
+ "Content-Encoding",
+ "Content-Language",
+ "Content-Location",
+ "Content-Type",
+ "Content-Length",
+ "Digest",
+ "Last-Modified",
+ ]
+ for header in content_specific_headers:
+ self.discard(header)
+ return self
+
# Backwards compatibility for httplib
getheaders = getlist
getallmatchingheaders = getlist
Index: urllib3-1.25.10/src/urllib3/connectionpool.py
===================================================================
--- urllib3-1.25.10.orig/src/urllib3/connectionpool.py
+++ urllib3-1.25.10/src/urllib3/connectionpool.py
@@ -35,6 +35,7 @@ from .connection import (
HTTPException,
BaseSSLError,
)
+from ._collections import HTTPHeaderDict
from .request import RequestMethods
from .response import HTTPResponse
@@ -771,7 +772,11 @@ class HTTPConnectionPool(ConnectionPool,
redirect_location = redirect and response.get_redirect_location()
if redirect_location:
if response.status == 303:
+ # Change the method according to RFC 9110, Section 15.4.4.
method = "GET"
+ # And lose the body not to transfer anything sensitive.
+ body = None
+ headers = HTTPHeaderDict(headers)._prepare_for_method_change()
try:
retries = retries.increment(method, url, response=response, _pool=self)
Index: urllib3-1.25.10/src/urllib3/poolmanager.py
===================================================================
--- urllib3-1.25.10.orig/src/urllib3/poolmanager.py
+++ urllib3-1.25.10/src/urllib3/poolmanager.py
@@ -4,7 +4,7 @@ import functools
import logging
import warnings
-from ._collections import RecentlyUsedContainer
+from ._collections import HTTPHeaderDict, RecentlyUsedContainer
from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool
from .connectionpool import port_by_scheme
from .exceptions import (
@@ -342,9 +342,12 @@ class PoolManager(RequestMethods):
# Support relative URLs for redirecting.
redirect_location = urljoin(url, redirect_location)
- # RFC 7231, Section 6.4.4
if response.status == 303:
+ # Change the method according to RFC 9110, Section 15.4.4.
method = "GET"
+ # And lose the body not to transfer anything sensitive.
+ kw["body"] = None
+ kw["headers"] = HTTPHeaderDict(kw["headers"])._prepare_for_method_change()
retries = kw.get("retries")
if not isinstance(retries, Retry):
Index: urllib3-1.25.10/test/with_dummyserver/test_connectionpool.py
===================================================================
--- urllib3-1.25.10.orig/test/with_dummyserver/test_connectionpool.py
+++ urllib3-1.25.10/test/with_dummyserver/test_connectionpool.py
@@ -5,6 +5,7 @@ import sys
import time
import warnings
import pytest
+import json
import mock
@@ -24,6 +25,7 @@ from urllib3.packages.six import b, u
from urllib3.packages.six.moves.urllib.parse import urlencode
from urllib3.util.retry import Retry, RequestHistory
from urllib3.util.timeout import Timeout
+from urllib3 import HTTPHeaderDict
from test import SHORT_TIMEOUT, LONG_TIMEOUT
from dummyserver.testcase import HTTPDummyServerTestCase, SocketDummyServerTestCase
@@ -403,6 +405,18 @@ class TestConnectionPool(HTTPDummyServer
assert r.status == 200
assert r.data == b"Dummy server!"
+ def test_303_redirect_makes_request_lose_body(self):
+ with HTTPConnectionPool(self.host, self.port) as pool:
+ response = pool.request(
+ "POST",
+ "/redirect",
+ fields={"target": "/headers_and_params", "status": "303 See Other"},
+ )
+ data = response.data.decode("utf-8")
+ data = json.loads(data)
+ assert data["params"] == {}
+ assert "Content-Type" not in HTTPHeaderDict(data["headers"])
+
def test_bad_connect(self):
with HTTPConnectionPool("badhost.invalid", self.port) as pool:
with pytest.raises(MaxRetryError) as e:
Index: urllib3-1.25.10/src/urllib3/__init__.py
===================================================================
--- urllib3-1.25.10.orig/src/urllib3/__init__.py
+++ urllib3-1.25.10/src/urllib3/__init__.py
@@ -15,6 +15,7 @@ from .util.url import get_host
from .util.timeout import Timeout
from .util.retry import Retry
from ._version import __version__
+from ._collections import HTTPHeaderDict
# Set default logging handler to avoid "No handler found" warnings.
@@ -28,6 +29,7 @@ __version__ = __version__
__all__ = (
"HTTPConnectionPool",
"HTTPSConnectionPool",
+ "HTTPHeaderDict",
"PoolManager",
"ProxyManager",
"HTTPResponse",