From 30deef45d924675f408ab5154df31605c08796ce Mon Sep 17 00:00:00 2001 From: -LAN- Date: Fri, 22 May 2026 09:31:46 +0800 Subject: [PATCH] fix(api): pass SSL verify flag to SSRF proxy mounts (#36455) --- api/core/helper/ssrf_proxy.py | 7 +++-- .../unit_tests/core/helper/test_ssrf_proxy.py | 31 ++++++++++++++++++- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/api/core/helper/ssrf_proxy.py b/api/core/helper/ssrf_proxy.py index 91e92712b7..b2493934bf 100644 --- a/api/core/helper/ssrf_proxy.py +++ b/api/core/helper/ssrf_proxy.py @@ -43,13 +43,16 @@ request_error = httpx.RequestError max_retries_exceeded_error = MaxRetriesExceededError -def _create_proxy_mounts() -> dict[str, httpx.HTTPTransport]: +def _create_proxy_mounts(verify: bool) -> dict[str, httpx.HTTPTransport]: + """Build per-scheme proxy transports with the same TLS policy as the SSRF client.""" return { "http://": httpx.HTTPTransport( proxy=dify_config.SSRF_PROXY_HTTP_URL, + verify=verify, ), "https://": httpx.HTTPTransport( proxy=dify_config.SSRF_PROXY_HTTPS_URL, + verify=verify, ), } @@ -64,7 +67,7 @@ def _build_ssrf_client(verify: bool) -> httpx.Client: if dify_config.SSRF_PROXY_HTTP_URL and dify_config.SSRF_PROXY_HTTPS_URL: return httpx.Client( - mounts=_create_proxy_mounts(), + mounts=_create_proxy_mounts(verify=verify), verify=verify, limits=_SSRF_CLIENT_LIMITS, ) diff --git a/api/tests/unit_tests/core/helper/test_ssrf_proxy.py b/api/tests/unit_tests/core/helper/test_ssrf_proxy.py index d9fed9ae2a..0659765da4 100644 --- a/api/tests/unit_tests/core/helper/test_ssrf_proxy.py +++ b/api/tests/unit_tests/core/helper/test_ssrf_proxy.py @@ -1,4 +1,4 @@ -from unittest.mock import MagicMock, patch +from unittest.mock import ANY, MagicMock, call, patch import httpx import pytest @@ -6,6 +6,7 @@ import pytest from core.helper.ssrf_proxy import ( SSRF_DEFAULT_MAX_RETRIES, SSRFProxy, + _build_ssrf_client, _get_user_provided_host_header, _to_graphon_http_response, graphon_ssrf_proxy, @@ -41,6 +42,34 @@ def test_retry_exceed_max_retries(mock_get_client): assert str(e.value) == f"Reached maximum retries ({SSRF_DEFAULT_MAX_RETRIES - 1}) for URL http://example.com" +def test_build_ssrf_client_passes_ssl_verify_to_proxy_mount_transports(): + mock_client = MagicMock() + http_transport = MagicMock() + https_transport = MagicMock() + + with ( + patch("core.helper.ssrf_proxy.dify_config.SSRF_PROXY_ALL_URL", None), + patch("core.helper.ssrf_proxy.dify_config.SSRF_PROXY_HTTP_URL", "http://proxy.example.com:8080"), + patch("core.helper.ssrf_proxy.dify_config.SSRF_PROXY_HTTPS_URL", "http://proxy.example.com:8443"), + patch("core.helper.ssrf_proxy.httpx.HTTPTransport", side_effect=[http_transport, https_transport]) as transport, + patch("core.helper.ssrf_proxy.httpx.Client", return_value=mock_client) as client, + ): + ssrf_client = _build_ssrf_client(verify=False) + + assert ssrf_client is mock_client + transport.assert_has_calls( + [ + call(proxy="http://proxy.example.com:8080", verify=False), + call(proxy="http://proxy.example.com:8443", verify=False), + ], + ) + client.assert_called_once_with( + mounts={"http://": http_transport, "https://": https_transport}, + verify=False, + limits=ANY, + ) + + class TestGetUserProvidedHostHeader: """Tests for _get_user_provided_host_header function."""