+
    lHàj¬   ã                  ó˜  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIH	t	 ^ RI
HtHtHtHtHt ^ RIHt ^ RIHt ^ RIHt ]P.                  ! ]4      tRR	R
. R. /tRt]P8                  ! 4       tRsR]R&   Rs R]R&   Rs!R]R&    ! R R]"4      t#R R lt$R R lt%R R lt&R R lt'R R lt(R(R  R! llt)R" R# lt*R$ R% lt+R(R& R' llt,R# ))aZ  Website access policy helpers for URL-capable tools.

Loads a user-managed website blocklist (``security.website_blocklist`` in ~/.hermes/config.yaml plus
optional shared list files) without the heavier CLI config stack. The parsed policy is cached with a
short TTL so config edits take effect quickly without re-parsing YAML on every URL check.
)ÚannotationsN)ÚPath)ÚAnyÚDictÚListÚOptionalÚTuple)Úurlparse)Úget_hermes_home)Ú_normalize_hostnameÚenabledFÚdomainsÚshared_filesg      >@zOptional[dict[str, Any]]Ú_cached_policyúOptional[str]Ú_cached_policy_pathg        ÚfloatÚ_cached_policy_timec                  ó   € ] tR t^!tRtRtR# )ÚWebsitePolicyErrorz/Raised when a website policy file is malformed.© N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__static_attributes__r   ó    Ú9/home/ubuntu/.hermes/hermes-agent/tools/website_policy.pyr   r   !   s   † Ý9r   r   c               ó    € V ^8„  d   QhRRRR/# )é   Úruler   Úreturnr   r   )Úformats   "r   Ú__annotate__r$   %   s   € ÷ Sñ S˜#ð S -ñ Sr   c                ó®  € \        V \        4      '       d>   V P                  4       P                  4       ;p'       d   VP	                  R4      '       d   R# RV9   d,   \        V4      pVP                  ;'       g    VP                  pVP                  R^4      ^ ,          P                  4       P                  R4      P                  R4      ;'       g    R# )z_Reduce a rule (bare host, URL, or ``host/path``) to a lowercase host; None for blanks/comments.Ú#Nú://Ú/Ú.zwww.)Ú
isinstanceÚstrÚstripÚlowerÚ
startswithr	   ÚnetlocÚpathÚsplitÚrstripÚremoveprefix)r!   ÚvalueÚparseds   &  r   Ú_normalize_ruler6   %   sž   € ä�dœC× Ò °$·*±*³,×2DÑ2DÓ2FÐ)F¨Ö)FÈ5×K[ÑK[Ð\_×K`ÒK`ÙØ�„~Ü˜%“ˆØ—‘×,Ð, §¡ˆØ�;‰;�s˜AÓ˜qÕ!×'Ñ'Ó)×0Ñ0°Ó5×BÑBÀ6ÓJ×RÐRÈdÐRr   c               ó    € V ^8„  d   QhRRRR/# )r    r0   r   r"   z	list[str]r   )r#   s   "r   r$   r$   /   s   € ÷ Mñ M Tð M¨iñ Mr   c                óT  €  V P                  RR7      p\        \        TP                  4       4       Uu. uF  q3'       g   K  TNK  	  up#   \         d    \        P                  RT 4       . u # \        \
        3 d#   p\        P                  RY4       . u Rp?# Rp?ii ; iu upi )u›   Rules from a shared blocklist file; missing/unreadable files warn and yield nothing rather than
raising â€” a bad file path must not disable all web tools.ú	utf-8-sig©Úencodingz.Shared blocklist file not found (skipping): %sz6Failed to read shared blocklist file %s (skipping): %sN)	Ú	read_textÚFileNotFoundErrorÚloggerÚwarningÚOSErrorÚUnicodeDecodeErrorÚmapr6   Ú
splitlines)r0   ÚrawÚexcr!   s   &   r   Ú_iter_blocklist_file_rulesrF   /   s�   € ðØ�n‰n kˆnÓ2ˆô !¤°#·.±.Ó2BÔCÓLÑC�TÃt�DˆDÑCÑLÐLøô ô Ü�‰ÐGÈÔNØŠ	ÜÔ'Ð(ô Ü�‰ÐOÐQUÔ[Ø�	ûðüò Ms4   ‚A ´	B%ÁB%Á#B"Á0B"Á?B"Â BÂB"ÂB"c               ó$   € V ^8„  d   QhRRRRRR/# )r    r4   r   Úlabelr+   r"   údict[str, Any]r   )r#   s   "r   r$   r$   =   s!   € ÷ ñ ˜Cð ¨ð °ñ r   c                ój   € V e%   \        V \        4      '       g   \        V R24      hT ;'       g    / # )zU``None`` (empty YAML section) counts as an empty mapping; other non-dicts are errors.z must be a mapping)r*   Údictr   )r4   rH   s   &&r   Ú_require_mappingrL   =   s3   € àÒ¤¨E´4×!8Ò!8Ü  E 7Ð*<Ð!=Ó>Ð>Ø�;ˆ;�BÐr   c               ó    € V ^8„  d   QhRRRR/# )r    Úconfig_pathr   r"   rI   r   )r#   s   "r   r$   r$   D   s   € ÷ ?ñ ? Tð ?¨nñ ?r   c                ót  € V P                  4       '       g   \        \        4      #  ^ RIp TP                  T P                  RR7      4      ;'       g    / p\        T\        4      '       g   \        R4      h\        TP                  R	/ 4      R	4      p\        TP                  R
/ 4      R4      p/ \        CTC#   \         d(    \
        P                  R4       \        \        4      u # i ; i  TP                   d   p\        RT  RT 24      ThRp?i\         d   p\        RT  RT 24      ThRp?ii ; i)é    Nu8   ruamel.yaml not installed â€” website blocklist disabledr9   r:   zInvalid config YAML at z: zFailed to read config file zconfig root must be a mappingÚsecurityÚwebsite_blocklistzsecurity.website_blocklist)ÚexistsrK   Ú_DEFAULT_WEBSITE_BLOCKLISTÚhermes_yamlÚImportErrorr>   ÚdebugÚ	safe_loadr<   Ú	YAMLErrorr   r@   r*   rL   Úget)rN   ÚyamlÚconfigrE   rQ   rR   s   &     r   Ú_load_policy_configr]   D   s1  € Ø×Ñ×ÒÜÔ.Ó/Ð/ð0Û"ð^Ø—‘ × 5Ñ 5¸{Ð 5Ó KÓL×RÐRÐPRˆô
 �fœd×#Ò#Ü Ð!@ÓAÐAÜ §
