{"record":{"id":"ab2cc427680cb9dc","repo":"langchain-ai/langchain","slug":"private-ip-range","errorCode":null,"errorMessage":"private IP range","messagePattern":"private IP range","errorType":"exception","errorClass":"SSRFBlockedError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/_security/_policy.py","lineNumber":212,"sourceCode":"def validate_resolved_ip(ip_str: str, policy: SSRFPolicy) -> None:\n    \"\"\"Validate a resolved IP address against the SSRF policy.\n\n    Raises SSRFBlockedError if the IP is blocked.\n    \"\"\"\n    try:\n        addr = ipaddress.ip_address(ip_str)\n    except ValueError as exc:\n        msg = \"invalid IP address\"\n        raise SSRFBlockedError(msg) from exc\n\n    if isinstance(addr, ipaddress.IPv6Address):\n        inner = _extract_embedded_ipv4(addr)\n        if inner is not None:\n            addr = inner\n\n    reason = _ip_in_blocked_networks(addr, policy)\n    if reason is not None:\n        raise SSRFBlockedError(reason)\n\n\ndef validate_hostname(hostname: str, policy: SSRFPolicy) -> None:\n    \"\"\"Validate a hostname against the SSRF policy.\n\n    Raises SSRFBlockedError if the hostname is blocked.\n    \"\"\"\n    lower = hostname.lower()\n\n    if policy.block_localhost and lower in _LOCALHOST_NAMES:\n        msg = \"localhost address\"\n        raise SSRFBlockedError(msg)\n\n    if policy.block_cloud_metadata and lower in _CLOUD_METADATA_HOSTNAMES:\n        msg = \"cloud metadata endpoint\"\n        raise SSRFBlockedError(msg)\n\n    if policy.block_k8s_internal and lower.endswith(_K8S_SUFFIX):","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/_security/_policy.py#L194-L230","documentation":"`\"private IP range\"` is the reason string returned by `_ip_in_blocked_networks` and raised as `SSRFBlockedError` at the `raise SSRFBlockedError(reason)` line in `validate_resolved_ip` when the resolved IP falls inside a private/reserved network (RFC1918 10/8, 172.16/12, 192.168/16, link-local 169.254/16, IPv6 ULA fc00::/7, etc.) and the policy blocks private ranges (the default). This is the core SSRF defense: an attacker-controlled URL must not be able to reach internal network space.","triggerScenarios":"`await validate_url('http://192.168.1.10:8080/api')`, `await validate_url('http://10.0.0.5/metadata')`, or a hostname like `my-internal-service.internal` that DNS resolves to a private IP, under the default `SSRFPolicy(block_private_ip=True)`. IPv4-mapped IPv6 addresses (`::ffff:10.0.0.5`) are unwrapped via `_extract_embedded_ipv4` and hit the same block.","commonSituations":"Running langchain locally or in a container where a legitimate internal endpoint (local Ollama at `http://192.168.x.x`, an internal gateway, a docker-network service) is the target; or in tests using `http://localhost`-adjacent private addresses. Developers are surprised because the URL works in curl but the library's SSRF guard blocks it before any request is made.","solutions":["If the private endpoint is trusted, pass `allow_private=True` to the API that builds the policy (`_policy_for(allow_private=...)` — e.g. the `allow_private`/`allow_http` flags on the public SSRF helper in `_ssrf_protection.py`) so private ranges are permitted.","Add the specific hostname to `policy.allowed_hosts` (and note `_effective_allowed_hosts` auto-allows `localhost`/`testserver` when `LANGCHAIN_ENV` starts with 'local').","Point at a public endpoint or an allowed proxy instead of the internal address.","For local dev/tests, set `LANGCHAIN_ENV=local` (or `local_test` for testserver-style hostnames) so the policy relaxes localhost/test hosts."],"exampleFix":"# before\nresult = await validate_url('http://192.168.1.20:11434/api/generate', DEFAULT_SSRF_POLICY)\n# SSRFBlockedError: private IP range\n\n# after\npolicy = SSRFPolicy(allowed_hosts={'192.168.1.20'})\nresult = await validate_url('http://192.168.1.20:11434/api/generate', policy)","handlingStrategy":"try-catch","validationCode":"import ipaddress\n\ndef is_private_target(host: str) -> bool:\n    try:\n        return ipaddress.ip_address(host).is_private\n    except ValueError:\n        return False  # hostname: resolution decides later","typeGuard":null,"tryCatchPattern":"from langchain_core._security._policy import SSRFBlockedError\n\ntry:\n    await validate_url(url, policy)\nexcept SSRFBlockedError as e:\n    if \"private IP range\" in str(e) and host_is_trusted_internal(url):\n        return await validate_url(url, SSRFPolicy(allowed_hosts={urlparse(url).hostname}))\n    raise","preventionTips":["Maintain an explicit allowed_hosts list for legitimate internal endpoints instead of disabling private-IP blocking wholesale.","Reserve allow_private=True for user-configured base URLs, never model-chosen URLs.","Set LANGCHAIN_ENV=local in dev/test so localhost-style hosts validate cleanly."],"tags":["ssrf","security","network","private-ip"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}