{"record":{"id":"f84525ea8cb38e8f","repo":"microsoft/semantic-kernel","slug":"the-request-uri-url-is-not-allowed-host-resol","errorCode":null,"errorMessage":"The request URI '{url}' is not allowed: host resolves to a {category} address ({address}), which is blocked by default to prevent Server-Side Request Forgery (SSRF). To allow this URL, add it to server_url_validation_allowed_base_urls or set allow_private_network_access=True.","messagePattern":"The request URI '(.+?)' is not allowed: host resolves to a (.+?) address \\((.+?)\\), which is blocked by default to prevent Server-Side Request Forgery \\(SSRF\\)\\. To allow this URL, add it to server_url_validation_allowed_base_urls or set allow_private_network_access=True\\.","errorType":"exception","errorClass":"FunctionExecutionException","httpStatus":null,"severity":"error","filePath":"python/semantic_kernel/connectors/openapi_plugin/server_url_validator.py","lineNumber":186,"sourceCode":"        ) from exc\n\n    addresses: list[ipaddress.IPv4Address | ipaddress.IPv6Address] = []\n    seen_addresses: set[str] = set()\n    for family, _, _, _, sockaddr in addr_info:\n        if family not in (socket.AF_INET, socket.AF_INET6):\n            continue\n        address = ipaddress.ip_address(sockaddr[0])\n        address_string = str(address)\n        if address_string not in seen_addresses:\n            addresses.append(address)\n            seen_addresses.add(address_string)\n    return addresses\n\n\ndef _ensure_public_address(url: str, address: ipaddress.IPv4Address | ipaddress.IPv6Address) -> None:\n    blocked, category = try_categorize_non_public_address(address)\n    if blocked:\n        raise FunctionExecutionException(\n            f\"The request URI '{url}' is not allowed: host resolves to a {category} address ({address}), \"\n            \"which is blocked by default to prevent Server-Side Request Forgery (SSRF). \"\n            \"To allow this URL, add it to server_url_validation_allowed_base_urls or set \"\n            \"allow_private_network_access=True.\"\n        )\n\n\ndef _try_classify_ipv4(address: ipaddress.IPv4Address) -> tuple[bool, str]:\n    b0, b1, b2, _ = address.packed\n\n    if b0 == 0:\n        return True, \"unspecified\"\n    if b0 == 10:\n        return True, \"private (RFC1918)\"\n    if b0 == 127:\n        return True, \"loopback\"\n    if b0 == 169 and b1 == 254:\n        return True, \"link-local\"","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/microsoft/semantic-kernel/blob/c028a0c7dc4f0814cdcbaba9d998f187a41197bf/python/semantic_kernel/connectors/openapi_plugin/server_url_validator.py#L168-L204","documentation":"Thrown when the host resolves to a non-public IP address. Categories blocked: loopback (127.0.0.0/8), RFC1918 private (10/8, 172.16/12, 192.168/16), link-local (169.254/16), unspecified (0/8), carrier-grade NAT, benchmarking, reserved, multicast, and IPv6 loopback/ULA/link-local/multicast/reserved. This is the core SSRF guard.","triggerScenarios":"Server URL host resolves to 127.0.0.1, 10.x, 192.168.x, 169.254.169.254 (cloud metadata), fc00::/7, ::1, etc.; or a DNS rebinding attack that flips a public-looking name to a private IP at request time.","commonSituations":"Pointing at localhost/127.0.0.1 for local dev; reaching an internal service on a private IP; cloud metadata endpoint abuse (169.254.169.254); DNS rebinding from an attacker-controlled hostname.","solutions":["Add the specific trusted base URL to allowed_base_urls to bypass all host checks for that destination","Set allow_private_network_access=True to skip the public-address check entirely (only when you fully trust and control the target)","Ensure you are hitting a public hostname that resolves to a genuinely public IP","Never allow untrusted/user-supplied URLs to reach internal or metadata addresses"],"exampleFix":"# before\noptions = ServerUrlValidationOptions()  # default: blocks private addresses\nawait validate_server_url('https://internal.svc.cluster.local/api', options)  # raises 1507\n\n# after - explicitly trust the internal base\noptions = ServerUrlValidationOptions(allowed_base_urls=['https://internal.svc.cluster.local/api'])\nawait validate_server_url('https://internal.svc.cluster.local/api/run', options)","handlingStrategy":"validation","validationCode":"import ipaddress, socket\n\ndef host_is_public(host: str) -> bool:\n    # if host is already an IP\n    try:\n        ip = ipaddress.ip_address(host)\n    except ValueError:\n        try:\n            infos = socket.getaddrinfo(host, None)\n        except OSError:\n            return False\n        ips = [ipaddress.ip_address(i[4][0]) for i in infos if i[0] in (socket.AF_INET, socket.AF_INET6)]\n    else:\n        ips = [ip]\n    for ip in ips:\n        if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_unspecified or ip.is_reserved or ip.is_multicast:\n            return False\n    return True\n\nif not host_is_public(urlparse(url).hostname):\n    # either reject or add the base url to allowed_base_urls / set allow_private_network_access\n    ...","typeGuard":null,"tryCatchPattern":"try:\n    await validate_server_url(url, options)\nexcept FunctionExecutionException as e:\n    if 'blocked by default to prevent Server-Side Request Forgery' in str(e):\n        # add the trusted base url to allowed_base_urls, or set allow_private_network_access=True\n        ...","preventionTips":["Never let untrusted/user-supplied URLs reach internal or metadata addresses","Use allow_private_network_access=True only for destinations you fully control","Prefer allow-listing specific trusted base URLs over broad private-network access","Watch for DNS rebinding: the check resolves at request time, so recheck if URLs are dynamic"],"tags":["ssrf","security","dns","private-network"],"backgroundTag":null,"analyzedSha":"c028a0c7dc4f0814cdcbaba9d998f187a41197bf","analyzedAt":"2026-08-13T13:48:05.040Z","schemaVersion":2},"datasetVersion":"2026-08-13T14:17:21.547Z"}