¡
¨:°rÓ :¸JÓG€HÜ(¨¯©Ð6IÈ2Ó)NÐPlÓmÐØ>Ô(Ð>Ð,=Ð>Ð>øô ô 0Ü�‰ÐOÔPÜÔ.Ó/Ò/ð0ûð
 �>‰>ô ZÜ Ð#:¸;¸-ÀrÈ#ÈÐ!OÓPÐVYÐYûÜô ^Ü Ð#>¸{¸mÈ2ÈcÈUÐ!SÓTÐZ]Ð]ûð^ús@   §B: ¬'C/ ÁC/ Â:/C,Ã+C,Ã/D7Ä DÄD7ÄD7Ä D2Ä2D7c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R/# )
r    ÚpolicyrI   Úkeyr+   ÚkindÚtypeÚdefaultr   r"   r   )r#   s   "r   r$   r$   Y   s0   € ÷ ñ ˜.ð ¨sð ¸$ð Èð ÐQTñ r   c                ó´   € V P                  W4      pV\        J d   T;'       g    . p\        WB4      '       g    V\        J d   RMRp\	        RV RV 24      hV# )zTTyped policy field; ``None``/empty list values are coerced to ``[]`` for lists only.ÚbooleanÚlistzsecurity.website_blocklist.z must be a )rZ   rf   r*   Úboolr   )r_   r`   ra   rc   r4   Ú	kind_names   &&&&  r   Ú_require_typeri   Y   sX   € à�J‰J�sÓ$€EØŒtƒ|Ø—�˜ˆÜ�e×"Ò"Ø!%¬£‘I°6ˆ	Ü Ð#>¸s¸eÀ;ÈyÈkÐ!ZÓ[Ð[Ø€Lr   c               ó    € V ^8„  d   QhRRRR/# )r    rN   úOptional[Path]r"   rI   r   )r#   s   "r   r$   r$   d   s   € ÷ ñ ¨ð À.ñ r   c                ó¬  € \        4       R,          p\        T ;'       g    T4      p\        P                  ! 4       pV f]   \        ;_uu_ 4        \
        V8H  ;'       d    V\        ,
          \        8  p\        e   V'       d   \        uuRRR4       # RRR4       T ;'       g    Tp \        V 4      p\        \        \        VR\        . 4      4      pV Uu. uF  qw'       g   K  VR3NK  	  pp\        VR\        . 4      p	\        VR\        R4      p
V	 F­  p\        V\        4      '       d   VP!                  4       '       g   K1  \#        V4      P%                  4       pVP'                  4       '       d   TM\        4       V,          P)                  4       pT\+        V4       Uu. uF  qÝ\        V4      3NK  	  up,          pK¯  	  RT
R\,        P/                  V4       UUu. uF  w  r~R	VR
V/NK  	  upp/pW8X  d    \        ;_uu_ 4        WòVusssRRR4       V# V#   + '       g   i     EL“; iu upi u upi u uppi   + '       g   i     T# ; i)uÇ   Parsed website blocklist policy (``{"enabled", "rules"}``); cached for ``_CACHE_TTL_SECONDS`` for
the default config path only â€” an explicit ``config_path`` (tests) bypasses and never populates it.zconfig.yamlNr   r\   r   r   TÚrulesÚpatternÚsource)r
   r+   ÚtimeÚ	monotonicÚ_cache_lockr   r   Ú_CACHE_TTL_SECONDSr   r]   rB   r6   ri   rf   rg   r*   r,   r   Ú
