+
    jHàjA+  ã                  óä  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
 ^ RIHtHt ^ RIHt ]P                   ! ]4      t]! RR7       ! R	 R
4      4       t]! RRRRR]! R04      33R6RRR7      3tR]R&   ]! RR7       ! R R4      4       tR R lt]3R R lltR R ltR R ltR  R! ltR" R# ltR$ R% ltR& R' ltR(t ^t!R) R* lt"R+ R, lt#R- R. lt$R/]!/R0 R1 llt%R2 R3 lt&R4 R5 lt'R# )7zÎSecurity advisory checker for Hermes Agent.

Cheap (one ``importlib.metadata.version()`` call per advisory package, safe on every CLI startup)
and silent unless a compromised package is actually installed.
)ÚannotationsN)Ú	dataclass)ÚPath)ÚIterableÚOptional)Údisplay_hermes_homeT)Úfrozenc                  ót   € ] tR t^t$ RtR]R&   R]R&   R]R&   R]R&   R]R&   R	]R
&   RtR]R&   RtR]R&   RtR# )ÚAdvisoryaY  ``id`` is lowercase-hyphen, stable and never reused (it is what acks key on). ``remediation``
is ordered: uninstall command first, then credential audit/rotation guidance. Steps may use
``{hermes_home}``; ``full_remediation_text`` fills it with ``display_hermes_home()`` at render
time so a profile / HERMES_HOME user is sent to their own .env.
ÚstrÚidÚtitleÚsummaryÚurlz&tuple[tuple[str, frozenset[str]], ...]Úcompromisedztuple[str, ...]ÚremediationÚ Ú	publishedÚhighÚseverity© N)	Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__r   r   Ú__static_attributes__r   ó    ÚC/home/ubuntu/.hermes/hermes-agent/hermes_cli/security_advisories.pyr
   r
      s=   ‡ ñð 	ƒGØƒJØƒLØ	ƒHØ7Ó7Ø Ó Ø€IˆsÓØ€Hˆc×r   r
   zshai-hulud-2026-05u<   Mini Shai-Hulud worm â€” mistralai 2.4.6 compromised on PyPIu`  PyPI quarantined the mistralai package on 2026-05-12 after a malicious 2.4.6 release. The worm steals credentials from environment variables and credential files (~/.npmrc, ~/.pypirc, ~/.aws/credentials, GitHub PATs, cloud SDK tokens) and exfils them to a hardcoded webhook. If you ran any Python process that imported mistralai 2.4.6 â€” including hermes when configured with provider=mistral for TTS or STT â€” assume those credentials are exposed. PyPI has since removed 2.4.6 and the project ships clean releases again (2.4.7, 2.4.8); this advisory only fires if the compromised 2.4.6 is still installed.z1https://socket.dev/blog/mini-shai-hulud-worm-pypiÚ	mistralaiz2.4.6z
2026-05-12Úcritical)r   r   r   r   r   r   r   r   ztuple[Advisory, ...]Ú
ADVISORIESc                  ó:   € ] tR t^Qt$ RtR]R&   R]R&   R]R&   RtR# )	ÚAdvisoryHitz.One package-version match against an advisory.r
   Úadvisoryr   ÚpackageÚinstalled_versionr   N)r   r   r   r   r   r   r   r   r   r   r$   r$   Q   s   ‡ á8àÓØƒLØ×r   r$   c               ó    € V ^8„  d   QhRRRR/# )é   Úpkg_namer   ÚreturnúOptional[str]r   )Úformats   "r   Ú__annotate__r.   Z   s   € ÷ ñ  ð ¨ñ r   c                óŠ   € ^ RI HpHp  V! V 4      #   T d     R# \         d    \        P                  RT RR7        R# i ; i)u¬   Installed version of ``pkg_name`` via ``importlib.metadata`` (uv venvs may lack pip), or
None if not installed or metadata is corrupt â€” never crash the CLI startup path.
)ÚPackageNotFoundErrorÚversionNz%importlib.metadata.version(%s) raisedT©Úexc_info)Úimportlib.metadatar0   r1   Ú	ExceptionÚloggerÚdebug)r*   r0   r1   s   &  r   Ú_installed_versionr8   Z   sI   € ÷ AðÙ�xÓ Ð øØô ÚÜô Ü�‰Ð<¸hÐQUˆÔVÚðús   Š ’AœA¥AÁAc               ó    € V ^8„  d   QhRRRR/# )r)   Ú
advisorieszIterable[Advisory]r+   úlist[AdvisoryHit]r   )r-   s   "r   r.   r.   h   s   € ÷ ñ Ð#5ð ÐGXñ r   c                ó¼   € V  UUUu. uFE  pVP                    F2  w  r#\        V4      ;pf   K  V'       d	   WC9   g   K&  \        WV4      NK4  	  KG  	  uppp# u upppi )zUAll hits: package installed AND version in the compromised set (or the set is empty).)r   r8   r$   )r:   r%   r*   Úbad_versionsÚ	installeds   &    r   Údetect_compromisedr?   h   sg   € ñ #õá"ˆHØ&.×&:Ô&:Ñ"ˆHÜ+¨HÓ5Ð5ˆIô 	3÷ MYÐ\eÑ\uô 	3Œ�H¨	Ö2á&:ñ 	3Ù"óð ùô s   ‡"A®A¶A¾Ac               ó   € V ^8„  d   QhRR/# )r)   r+   zset[str]r   )r-   s   "r   r.   r.   v   s   € ÷ 
_ñ 
_�xñ 
_r   c                 óÄ  €  ^ RI Hp  V ! 4       P                  R4      ;'       g    / P                  R4      ;'       g    . p\        T\        4      '       dK   T Uu0 uF=  p\        T4      P                  4       '       g   K$  \        T4      P                  4       kK?  	  up# \        4       #   \         d%    \        P                  RRR7       \        4       u # i ; iu upi )u•   Advisory IDs the user has dismissed; empty when config can't be loaded (never block startup â€”
the advisory keeps firing until config is repaired).
)Úload_configÚsecurityÚacked_advisoriesz'Could not load config for advisory acksTr2   )Úhermes_cli.configrB   Úgetr5   r6   r7   ÚsetÚ
isinstanceÚlistr   Ústrip)rB   ÚrawÚxs      r   Úget_acked_idsrM   v   sª   € ðÝ1Ù‹}× Ñ  Ó,×2Ð2°×7Ñ7Ð8JÓK×QÐQÈrˆô ?IÈÌd×>SÒ>S¡CÓ:¡C˜q¬3¨q«6¯<©<¯>ŒNŒC�‹F�L‰LŽN¡CÑ:Ð^ÔY\ÓY^Ð^øô ô Ü�‰Ð>ÈˆÔNÜ‹uŠðüò ;s'   ‚"B+ ¥B+ ¾B+ Á!CÂCÂ+,CÃCc               ó    € V ^8„  d   QhRRRR/# )r)   Úadvisory_idr   r+   Úboolr   )r-   s   "r   r.   r.   ƒ   s   € ÷ ñ ˜cð  dñ r   c                ó¸  € V P                  4       p V '       g   R#  ^ RIHpHp  T! 4       pTP                  R/ 4      pTP                  R4      ;'       g    . p\        T\        4      '       g   . pY9  d   YP.,           TR&   T! T4       R#   \         d    \
        P                  R4        R# i ; i  \         d    \
        P                  RT 4        R# i ; i)z<Persist an ack for ``advisory_id``. Returns True on success.F)rB   Úsave_configz-Could not import config module to persist ackrC   rD   Tz%Failed to persist advisory ack for %s)rJ   rE   rB   rR   r5   r6   ÚwarningÚ
setdefaultrF   rH   rI   Ú	exception)rO   rB   rR   ÚcfgÚsecÚexistings   &     r   Úack_advisoryrY   ƒ   sÉ   € à×#Ñ#Ó%€KßÙðß>ðÙ‹mˆØ�n‰n˜Z¨Ó,ˆØ—7‘7Ð-Ó.×4Ð4°"ˆÜ˜(¤D×)Ò)ØˆHØÔ&Ø&.°Õ&>ˆCÐ"Ñ#Ù˜ÔÙøô ô Ü�‰ÐFÔGÚðûô ô Ü×ÑÐ@À+ÔNÚðús(   œB ¥0B4 Á5B4 Â B1Â0B1Â4!CÃCc               ó    € V ^8„  d   QhRRRR/# )r)   Úhitsr;   r+   r   )r-   s   "r   r.   r.   œ   s   € ÷ ;ñ ;Ð*ð ;Ð/@ñ ;r   c                ó    € V '       d   \        4       M	\        4       pV  Uu. uF!  q"P                  P                  V9  g   K  VNK#  	  up# u upi )z6Only hits whose advisories the user has not dismissed.)rM   rG   r%   r   )r[   ÚackedÚhs   &  r   Úfilter_unackedr_   œ   s6   € ç#ŒMŒO¬«€EÙÓ:‘t�!Ÿz™zŸ}™}°EÑ9�AˆA‘tÑ:Ð:ùÒ:s   ¡AÁAc               ó   € V ^8„  d   QhRR/# )r)   r+   rP   r   )r-   s   "r   r.   r.   ¢   s   € ÷ Bñ B˜dñ Br   c                 ó–   € \         P                  P                  R 4      '       * ;'       d    \        P                  P                  4       # )ÚNO_COLOR)ÚosÚenvironrF   ÚsysÚstdoutÚisattyr   r   r   Ú_term_supports_colorrh   ¢   s-   € Ü�z‰z�~‰~˜jÓ)Ô)×AÐA¬c¯j©j×.?Ñ.?Ó.AÐAr   c               ó    € V ^8„  d   QhRRRR/# )r)   r[   r;   r+   ú	list[str]r   )r-   s   "r   r.   r.   ¦   s   € ÷ ñ Ð.ð °9ñ r   c           	     ó\  € V '       g   . # V ^ ,          pRVP                   P                   RVP                   P                   2RVP                   RVP                   2R.p\        V 4      ^8”  d<   TP                  ^R\        V 4      ^,
           R\        V 4      ^8”  d   RMR	 R
24       V# )zU1-3 short unstyled lines for a startup banner; always names the worst hit explicitly.zSECURITY ADVISORY [z]: z  Detected: ú==z,  Run 'hermes doctor' for remediation steps.z  (z additional advisorÚiesÚyz also active.))r%   r   r   r&   r'   ÚlenÚinsert)r[   ÚprimaryÚliness   &  r   Úshort_banner_linesrs   ¦   s«   € çØˆ	Ø�1�g€Gà
˜g×.Ñ.×1Ñ1Ð2°#°g×6FÑ6F×6LÑ6LÐ5MÐNØ
�w—‘Ð' r¨'×*CÑ*CÐ)DÐEØ6ð€Eô
 ˆ4ƒy�1„}Ø�‰�Q˜#œc $›i¨!�m˜_Ð,?ÌÈTËÐUVÌÁÐ\_Ð?`Ð`nÐoÔpØ€Lr   c               ó    € V ^8„  d   QhRRRR/# )r)   Úhitr$   r+   rj   r   )r-   s   "r   r.   r.   µ   s   € ÷ ñ ˜{ð ¨yñ r   c                ó4  € V P                   pRVP                   R2RVP                   RVP                   RVP                   2RV P
                   RV P                   2RVP                   2R	VP                  R	R
.R \        VP                  ^4       4       O# )z7Multi-line block describing the advisory + remediation.z=== z ===zID:        z    Severity: z    Published: zDetected:  rl   zReference: r   zRemediation:c              3  óf   "  € T F'  w  rR V RVP                  \        4       R7       2x € K)  	  R# 5i)z  z. )Úhermes_homeN)r-   r   )Ú.0ÚiÚsteps   &  r   Ú	<genexpr>Ú(full_remediation_text.<locals>.<genexpr>Á   s1   é € Ð	pÑToÉÈˆBˆqˆc��D—K‘KÔ,?Ó,A�KÓBÐCÕ
DÓToùs   ‚/1)r%   r   r   r   r   r&   r'   r   r   Ú	enumerater   )ru   Úas   & r   Úfull_remediation_textr€   µ   s    € à�‰€Aà
ˆq�w‰wˆi�tÐØ
�a—d‘d�V˜>¨!¯*©*¨°_ÀQÇ[Á[ÀMÐRØ
�c—k‘k�] " S×%:Ñ%:Ð$;Ð<Ø
�a—e‘e�WÐØ
Ø	�	‰	Ø
Øð
ñ 
qÔT]Ð^_×^kÑ^kÐmnÔToÓ	pð
ð 
r   Úadvisory_banner_seenc               ó   € V ^8„  d   QhRR/# )r)   r+   zOptional[Path]r   )r-   s   "r   r.   r.   Î   s   € ÷ ñ ˜Nñ r   c                 ó¢   €  ^ RI Hp  \        V ! 4       4      R,          pVP                  RRR7       V\        ,          #   \
         d     R# i ; i)é    )Úget_hermes_homeÚcacheT)ÚparentsÚexist_okN)Úhermes_constantsr…   r   ÚmkdirÚ_BANNER_CACHE_FILEr5   )r…   Ú	cache_dirs     r   Ú_banner_cache_pathr�   Î   sJ   € ðÝ4Ü™Ó*Ó+¨gÕ5ˆ	Ø�‰ ¨tˆÔ4ØÔ-Õ-Ð-øÜô Úðús   ‚<? ¿AÁAc               ó   € V ^8„  d   QhRR/# )r)   r+   údict[str, float]r   )r-   s   "r   r.   r.   Ø   s   € ÷ ñ Ð,ñ r   c                 ó^  € \        4       p  V e7   V P                  4       '       d!   V P                  RR7      P                  4       M. p/ pR T 4        F1  p \        T4      ^8X  d   \        T^,          4      Y#^ ,          &   K1  K3  	  T#   \         d    / u # i ; i  \         d     KX  i ; i)Nz	utf-8-sig©Úencodingc              3  óD   "  € T F  qP                  R ^4      x € K  	  R # 5i©N)Úsplit)ry   Úlines   & r   r|   Ú%_read_banner_cache.<locals>.<genexpr>ß   s   é € Ð8±%¨$—*‘*˜T 1×%Ð%³%ùs   ‚ )r�   ÚexistsÚ	read_textÚ
splitlinesr5   ro   ÚfloatÚ
ValueError)Úprr   ÚoutÚpartss       r   Ú_read_banner_cacher    Ø   s¬   € ÜÓ€AðØBCÂ-ÐTU×T\ÑT\×T^ÒT^�—‘ [�Ó1×<Ñ<Ô>Ðdfˆð €CÙ8±%Ö8ˆð	Ü�5‹z˜QŒÜ % e¨A¥h£�˜!•H“ñ ñ 9ð €Jøô ô ØŠ	ðûô ô 	Úð	ús(   ŒB
 ¦"B
 Á+BÂ
BÂBÂB,Â+B,c               ó    € V ^8„  d   QhRRRR/# )r)   Úseenr�   r+   ÚNoner   )r-   s   "r   r.   r.   è   s   € ÷ Mñ MÐ.ð M°4ñ Mr   c                óö   €  \        4       ;peA   VP                  RP                  R V P                  4        4       4      R,           RR7       R # R #   \         d    \
        P                  RRR7        R # i ; i)NÚ
c              3  ó4   "  € T F  w  rV R V 2x € K  	  R# 5i)Ú Nr   )ry   ÚaidÚtss   &  r   r|   Ú&_write_banner_cache.<locals>.<genexpr>ë   s   é € Ð"MÁ±W°S c U¨!¨B¨4¥=Ãùs   ‚zutf-8r‘   z%Could not write advisory banner cacheTr2   )r�   Ú
write_textÚjoinÚitemsr5   r6   r7   )r¢   r�   s   & r   Ú_write_banner_cacher®   è   si   € ðMÜ#Ó%Ð%ˆAÒ2Ø�L‰L˜Ÿ™Ñ"MÀÇ
Á
ÄÓ"MÓMÐPTÕTÐ_fˆLÖgñ 3øäô MÜ�‰Ð<Àtˆ×LðMús   ‚AA Á"A8Á7A8Úrepeat_hoursc               ó$   € V ^8„  d   QhRRRRRR/# )r)   r[   r;   r¯   Úintr+   r   )r-   s   "r   r.   r.   ð   s#   € ÷ ñ Ð/ð À#ð Ðarñ r   c               óv  € ^ RI p\        V 4      pV'       g   . # VP                  4       p\        4       pWAR,          ,
          pV Uu. uF1  quP                  VP                  P
                  R4      V8  g   K/  VNK3  	  ppV F  pWEVP                  P
                  &   K  	  V'       d   \        V4       V# u upi )zšHits whose banner is due (not acked, not recently shown). Side effect: stamps the banner
cache for every returned hit, so callers must render the result.
Ni  g        )Útimer_   r    rF   r%   r   r®   )	r[   r¯   r³   ÚfreshÚnowr†   Úcutoffru   Údues	   &$       r   Úhits_due_for_bannerr¸   ð   s—   € ó ä˜4Ó €EßØˆ	Ø
�)‰)‹+€CÜÓ €EØ 4Õ'Õ(€FÙÓ
L™%�3§9¡9¨S¯\©\¯_©_¸cÓ#BÀVÑ#K�3ˆ3™%€CÐ
LÛˆØ!$ˆc�l‰l�o‰oÓñ ç
Ü˜EÔ"Ø€Jùò Ms   Á,B6Á8B6c               ó    € V ^8„  d   QhRRRR/# ©r)   r[   r;   r+   r,   r   )r-   s   "r   r.   r.     s   € ÷ Jñ JÐ*ð J¨}ñ Jr   c                ó�   € \        V 4      pV'       g   R# RP                  \        V4      4      p\        4       '       d   RV R2# T# )zOPrintable startup banner, or None if nothing is due (updates the banner cache).Nr¥   z[1;31mz[0m)r¸   r¬   rs   rh   )r[   r·   Útexts   &  r   Ústartup_bannerr½     sB   € ä
˜dÓ
#€CßÙØ�9‰9Ô'¨Ó,Ó-€DÜ)=×)?Ò)?ˆZ˜�v˜WÐ%ÐIÀTÐIr   c               ó    € V ^8„  d   QhRRRR/# rº   r   )r-   s   "r   r.   r.     s   € ÷ 
Dñ 
DÐ/ð 
D°Mñ 
Dr   c           
     ón  € \        V 4      pV'       g   R# \        V4      ^8X  dk   V^ ,          pRVP                  P                   RVP                   RVP
                   RVP                  P                   RVP                  P                   2
# \        V4       RRP                  R	 V 4       4       R
2# )z4One-line log message for gateway operators, or None.NzSecurity advisory [z
] active: rl   z	 matches z. See z" security advisories active (IDs: z, c              3  óL   "  € T F  qP                   P                  x € K  	  R # 5ir”   )r%   r   )ry   r^   s   & r   r|   Ú&gateway_log_message.<locals>.<genexpr>  s   é € ÐFdÑ^cÐYZÇzÁzÇ}Æ}Ó^cùs   ‚"$z7). Run `hermes doctor` on the gateway host for details.)	r_   ro   r%   r   r&   r'   r   r   r¬   )r[   r´   r^   s   &  r   Úgateway_log_messagerÂ     s±   € ä˜4Ó €EßÙÜ
ˆ5ƒz�Q„Ø�!�HˆØ% a§j¡j§m¡m _°J¸q¿y¹y¸kÈÈA×L_ÑL_ÐK`ð aØŸ:™:×+Ñ+Ð,¨F°1·:±:·>±>Ð2BðDð 	Eä�5‹zˆlÐ<¸T¿Y¹YÑFdÑ^cÓFdÓ=dÐ<eð fCð Cð Dr   )zARun: pip uninstall -y mistralai  (or: uv pip uninstall mistralai)zpRotate API keys in {hermes_home}/.env (OpenRouter, Anthropic, OpenAI, Nous, GitHub, AWS, Google, Mistral, etc.).z‰Audit ~/.npmrc, ~/.pypirc, ~/.aws/credentials, ~/.config/gh/hosts.yml, and any other credential files for tokens that may have been read.zgCheck GitHub for unexpected new SSH keys, deploy keys, or webhook additions on repos you have admin on.zOAfter cleanup: hermes doctor --ack shai-hulud-2026-05  to dismiss this warning.)(Ú__conditional_annotations__r   Ú
__future__r   Úloggingrc   re   Údataclassesr   Úpathlibr   Útypingr   r   r‰   r   Ú	getLoggerr   r6   r
   Ú	frozensetr"   r   r$   r8   r?   rM   rY   r_   rh   rs   r€   r‹   Ú_BANNER_REPEAT_HOURSr�   r    r®   r¸   r½   rÂ   )rÃ   s   @r   Ú<module>rÌ      s"  øðôõ #ã Û 	Û 
Ý !Ý ß %å 0à	×	Ò	˜8Ó	$€ñ �$Ô÷ð ó ðñ$ ØØLð	8ð @Ø!¡9¨g¨YÓ#7Ð8Ð:ð

ð Øô;ð $€
Ð ó  ñF �$Ô÷ð ó ðõð 9C÷ õ
_õõ2;õBõõð* ,Ð ØÐ õõõ MðÐH\÷ õ(J÷
Dr   