Ensure functions have docstrings for documentation
def validate_safe_url(
1"""SSRF Protection - thin wrapper raising ValueError for internal callers.23Delegates all validation to `langchain_core._security._policy`.4"""56import os7import socket8from typing import Annotated, Any9from urllib.parse import urlparse1011from pydantic import (12 AnyHttpUrl,13 BeforeValidator,14 HttpUrl,15)1617from langchain_core._security._exceptions import SSRFBlockedError18from langchain_core._security._policy import (19 SSRFPolicy,20)21from langchain_core._security._policy import (22 validate_resolved_ip as _validate_resolved_ip,23)24from langchain_core._security._policy import (25 validate_url_sync as _validate_url_sync,26)272829def _policy_for(*, allow_private: bool, allow_http: bool) -> SSRFPolicy:30 """Build an `SSRFPolicy` from the legacy flag interface."""31 schemes = frozenset({"http", "https"}) if allow_http else frozenset({"https"})32 return SSRFPolicy(33 allowed_schemes=schemes,34 block_private_ips=not allow_private,35 block_localhost=not allow_private,36 block_cloud_metadata=True,37 block_k8s_internal=True,38 )394041def validate_safe_url(42 url: str | AnyHttpUrl,43 *,44 allow_private: bool = False,45 allow_http: bool = True,46) -> str:47 """Validate a URL for SSRF protection.4849 This function validates URLs to prevent Server-Side Request Forgery (SSRF) attacks50 by blocking requests to private networks and cloud metadata endpoints.5152 Args:53 url: The URL to validate (string or Pydantic HttpUrl).54 allow_private: If `True`, allows private IPs and localhost (for development).55 Cloud metadata endpoints are ALWAYS blocked.56 allow_http: If `True`, allows both HTTP and HTTPS. If `False`, only HTTPS.5758 Returns:59 The validated URL as a string.6061 Raises:62 ValueError: If URL is invalid or potentially dangerous.63 """64 url_str = str(url)65 parsed = urlparse(url_str)66 hostname = parsed.hostname or ""6768 # Test-environment bypass (preserved from original implementation)69 if (70 os.environ.get("LANGCHAIN_ENV") == "local_test"71 and hostname.startswith("test")72 and "server" in hostname73 ):74 return url_str7576 policy = _policy_for(allow_private=allow_private, allow_http=allow_http)7778 # Synchronous scheme + hostname checks79 try:80 _validate_url_sync(url_str, policy)81 except SSRFBlockedError as exc:82 raise ValueError(str(exc)) from exc8384 # DNS resolution and IP validation85 try:86 addr_info = socket.getaddrinfo(87 hostname,88 parsed.port or (443 if parsed.scheme == "https" else 80),89 socket.AF_UNSPEC,90 socket.SOCK_STREAM,91 )9293 for result in addr_info:94 ip_str: str = result[4][0] # type: ignore[assignment]95 try:96 _validate_resolved_ip(ip_str, policy)97 except SSRFBlockedError as exc:98 raise ValueError(str(exc)) from exc99100 except socket.gaierror as e:101 msg = f"Failed to resolve hostname '{hostname}': {e}"102 raise ValueError(msg) from e103 except OSError as e:104 msg = f"Network error while validating URL: {e}"105 raise ValueError(msg) from e106107 return url_str108109110def is_safe_url(111 url: str | AnyHttpUrl,112 *,113 allow_private: bool = False,114 allow_http: bool = True,115) -> bool:116 """Non-throwing version of `validate_safe_url`."""117 try:118 validate_safe_url(url, allow_private=allow_private, allow_http=allow_http)119 except ValueError:120 return False121 else:122 return True123124125def _validate_url_ssrf_strict(v: Any) -> Any:126 """Validate URL for SSRF protection (strict mode)."""127 if isinstance(v, str):128 validate_safe_url(v, allow_private=False, allow_http=True)129 return v130131132def _validate_url_ssrf_https_only(v: Any) -> Any:133 if isinstance(v, str):134 validate_safe_url(v, allow_private=False, allow_http=False)135 return v136137138def _validate_url_ssrf_relaxed(v: Any) -> Any:139 """Validate URL for SSRF protection (relaxed mode - allows private IPs)."""140 if isinstance(v, str):141 validate_safe_url(v, allow_private=True, allow_http=True)142 return v143144145# Annotated types with SSRF protection146SSRFProtectedUrl = Annotated[HttpUrl, BeforeValidator(_validate_url_ssrf_strict)]147SSRFProtectedUrlRelaxed = Annotated[148 HttpUrl, BeforeValidator(_validate_url_ssrf_relaxed)149]150SSRFProtectedHttpsUrl = Annotated[151 HttpUrl, BeforeValidator(_validate_url_ssrf_https_only)152]153SSRFProtectedHttpsUrlStr = Annotated[154 str, BeforeValidator(_validate_url_ssrf_https_only)155]
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.