fix(openai): use SSRF-safe transport for image token counting (#36819)

This commit is contained in:
ccurme
2026-04-16 09:52:02 -04:00
committed by GitHub
parent 338aa8131a
commit 0516156ef9
2 changed files with 18 additions and 27 deletions

View File

@@ -141,6 +141,7 @@ from langchain_openai.chat_models._compat import (
from langchain_openai.data._profiles import _PROFILES
if TYPE_CHECKING:
import httpx
from langchain_core.language_models import ModelProfile
from openai.types.responses import Response
@@ -150,6 +151,20 @@ logger = logging.getLogger(__name__)
# https://www.python-httpx.org/advanced/ssl/#configuring-client-instances
global_ssl_context = ssl.create_default_context(cafile=certifi.where())
_ssrf_client: httpx.Client | None = None
def _get_ssrf_safe_client() -> httpx.Client:
global _ssrf_client
if _ssrf_client is None:
from langchain_core._security._transport import ssrf_safe_client
_ssrf_client = ssrf_safe_client(
verify=global_ssl_context, follow_redirects=False
)
return _ssrf_client
_MODEL_PROFILES = cast(ModelProfileRegistry, _PROFILES)
@@ -3638,28 +3653,7 @@ def _url_to_size(image_source: str) -> tuple[int, int] | None:
)
return None
if _is_url(image_source):
try:
import httpx
except ImportError:
logger.info(
"Unable to count image tokens. To count image tokens please install "
"`pip install -U httpx`."
)
return None
# Validate URL for SSRF protection
try:
from langchain_core._security._ssrf_protection import validate_safe_url
validate_safe_url(image_source, allow_private=False, allow_http=True)
except ImportError:
logger.warning(
"SSRF protection not available. "
"Update langchain-core to get SSRF protection."
)
except ValueError as e:
logger.warning("Image URL failed SSRF validation: %s", e)
return None
import httpx
# Set reasonable limits to prevent resource exhaustion
# Timeout prevents indefinite hangs on slow/malicious servers
@@ -3668,10 +3662,7 @@ def _url_to_size(image_source: str) -> tuple[int, int] | None:
max_size = 50 * 1024 * 1024 # 50 MB
try:
response = httpx.get(
image_source,
timeout=timeout,
)
response = _get_ssrf_safe_client().get(image_source, timeout=timeout)
response.raise_for_status()
# Check response size before loading into memory

View File

@@ -23,7 +23,7 @@ classifiers = [
version = "1.1.13"
requires-python = ">=3.10.0,<4.0.0"
dependencies = [
"langchain-core>=1.2.29,<2.0.0",
"langchain-core>=1.2.31,<2.0.0",
"openai>=2.26.0,<3.0.0",
"tiktoken>=0.7.0,<1.0.0",
]