From 5247f3e591d17eb0c8920358b82c8f7d6f0b681d Mon Sep 17 00:00:00 2001 From: Tatamis <80774326+Tatamis@users.noreply.github.com> Date: Thu, 17 Sep 2026 18:26:34 +0300 Subject: [PATCH 1/2] fix: user-configured proxies overridden by http_proxy/https_proxy env vars ResilientSession.request() never forwarded a "proxies" kwarg to requests.Session.request(), relying entirely on self.proxies as the fallback. But requests.Session.merge_environment_settings() only uses self.proxies as a fallback for keys missing from the per-request proxies dict; when no "proxies" kwarg is passed, that dict starts empty and gets filled first from the http_proxy/https_proxy environment variables via setdefault(), before merge_setting() lets those request-level values win over self.proxies for any matching key. The end result: a JIRA client configured with an explicit proxies dict silently used the environment's proxies instead whenever those env vars were also set. Pass self.proxies through explicitly as the "proxies" kwarg in _jira_prepare() so it's already populated before the environment fallback runs, and can only fill gaps rather than being overridden. Fixes #2398 --- jira/resilientsession.py | 12 ++++++++++++ tests/test_resilientsession.py | 27 +++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/jira/resilientsession.py b/jira/resilientsession.py index 39ce7643d..fd709eaf9 100644 --- a/jira/resilientsession.py +++ b/jira/resilientsession.py @@ -188,6 +188,18 @@ def _jira_prepare(self, **original_kwargs) -> dict: if "verify" not in prepared_kwargs: prepared_kwargs["verify"] = self.verify + if "proxies" not in prepared_kwargs and self.proxies: + # Session.request() only falls back to self.proxies when no + # "proxies" kwarg is passed at all, but by then + # merge_environment_settings() has already used setdefault() to + # fill an empty request-level proxies dict from the http_proxy/ + # https_proxy environment variables, and merge_setting() lets + # that request-level dict win over self.proxies for any matching + # key. Passing self.proxies through explicitly here means it's + # already populated before the environment fallback runs, so it + # can only fill in gaps rather than being overridden by them. + prepared_kwargs["proxies"] = self.proxies + return prepared_kwargs def request( # type: ignore[override] # An intentionally different override diff --git a/tests/test_resilientsession.py b/tests/test_resilientsession.py index 2d0ed9047..9e9735944 100644 --- a/tests/test_resilientsession.py +++ b/tests/test_resilientsession.py @@ -254,3 +254,30 @@ def test_empty_dict_body_not_forwarded(mocked_request_method: Mock): session.get(url="mocked_url", data={}) kwargs = mocked_request_method.call_args.kwargs assert kwargs.get("data") == {} + + +@patch("requests.Session.request") +def test_configured_proxies_are_forwarded(mocked_request_method: Mock): + # Disable retries for this test. + session = jira.resilientsession.ResilientSession(max_retries=0) + # A user-configured session proxy must be passed explicitly as the + # per-request "proxies" kwarg. Left unset, requests.Session.request() + # lets an env-derived http_proxy/https_proxy value (from + # merge_environment_settings()'s setdefault()) win over self.proxies + # for any matching key. + session.proxies = { + "http": "http://localhost:3128", + "https": "http://localhost:3128", + } + session.get(url="mocked_url") + kwargs = mocked_request_method.call_args.kwargs + assert kwargs.get("proxies") == session.proxies + + +@patch("requests.Session.request") +def test_no_proxies_kwarg_when_unconfigured(mocked_request_method: Mock): + # Disable retries for this test. + session = jira.resilientsession.ResilientSession(max_retries=0) + session.get(url="mocked_url") + kwargs = mocked_request_method.call_args.kwargs + assert "proxies" not in kwargs From 7bc50b58a016f2ade3d19ad1892d3dc59f6acfd0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:27:07 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- jira/resilientsession.py | 2 +- tests/test_resilientsession.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jira/resilientsession.py b/jira/resilientsession.py index fd709eaf9..4cf7cfb8e 100644 --- a/jira/resilientsession.py +++ b/jira/resilientsession.py @@ -193,7 +193,7 @@ def _jira_prepare(self, **original_kwargs) -> dict: # "proxies" kwarg is passed at all, but by then # merge_environment_settings() has already used setdefault() to # fill an empty request-level proxies dict from the http_proxy/ - # https_proxy environment variables, and merge_setting() lets + # https_proxy environment variables, and merge_setting() let's # that request-level dict win over self.proxies for any matching # key. Passing self.proxies through explicitly here means it's # already populated before the environment fallback runs, so it diff --git a/tests/test_resilientsession.py b/tests/test_resilientsession.py index 9e9735944..f23c917d1 100644 --- a/tests/test_resilientsession.py +++ b/tests/test_resilientsession.py @@ -262,7 +262,7 @@ def test_configured_proxies_are_forwarded(mocked_request_method: Mock): session = jira.resilientsession.ResilientSession(max_retries=0) # A user-configured session proxy must be passed explicitly as the # per-request "proxies" kwarg. Left unset, requests.Session.request() - # lets an env-derived http_proxy/https_proxy value (from + # let's an env-derived http_proxy/https_proxy value (from # merge_environment_settings()'s setdefault()) win over self.proxies # for any matching key. session.proxies = {