§
    ‰eîiF$  ã            	       óì  — U d Z ddlZddlZddlZddlZddlmZ  ej        e¦  «        Z	 e
ddh¦  «        Z e
 ej        d¦  «         ej        d¦  «         ej        d¦  «         ej        d	¦  «         ej        d
¦  «        h¦  «        Z ej        d¦  «        fZ e
dh¦  «        Z ej        d¦  «        Zdadaeed<   defd„Zdd„Zdej        ej        z  defd„Zdededefd„Zdedefd„ZdS )uL  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.

The check can be globally disabled via ``security.allow_private_urls: true``
in config.yaml for environments where DNS resolves external domains to
private/benchmark-range IPs (OpenWrt routers, corporate proxies, VPNs
that use 198.18.0.0/15 or 100.64.0.0/10).  Even when disabled, cloud
metadata hostnames (metadata.google.internal, 169.254.169.254) are
**always** blocked â€” those are never legitimate agent targets.

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 is mitigated by httpx event hooks that re-validate
    each redirect target in vision_tools, gateway platform adapters, and
    media cache helpers. Web tools use third-party SDKs (Firecrawl/Tavily)
    where redirect handling is on their servers.
é    N)Úurlparsezmetadata.google.internalzmetadata.googz169.254.169.254z169.254.170.2z169.254.169.253zfd00:ec2::254z100.100.100.200z169.254.0.0/16zmultimedia.nt.qq.com.cnz100.64.0.0/10FÚ_cached_allow_privateÚreturnc                  ó>  — t           rt          S da dat          j        dd¦  «                             ¦   «                              ¦   «         } | dv r	dat          S | dv rt          S 	 ddlm}  |¦   «         }|                     d	i ¦  «        }t          |t          ¦  «        r|                     d
¦  «        r	dat          S |                     di ¦  «        }t          |t          ¦  «        r|                     d
¦  «        r	dat          S n# t          $ r Y nw xY wt          S )ac  Return True when the user has opted out of private-IP blocking.

    Checks (in priority order):
    1. ``HERMES_ALLOW_PRIVATE_URLS`` env var  (``true``/``1``/``yes``)
    2. ``security.allow_private_urls`` in config.yaml
    3. ``browser.allow_private_urls`` in config.yaml  (legacy / backward compat)

    Result is cached for the process lifetime.
    TFÚHERMES_ALLOW_PRIVATE_URLSÚ )ÚtrueÚ1Úyes)ÚfalseÚ0Únor   )Úread_raw_configÚsecurityÚallow_private_urlsÚbrowser)Ú_allow_private_resolvedr   ÚosÚgetenvÚstripÚlowerÚhermes_cli.configr   ÚgetÚ
isinstanceÚdictÚ	Exception)Úenv_valr   ÚcfgÚsecr   s        ú2/home/div/.hermes/hermes-agent/tools/url_safety.pyÚ_global_allow_private_urlsr!   N   sK  € õ ð %Ý$Ð$à"ÐØ!Ðõ ŒiÐ3°RÑ8Ô8×>Ò>Ñ@Ô@×FÒFÑHÔH€GØÐ&Ð&Ð&Ø $ÐÝ$Ð$ØÐ&Ð&Ð&å$Ð$ðØ5Ð5Ð5Ð5Ð5Ð5ØˆoÑÔˆà�gŠg�j "Ñ%Ô%ˆÝ�c�4Ñ Ô ð 	) S§W¢WÐ-AÑ%BÔ%Bð 	)Ø$(Ð!Ý(Ð(à—'’'˜) RÑ(Ô(ˆÝ�g�tÑ$Ô$ð 	)¨¯ªÐ5IÑ)JÔ)Jð 	)Ø$(Ð!Ý(Ð(øøÝð ð ð àˆðøøøõ !Ð s   Á%AD Â>AD Ä
DÄDc                  ó   — da dadS )u+   Reset the cached toggle â€” only for tests.FN)r   r   © ó    r    Ú_reset_allow_private_cacher%   }   s   € ð $ÐØ!ÐÐÐr$   Úipc                 óx   — | j         s| j        s| j        s| j        rdS | j        s| j        rdS | t          v rdS dS )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/   „   s[   € à	„}ð ˜œð ¨"Ô*:ð ¸b¼nð ØˆtØ	„ð ˜"Ô+ð Øˆtà	�^ÐÐØˆtØˆ5r$   ÚhostnameÚschemec                 ó    — |dk    o| t           v S )zGReturn True when a trusted HTTPS hostname may bypass IP-class blocking.Úhttps)Ú_TRUSTED_PRIVATE_IP_HOSTS)r0   r1   s     r    Ú_allows_private_ip_resolutionr5   �   s   € à�WÒÐF Õ-FÐ!FÐFr$   Úurlc                 ó’  ‡— 	 t          | ¦  «        }|j        pd                     ¦   «                              ¦   «                              d¦  «        }|j        pd                     ¦   «                              ¦   «         }|sdS |t          v rt                               d|¦  «         dS t          ¦   «         }t          ||¦  «        }	 t          j        |dt          j        t          j        ¦  «        }n1# t          j        $ r t                               d|¦  «         Y dS w xY w|D ]°\  }}}}}	|	d         }
	 t!          j        |