expanduserÚis_absoluteÚresolverF   rK   Úfromkeys)rN   Údefault_pathÚresolved_pathÚnowÚfreshr_   r   ÚpÚpairsr   r   Úshared_filer0   Ú
normalizedÚsÚresults   &               r   Úload_website_blocklistr‚   d   sè  € ô #Ó$ }Õ4€LÜ˜×3Ð3 |Ó4€MÜ
�.Š.Ó
€CØÒßŽ[Ü'¨=Ñ8×mÐm¸cÔDWÕ>WÔ[mÑ=mˆEÜÒ)¯eÜ%÷ �[Š[ð ×-Ð- €KÜ  Ó-€FÜ”/¤=°¸ÄDÈ"Ó#MÓN€GÙ;BÓ#H¹7°aÃa¤M Q¨£M¹7€EÐ#HÜ  ¨¼¸rÓB€LÜ˜F I¬t°TÓ:€GÛ#ˆÜ˜+¤s×+Ò+°;×3DÑ3D×3FÒ3FÙÜ�KÓ ×+Ñ+Ó-ˆØ×'Ñ'×)Ò)‰t´Ó0AÀDÕ0H×/QÑ/QÓ/SˆØÔ<VÐW[Ô<\Ó]Ñ<\¨jœs 4›yÓ)Ñ<\Ñ]Õ]Šñ $ð ˜ 'ÔTX×TaÑTaÐbgÔThÔ+iÑThÉDÈA¨Y¸¸8ÀQÓ,GÑThÒ+iÐj€FØÔ"ßŽ[ØGMÐ^aÐDˆNÐ/Ð1D÷ à€Mˆ6€M÷+ �[ˆ[üò $Iùò ^ùã+içŽ[à€Mús6   Á5HÂHÃ	H2Ã%H2Æ$H7Ç!H<ÈIÈH/	ÉI	c               ó$   € V ^8„  d   QhRRRRRR/# )r    Úhostr+   rn   r"   rg   r   )r#   s   "r   r$   r$   „   s!   € ÷ ;ñ ; 3ð ;°ð ;¸ñ ;r   c                óÀ   € V '       d	   V'       g   R# VP                  R4      '       d   \        P                  ! W4      # W8H  ;'       g    V P                  RV 24      # )zS``*.example.com`` rules glob-match; bare hosts match exactly or as a parent domain.Fz*.r)   )r.   ÚfnmatchÚendswith)r„   rn   s   &&r   Ú_match_host_against_rulerˆ   „   sI   € ç—wÙØ×Ñ˜$×ÒÜ�Š˜tÓ-Ð-Ø‰?×:Ð:˜dŸm™m¨a°¨y¨MÓ:Ð:r   c               ó    € V ^8„  d   QhRRRR/# )r    Úurlr+   r"   r   )r#   s   "r   r$   r$   �   s   € ÷ ñ  3ð ¨3ñ r   c                óú   € \        V 4      p\        VP                  ;'       g    VP                  4      pV'       g?   RV 9  d8   \        RV  24      p\        VP                  ;'       g    VP                  4      pV# )zPHost of ``url``; schemeless inputs (``example.com/x``) are retried as ``//url``.r'   z//)r	   Ú_normalize_hostÚhostnamer/   )rŠ   r5   r„   s   &  r   Ú_extract_host_from_urlishrŽ   �   s^   € ä�c‹]€FÜ˜6Ÿ?™?×;Ð;¨f¯m©mÓ<€Dß�E Ô$Ü˜B˜s˜e˜*Ó%ˆÜ˜vŸ™×?Ð?°&·-±-Ó@ˆØ€Kr   c               ó$   € V ^8„  d   QhRRRRRR/# )r    rŠ   r+   rN   rk   r"   zOptional[dict[str, str]]r   )r#   s   "r   r$   r$   —   s"   € ÷ "ñ "˜cð "°ð "ÐJbñ "r   c                óä  € VfF   \         ;_uu_ 4        \        e'   \        P                  R4      '       g    RRR4       R# RRR4       \        V 4      pV'       g   R#  \	        V4      pTP                  R4      '       g   R# TP                  R. 4       Fe  pTP                  RR4      TP                  RR	4      rv\        Y&4      '       g   K9  \        P                  R
YT4       RT RTRTRTRRT RT RT 2/u # 	  R#   + '       g   i     LÇ; i  \
         d'   pTe   h \        P                  RT4        Rp?R# Rp?i\         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)u  ``None`` if the URL is allowed by the blocklist policy, else block metadata (host/rule/source/message).

