From 65a14116d503dd544cbba92284517c99e5e9a448 Mon Sep 17 00:00:00 2001 From: Richard Weickelt Date: Wed, 11 Dec 2024 22:04:43 +0100 Subject: [PATCH] requests: Do not leak header modifications when calling request. The requests() function takes a headers dict argument (call-by-reference). This object is then modified in the function. For instance the host is added and authentication information. Such behavior is not expected. It is also problematic: - Modifications of the header dictionary will be visible on the caller site. - When reusing the same (supposedly read-only) headers object for differenct calls, the second call will apparently re-use wrong headers from the previous call and may fail. This patch should also fix #839. Unfortunately the copy operation does not preserve the key order and we have to touch the existing test cases. Signed-off-by: Richard Weickelt --- python-ecosys/requests/requests/__init__.py | 2 ++ python-ecosys/requests/test_requests.py | 13 +++++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/python-ecosys/requests/requests/__init__.py b/python-ecosys/requests/requests/__init__.py index a9a183619..2951035f7 100644 --- a/python-ecosys/requests/requests/__init__.py +++ b/python-ecosys/requests/requests/__init__.py @@ -46,6 +46,8 @@ def request( ): if headers is None: headers = {} + else: + headers = headers.copy() redirect = None # redirection url, None means no redirection chunked_data = data and getattr(data, "__next__", None) and not getattr(data, "__len__", None) diff --git a/python-ecosys/requests/test_requests.py b/python-ecosys/requests/test_requests.py index 513e533a3..ac77291b0 100644 --- a/python-ecosys/requests/test_requests.py +++ b/python-ecosys/requests/test_requests.py @@ -102,11 +102,11 @@ def chunks(): def test_overwrite_get_headers(): response = requests.request( - "GET", "http://example.com", headers={"Connection": "keep-alive", "Host": "test.com"} + "GET", "http://example.com", headers={"Host": "test.com", "Connection": "keep-alive"} ) assert response.raw._write_buffer.getvalue() == ( - b"GET / HTTP/1.0\r\n" + b"Host: test.com\r\n" + b"Connection: keep-alive\r\n\r\n" + b"GET / HTTP/1.0\r\n" + b"Connection: keep-alive\r\n" + b"Host: test.com\r\n\r\n" ), format_message(response) @@ -145,6 +145,14 @@ def chunks(): ), format_message(response) +def test_do_not_modify_headers_argument(): + global do_not_modify_this_dict + do_not_modify_this_dict = {} + requests.request("GET", "http://example.com", headers=do_not_modify_this_dict) + + assert do_not_modify_this_dict == {}, do_not_modify_this_dict + + test_simple_get() test_get_auth() test_get_custom_header() @@ -153,3 +161,4 @@ def chunks(): test_overwrite_get_headers() test_overwrite_post_json_headers() test_overwrite_post_chunked_data_headers() +test_do_not_modify_headers_argument()