¦  «        Šn# t$          $ r Y Œ2w xY w‰t&          v s t)          ˆfd„t*          D ¦   «         ¦  «        rt                               d	||
¦  «          dS |s0|s.t-          ‰¦  «        rt                               d
||
¦  «          dS Œ±|rt                               d|¦  «         n|rt                               d|¦  «         dS # t0          $ r'}t                               d| |¦  «         Y d}~dS d}~ww xY w)uø  Return 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.

    When ``security.allow_private_urls`` is enabled (or the env var
    ``HERMES_ALLOW_PRIVATE_URLS=true``), private-IP blocking is skipped.
    Cloud metadata endpoints (169.254.169.254, metadata.google.internal)
    remain blocked regardless â€” they are never legitimate agent targets.
    r   ú.Fz(Blocked request to internal hostname: %sNu1   Blocked request â€” DNS resolution failed for: %sr   c              3   ó    •K  — | ]}‰|v V — Œ	d S )Nr#   )Ú.0Únetr&   s     €r    ú	<genexpr>zis_safe_url.<locals>.<genexpr>Â   s'   øè è € Ð/^Ð/^¸c°°c°	Ð/^Ð/^Ð/^Ð/^Ð/^Ð/^r$   z3Blocked request to cloud metadata address: %s -> %sz5Blocked request to private/internal address: %s -> %szKAllowing private/internal resolution (security.allow_private_urls=true): %szAAllowing trusted hostname despite private/internal resolution: %sTu5   Blocked request â€” URL safety check error for %s: %s)r   r0   r   r   Úrstripr1   Ú_BLOCKED_HOSTNAMESÚloggerÚwarningr!   r5   ÚsocketÚgetaddrinfoÚ	AF_UNSPECÚSOCK_STREAMÚgaierrorÚ	ipaddressÚ
ip_addressÚ
ValueErrorÚ_ALWAYS_BLOCKED_IPSÚanyÚ_ALWAYS_BLOCKED_NETWORKSr/   Údebugr   )r6   Úparsedr0   r1   Úallow_all_privateÚallow_private_ipÚ	addr_infoÚfamilyÚ_ÚsockaddrÚip_strÚexcr&   s               @r    Úis_safe_urlrV   •   sµ  ø€ ðAÝ˜#‘”ˆØ”OÐ) r×0Ò0Ñ2Ô2×8Ò8Ñ:Ô:×AÒAÀ#ÑFÔFˆØ”-Ð% 2×,Ò,Ñ.Ô.×4Ò4Ñ6Ô6ˆØð 	Ø�5ð Õ)Ð)Ð)Ý�NŠNÐEÀxÑPÔPÐPØ�5õ 7Ñ8Ô8Ðå8¸À6ÑJÔJÐð	ÝÔ*¨8°T½6Ô;KÍVÔM_Ñ`Ô`ˆIˆIøÝŒð 	ð 	ð 	õ �NŠNÐNÐPXÑYÔYÐYØ�5�5ð		øøøð *3ð 	ð 	Ñ%ˆF�A�q˜!˜XØ˜a”[ˆFðÝÔ)¨&Ñ1Ô1��øÝð ð ð Ø�ðøøøð Õ(Ð(Ð(­CÐ/^Ð/^Ð/^Ð/^ÕE]Ð/^Ñ/^Ô/^Ñ,^Ô,^Ð(Ý—’ØIØ˜fñô ð ð �u�uà$ð Ð-=ð Å.ÐQSÑBTÔBTð Ý—’ØKØ˜fñô ð ð �u�uøàð 		Ý�LŠLØ]Øñô ð ð ð ð 	Ý�LŠLØSØñô ð ð
 ˆtøåð ð ð õ 	�ŠÐNÐPSÐUXÑYÔYÐYØˆuˆuˆuˆuˆuøøøøð	øøøsy   ƒA>H Â$H Â)H Ã+C4 Ã3H Ã4*D"ÄH Ä!D"Ä"H Ä8EÅH Å
EÅH ÅEÅA	H Æ%0H Ç<H È
IÈIÉI)r   N)Ú__doc__rF   Úloggingr   rA   Úurllib.parser   Ú	getLoggerÚ__name__r?   Ú	frozensetr>   rG   rI   Ú
ip_networkrK   r4   r.   r   r   ÚboolÚ__annotations__r!   r%   ÚIPv4AddressÚIPv6Addressr/   Ústrr5   rV   r#   r$   r    ú<module>rc      s  ððð ð ð2 Ð Ð Ð Ø €€€Ø 	€	€	€	Ø €€€Ø !Ð !Ð !Ð !Ð !Ð !à	ˆÔ	˜8Ñ	$Ô	$€ð
 �YØØð ñ ô Ð ð  �iØ€IÔÐ*Ñ+Ô+Ø€IÔ˜Ñ)Ô)Ø€IÔÐ*Ñ+Ô+Ø€IÔ˜Ñ)Ô)Ø€IÔÐ*Ñ+Ô+ð!ñ ô Ð ð €IÔÐ)Ñ*Ô*ðÐ ð &˜IØð'ñ ô Ð ð &�Ô% oÑ6Ô6€ð  Ð Ø#Ð �tÐ #Ð #Ñ #ð,! Dð ,!ð ,!ð ,!ð ,!ð^"ð "ð "ð "ð	�yÔ,¨yÔ/DÑDð 	Èð 	ð 	ð 	ð 	ðG¨Cð G¸ð GÀð Gð Gð Gð Gð
L�Sð L˜Tð Lð Lð Lð Lð Lð Lr$   