Fails open on policy errors (warn + ``None``) so a config typo can't break all web tools â€” except with
an explicit ``config_path`` (tests), where errors propagate.
Nr   z.Website policy config error (failing open): %sz:Unexpected error loading website policy (failing open): %srm   rn   Ú ro   r\   u,   Blocked URL %s â€” matched rule '%s' from %srŠ   r„   r!   ÚmessagezBlocked by website policy: 'z' matched rule 'z' from )rr   r   rZ   rŽ   r‚   r   r>   r?   Ú	Exceptionrˆ   Úinfo)rŠ   rN   r„   r_   rE   r!   rn   ro   s   &&      r   Úcheck_website_accessr•   —   sC  € ð ÒßŽ[ÜÒ)´.×2DÑ2DÀY×2OÒ2OØ÷ �[‰[ô % SÓ)€DßÙð	Ü'¨Ó4ˆð �:‰:�i× Ò ÙØ—
‘
˜7 BÖ'ˆØŸ(™( 9¨bÓ1°4·8±8¸HÀhÓ3O�Ü# D×2Ô2Ü�K‰KÐFÈÐV\Ô]à�s˜F D¨&°'¸8ÀVØÐ9¸$¸Ð?OÐPWÈyÐX_Ð`fÐ_gÐhðò ñ	 (ñ ÷5 �[ûô ô ØÒ"ØÜ�‰ÐGÈÔMÝûÜô Ü�‰ÐSÐUXÔYÝûðús:   “#D·DÁ D ÄD	ÄE/ÄE Å E/ÅE/ÅE*Å*E/)N)-Ú__conditional_annotations__r   Ú
__future__r   r†   ÚloggingÚ	threadingrp   Úpathlibr   Útypingr   r   r   r   r   Úurllib.parser	   Úhermes_constantsr
   Útools.url_safetyr   rŒ   Ú	getLoggerr   r>   rT   rs   ÚLockrr   r   Ú__annotations__r   r   r“   r   r6   rF   rL   r]   ri   r‚   rˆ   rŽ   r•   )r–   s   @r   Ú<module>r¢      sÇ   øðôõ #ã Û Û Û Ý ß 3Õ 3Ý !å ,Ý Cà	×	Ò	˜8Ó	$€à'¨°	¸2¸~ÈrÐRÐ ð Ð Ø�nŠnÓ€Ø+/€Ð(Ó /Ø%)Ð �]Ó )Ø Ð �UÓ  ô:˜ô :õSõMõõ?õ*÷õ@;õ÷"ñ "r   