{"record":{"id":"cc2518b2e6541e74","repo":"ComposioHQ/composio","slug":"refusing-to-talk-to-hostname-the-connection-w","errorCode":null,"errorMessage":"Refusing to talk to \"{hostname}\": the connection was established to {peer}, not to the validated address {address}","messagePattern":"Refusing to talk to \"(.+?)\": the connection was established to (.+?), not to the validated address (.+?)","errorType":"exception","errorClass":"BlockedInternalUrlError","httpStatus":null,"severity":"error","filePath":"python/composio/utils/url_safety.py","lineNumber":290,"sourceCode":"    signals ``Connection: close``, while the body stays readable.\n    \"\"\"\n    try:\n        peer = sock.getpeername()[0]\n    except (AttributeError, OSError, IndexError):\n        return\n\n    try:\n        connected_to_pinned = ipaddress.ip_address(peer) == ipaddress.ip_address(\n            address\n        )\n    except ValueError:\n        connected_to_pinned = peer == address\n\n    if connected_to_pinned:\n        return\n\n    sock.close()\n    raise BlockedInternalUrlError(\n        f'Refusing to talk to \"{hostname}\": the connection was established to '\n        f\"{peer}, not to the validated address {address}\"\n    )\n\n\ndef _proxy_applies(url: str, proxies: t.Optional[t.Mapping[str, str]]) -> bool:\n    \"\"\"Whether Requests would send ``url`` through a proxy.\n\n    Requests honours ``HTTP_PROXY``/``HTTPS_PROXY``/``ALL_PROXY`` (minus\n    ``NO_PROXY``) by default. Through a proxy the socket is dialled to the\n    *proxy*, so pinning the target address would connect to the wrong host\n    entirely.\n\n    Residual: proxied requests keep only the pre-flight check, because the\n    proxy resolves the hostname itself and the SDK cannot see or pin that\n    resolution. A rebinding window therefore remains for callers that run\n    behind a proxy — including one inherited from the environment.\n    \"\"\"","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/url_safety.py#L272-L308","documentation":"After DNS validation, the request is pinned to a specific validated IP via a custom HTTPAdapter. _assert_pinned_peer verifies via the TLS peer certificate that the host actually served by the connection matches the pinned address; a mismatch (DNS change mid-flight / rebinding) fails closed and the socket is closed.","triggerScenarios":"DNS answer changing between validation and connection (TOCTOU rebinding attack), or environments where the adapter's connection lands on a different address (some proxy/transparent NAT setups). Reproduce via test_peer_mismatch_fails_closed.","commonSituations":"DNS-rebinding attacks against the fetch helper, rotating DNS with short TTLs, captive portals / transparent proxies rewriting connections.","solutions":["Retry the request — a fresh validation cycle will pin the new address","Check whether a proxy/VPN is intercepting connections and disable it for this host","Confirm DNS stability (dig +short repeatedly) for the target host","If using a local proxy, exclude the target from proxying (NO_PROXY / trust_env=False semantics)"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        return safe_request('GET', url)\n    except BlockedInternalUrlError as e:\n        if 'not to the validated address' not in str(e) or attempt == 2:\n            raise\n        time.sleep(0.5)  # DNS changed mid-flight; revalidate on retry","preventionTips":["Use stable-DNS hostnames (low churn) for fetched targets","Disable transparent proxies/VPNs that rewrite connections"],"tags":["security","ssrf","dns","tls","python"],"backgroundTag":"certificate-host-mismatch","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}