+
    Kif                         R t ^ RIt^ RIt^ RIt^ RIHt ]P                  ! ]4      t]	! RR04      t
]P                  ! R4      tR R ltR R	 ltR# )
us  URL safety checks — blocks requests to private/internal network addresses.

Prevents SSRF (Server-Side Request Forgery) where a malicious prompt or
skill could trick the agent into fetching internal resources like cloud
metadata endpoints (169.254.169.254), localhost services, or private
network hosts.

Limitations (documented, not fixable at pre-flight level):
  - DNS rebinding (TOCTOU): an attacker-controlled DNS server with TTL=0
    can return a public IP for the check, then a private IP for the actual
    connection. Fixing this requires connection-level validation (e.g.
    Python's Champion library or an egress proxy like Stripe's Smokescreen).
  - Redirect-based bypass in vision_tools is mitigated by an httpx event
    hook that re-validates each redirect target. Web tools use third-party
    SDKs (Firecrawl/Tavily) where redirect handling is on their servers.
N)urlparsezmetadata.google.internalzmetadata.googz100.64.0.0/10c                n    V ^8  d   QhR\         P                  \         P                  ,          R\        /# )   ipreturn)	ipaddressIPv4AddressIPv6Addressbool)formats   "-/home/ubuntu/hermes-agent/tools/url_safety.py__annotate__r   &   s+     	 	y,,y/D/DD 	 	    c                    V P                   '       g7   V P                  '       g%   V P                  '       g   V P                  '       d   R# V P                  '       g   V P
                  '       d   R# V \        9   d   R# R# )z<Return True if the IP should be blocked for SSRF protection.TF)
is_privateis_loopbackis_link_localis_reservedis_multicastis_unspecified_CGNAT_NETWORK)r   s   &r   _is_blocked_ipr   &   sN    	}}}"*:*:*:bnnn	"+++	^r   c                0    V ^8  d   QhR\         R\        /# )r   urlr   )strr
   )r   s   "r   r   r   2   s     . .S .T .r   c                    \        V 4      pVP                  ;'       g    RP                  4       P                  4       pV'       g   R# V\        9   d   \
        P                  RV4       R#  \        P                  ! VR\        P                  \        P                  4      pT FR  w  p  rVT^ ,          p \        P                  ! T4      p\        T4      '       g   K;  \
        P                  RY'4        R# 	  R#   \        P                   d    \
        P                  RT4        R# i ; i  \         d     K  i ; i  \          d"   p	\
        P                  RY	4        Rp	?	R# Rp	?	ii ; i)	zReturn True if the URL target is not a private/internal address.

Resolves the hostname to an IP and checks against private ranges.
Fails closed: DNS errors and unexpected exceptions block the request.
 Fz(Blocked request to internal hostname: %sNu1   Blocked request — DNS resolution failed for: %sz5Blocked request to private/internal address: %s -> %sTu5   Blocked request — URL safety check error for %s: %s)r   hostnamestriplower_BLOCKED_HOSTNAMESloggerwarningsocketgetaddrinfo	AF_UNSPECSOCK_STREAMgaierrorr   
ip_address
ValueErrorr   	Exception)
r   parsedr   	addr_infofamily_sockaddrip_strr   excs
   &         r   is_safe_urlr2   2   s<   (#OO))r00288: ))NNExP	**8T6;K;KVM_M_`I *3%FAq!a[F))&1 b!!K  *3 )  	 NNNPXY		     	NPSY	sv   E  &E  	!E  -5C< "E  6D.E  E  8E  <+D+'E  *D++E  .D=9E  <D==E   E,E''E,)__doc__r   loggingr#   urllib.parser   	getLogger__name__r!   	frozensetr    
ip_networkr   r   r2    r   r   <module>r;      s\   "    !			8	$     %%o6	.r   