§
    ‰eîi	f  ã            	       ó�  — U d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	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dZde› d�ZdZded	ed
efd„Zded	ed
efd„Zd
efd„Zdaedz  ez  ed<   dZ da!eed<    ej"        ¦   «         Z#da$ej%        dz  ed<   dZ&d
efd„Z'd
efd„Z(d
edz  fd„Z)d
efd„Z*d6defd„Z+d„ Z,d
efd„Z-d
edz  fd„Z.d7deded efd!„Z/d"ed#ed$ed
edz  fd%„Z0d&ed"ed'ed
efd(„Z1d)d*œd+ed
e2edz  ef         fd,„Z3d-ed
efd.„Z4d-ed
efd/„Z5d)d*œd+efd0„Z6d)d*œd+efd1„Z7d2Z8d3Z9d4ed
efd5„Z:dS )8uù  Tirith pre-exec security scanning wrapper.

Runs the tirith binary as a subprocess to scan commands for content-level
threats (homograph URLs, pipe-to-interpreter, terminal injection, etc.).

Exit code is the verdict source of truth:
  0 = allow, 1 = block, 2 = warn

JSON stdout enriches findings/summary but never overrides the verdict.
Operational failures (spawn error, timeout, unknown exit code) respect
the fail_open config setting. Programming errors propagate.

Auto-install: if tirith is not found on PATH or at the configured path,
it is automatically downloaded from GitHub releases to $HERMES_HOME/bin/tirith.
The download always verifies SHA-256 checksums.  When cosign is available on
PATH, provenance verification (GitHub Actions workflow signature) is also
performed.  If cosign is not installed, the download proceeds with SHA-256
verification only â€” still secure via HTTPS + checksum, just without supply
chain provenance proof.  Installation runs in a background thread so startup
never blocks.
é    N)Úget_hermes_homezsheeki03/tirithz^https://github.com/z,/\.github/workflows/release\.yml@refs/tags/vz+https://token.actions.githubusercontent.comÚkeyÚdefaultÚreturnc                 ó^   — t          j        | ¦  «        }|€|S |                     ¦   «         dv S )N)Ú1ÚtrueÚyes)ÚosÚgetenvÚlower©r   r   Úvals      ú7/home/div/.hermes/hermes-agent/tools/tirith_security.pyÚ	_env_boolr   3   s-   € Ý
Œ)�C‰.Œ.€CØ
€{ØˆØ�9Š9‰;Œ;Ð.Ð.Ð.ó    c                 óv   — t          j        | ¦  «        }|€|S 	 t          |¦  «        S # t          $ r |cY S w xY w)N)r   r   ÚintÚ
ValueErrorr   s      r   Ú_env_intr   :   sM   € Ý
Œ)�C‰.Œ.€CØ
€{ØˆðÝ�3‰xŒxˆøÝð ð ð Øˆˆˆðøøøs   š) ©8·8c                  óÜ  — dddddœ} 	 ddl m}  |¦   «                              di ¦  «        pi }n# t          $ r i }Y nw xY wt	          d|                     d	| d	         ¦  «        ¦  «        t          j        d
|                     d| d         ¦  «        ¦  «        t          d|                     d| d         ¦  «        ¦  «        t	          d|                     d| d         ¦  «        ¦  «        dœS )z@Load security settings from config.yaml, with env var overrides.TÚtirithé   )Útirith_enabledÚtirith_pathÚtirith_timeoutÚtirith_fail_openr   )Úload_configÚsecurityÚTIRITH_ENABLEDr   Ú
TIRITH_BINr   ÚTIRITH_TIMEOUTr   ÚTIRITH_FAIL_OPENr   )Úhermes_cli.configr   ÚgetÚ	Exceptionr   r   r   r   )Údefaultsr   Úcfgs      r   Ú_load_security_configr)   D   s  € ð ØØØ ð	ð €HðØ1Ð1Ð1Ð1Ð1Ð1Øˆk‰mŒm×Ò 
¨BÑ/Ô/Ð5°2ˆˆøÝð ð ð Øˆˆˆðøøøõ $Ð$4°c·g²gÐ>NÐPXÐYiÔPjÑ6kÔ6kÑlÔlÝ”y ¨s¯wªw°}ÀhÈ}ÔF]Ñ/^Ô/^Ñ_Ô_Ý"Ð#3°S·W²WÐ=MÈxÐXhÔOiÑ5jÔ5jÑkÔkÝ%Ð&8¸#¿'º'ÐBTÐV^Ð_qÔVrÑ:sÔ:sÑtÔtð	ð ð s   ‰&0 °?¾?Ú_resolved_pathFÚ Ú_install_failure_reasonÚ_install_threadi€Q c                  ó8   — t          t          ¦   «         ¦  «        S )zAReturn the Hermes home directory, respecting HERMES_HOME env var.)Ústrr   © r   r   Ú_get_hermes_homer1   l   s   € å�Ñ Ô Ñ!Ô!Ð!r   c                  óZ   — t           j                             t          ¦   «         d¦  «        S )z3Return the path to the install-failure marker file.z.tirith-install-failed)r   ÚpathÚjoinr1   r0   r   r   Ú_failure_marker_pathr5   q   s    € åŒ7�<Š<Õ(Ñ*Ô*Ð,DÑEÔEÐEr   c                  ób  — 	 t          ¦   «         } t          j                             | ¦  «        }t	          j        ¦   «         |z
  t
          k    rdS t          | d¦  «        5 }|                     ¦   «                              ¦   «         cddd¦  «         S # 1 swxY w Y   dS # t          $ r Y dS w xY w)z˜Read the failure reason from the disk marker.

    Returns the reason string, or None if the marker doesn't exist or is
    older than _MARKER_TTL.
    NÚr)
r5   r   r3   ÚgetmtimeÚtimeÚ_MARKER_TTLÚopenÚreadÚstripÚOSError)ÚpÚmtimeÚfs      r   Ú_read_failure_reasonrB   v   sè   € ðÝ Ñ"Ô"ˆÝ”× Ò  Ñ#Ô#ˆÝŒI‰KŒK˜%Ñ¥KÒ/Ð/Ø�4Ý�!�S‰\Œ\ð 	$˜QØ—6’6‘8”8—>’>Ñ#Ô#ð	$ð 	$ð 	$ð 	$ñ 	$ô 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øøøð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øåð ð ð Øˆtˆtðøøøs<   ‚AB  ÁB  Á &BÂB  ÂBÂB  ÂBÂB  Â 
B.Â-B.c                  ó~   — t          ¦   «         } | €dS | dk    r$t          j        d¦  «        rt          ¦   «          dS dS )zíCheck if a recent install failure was persisted to disk.

    Returns False (allowing retry) when:
    - No marker exists
    - Marker is older than _MARKER_TTL (24h)
    - Marker reason is 'cosign_missing' and cosign is now on PATH
    NFÚcosign_missingÚcosignT)rB   ÚshutilÚwhichÚ_clear_install_failed)Úreasons    r   Ú_is_install_failed_on_diskrJ   ‡   sJ   € õ "Ñ#Ô#€FØ€~ØˆuØÐ!Ò!Ð!¥f¤l°8Ñ&<Ô&<Ð!ÝÑÔÐØˆuØˆ4r   rI   c                 ó(  — 	 t          ¦   «         }t          j        t          j                             |¦  «        d¬¦  «         t          |d¦  «        5 }|                     | ¦  «         ddd¦  «         dS # 1 swxY w Y   dS # t          $ r Y dS w xY w)a  Persist install failure to disk to avoid retry on next process.

    Args:
        reason: Short tag identifying the failure cause. Use "cosign_missing"
                when cosign is not on PATH so the marker can be auto-cleared
                once cosign becomes available.
    T©Úexist_okÚwN)r5   r   Úmakedirsr3   Údirnamer;   Úwriter>   )rI   r?   rA   s      r   Ú_mark_install_failedrR   ˜   sÕ   € ðÝ Ñ"Ô"ˆÝ
Œ•B”G—O’O AÑ&Ô&°Ð6Ñ6Ô6Ð6Ý�!�S‰\Œ\ð 	˜QØ�GŠG�F‰OŒOˆOð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð 	ð 	øåð ð ð Øˆˆðøøøs6   ‚AB ÁA6Á)B Á6A:Á:B Á=A:Á>B Â
BÂBc                  ój   — 	 t          j        t          ¦   «         ¦  «         dS # t          $ r Y dS w xY w)z3Remove the failure marker after successful install.N)r   Úunlinkr5   r>   r0   r   r   rH   rH   ©   sF   € ðÝ
Œ	Õ&Ñ(Ô(Ñ)Ô)Ð)Ð)Ð)øÝð ð ð Øˆˆðøøøs   ‚ $ ¤
2±2c                  óŠ   — t           j                             t          ¦   «         d¦  «        } t          j        | d¬¦  «         | S )z/Return $HERMES_HOME/bin, creating it if needed.ÚbinTrL   )r   r3   r4   r1   rO   )Úds    r   Ú_hermes_bin_dirrX   ±   s8   € å
Œ�ŠÕ%Ñ'Ô'¨Ñ/Ô/€AÝ„K�˜DÐ!Ñ!Ô!Ð!Ø€Hr   c                  óÄ   — t          j        ¦   «         } t          j        ¦   «                              ¦   «         }| dk    rd}n	| dv rd}ndS |dv rd}n	|dv rd	}ndS |› d
|› �S )z@Return the Rust target triple for the current platform, or None.ÚDarwinzapple-darwin)ÚLinuxÚAndroidzunknown-linux-gnuN)Úx86_64Úamd64r]   )Úaarch64Úarm64r_   Ú-)ÚplatformÚsystemÚmachiner   )rc   rd   ÚplatÚarchs       r   Ú_detect_targetrg   ¸   s”   € åŒ_ÑÔ€FÝÔÑ Ô ×&Ò&Ñ(Ô(€Gð �ÒÐØˆˆØ	Ð'Ð	'Ð	'Ø"ˆˆàˆtàÐ%Ð%Ð%ØˆˆØ	Ð(Ð	(Ð	(ØˆˆàˆtàÐÐ�TÐÐÐr   é
   ÚurlÚdestÚtimeoutc                 ó�  — t           j                             | ¦  «        }t          j        d¦  «        }|r|                     dd|› �¦  «         t           j                             ||¬¦  «        5 }t          |d¦  «        5 }t          j	        ||¦  «         ddd¦  «         n# 1 swxY w Y   ddd¦  «         dS # 1 swxY w Y   dS )zDownload a URL to a local file.ÚGITHUB_TOKENÚAuthorizationztoken )rk   ÚwbN)
ÚurllibÚrequestÚRequestr   r   Ú
add_headerÚurlopenr;   rF   Úcopyfileobj)ri   rj   rk   ÚreqÚtokenÚresprA   s          r   Ú_download_filery   Ï   s<  € å
Œ.×
 Ò
  Ñ
%Ô
%€CÝŒI�nÑ%Ô%€EØð :Ø�Š�Ð(8°Ð(8Ð(8Ñ9Ô9Ð9Ý	Œ×	Ò	 ¨WÐ	Ñ	5Ô	5ð $¸½tÀDÈ$Ñ?OÔ?Oð $ÐSTÝÔ˜4 Ñ#Ô#Ð#ð$ð $ð $ñ $ô $ð $ð $ð $ð $ð $ð $øøøð $ð $ð $ð $ð $ð $ð $ñ $ô $ð $ð $ð $ð $ð $ð $ð $øøøð $ð $ð $ð $ð $ð $s6   Á0B;ÂB#ÂB;Â#B'	Â'B;Â*B'	Â+B;Â;B?ÃB?Úchecksums_pathÚsig_pathÚ	cert_pathc                 ó  — t          j        d¦  «        }|st                               d¦  «         dS 	 t	          j        |dd|d|dt          dt          | gd	d	d
¬¦  «        }|j        dk    rt                               d¦  «         d	S t           	                    d|j        |j
                             ¦   «         ¦  «         dS # t          t          j        f$ r&}t           	                    d|¦  «         Y d}~dS d}~ww xY w)uk  Verify cosign provenance signature on checksums.txt.

    Returns:
        True  â€” cosign verified successfully
        False â€” cosign found but verification failed
        None  â€” cosign not available (not on PATH, or execution failed)

    The caller treats both False and None as "abort auto-install" â€” only
    True allows the install to proceed.
    rE   zcosign not found on PATHNzverify-blobz--certificatez--signaturez--certificate-identity-regexpz--certificate-oidc-issuerTé   ©Úcapture_outputÚtextrk   r   z%cosign provenance verification passedz(cosign verification failed (exit %d): %sFzcosign execution failed: %s)rF   rG   ÚloggerÚinfoÚ
subprocessÚrunÚ_COSIGN_IDENTITY_REGEXPÚ_COSIGN_ISSUERÚ
returncodeÚwarningÚstderrr=   r>   ÚTimeoutExpired)rz   r{   r|   rE   ÚresultÚexcs         r   Ú_verify_cosignrŽ   Ù   s#  € õ Œ\˜(Ñ#Ô#€FØð Ý�ŠÐ.Ñ/Ô/Ð/ØˆtðÝ”Ø�]Ø˜iØ˜HØ,Õ.EØ(­.Øðð  ØØð

ñ 

ô 

ˆð Ô Ò!Ð!Ý�KŠKÐ?Ñ@Ô@Ð@Ø�4å�NŠNÐEØ Ô+¨V¬]×-@Ò-@Ñ-BÔ-BñDô Dð Dà�5øÝ•ZÔ.Ð/ð ð ð Ý�ŠÐ4°cÑ:Ô:Ð:Øˆtˆtˆtˆtˆtøøøøðøøøs   ´AC Â8C ÃC>ÃC9Ã9C>Úarchive_pathÚarchive_namec                 ó‚  ‡	— d}t          |¦  «        5 Š	‰	D ]S}|                     ¦   «                              dd¦  «        }t          |¦  «        dk    r|d         |k    r
|d         } nŒTddd¦  «         n# 1 swxY w Y   |st                               d|¦  «         dS t          j        ¦   «         }t          | d¦  «        5 Š	t          ˆ	fd	„d
¦  «        D ]}| 	                    |¦  «         Œ	 ddd¦  «         n# 1 swxY w Y   | 
                    ¦   «         }||k    rt                               d||¦  «         dS dS )z4Verify SHA-256 of the archive against checksums.txt.Nz  é   é   r   zNo checksum entry for %sFÚrbc                  ó.   •— ‰                       d¦  «        S )Ni    )r<   )rA   s   €r   ú<lambda>z"_verify_checksum.<locals>.<lambda>  s   ø€  !§&¢&¨¡,¤,€ r   r   z&Checksum mismatch: expected %s, got %sT)r;   r=   ÚsplitÚlenr‚   r‰   ÚhashlibÚsha256ÚiterÚupdateÚ	hexdigest)
r�   rz   r�   ÚexpectedÚlineÚpartsÚshaÚchunkÚactualrA   s
            @r   Ú_verify_checksumr¤     sË  ø€ à€HÝ	ˆnÑ	Ô	ð  Øð 	ð 	ˆDà—J’J‘L”L×&Ò& t¨QÑ/Ô/ˆEÝ�5‰zŒz˜QŠˆ 5¨¤8¨|Ò#;Ð#;Ø  œ8�Ø�øðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð ð Ý�ŠÐ1°<Ñ@Ô@Ð@Øˆuå
Œ.Ñ
Ô
€CÝ	ˆl˜DÑ	!Ô	!ð  QÝÐ.Ð.Ð.Ð.°Ñ4Ô4ð 	ð 	ˆEØ�JŠJ�uÑÔÐÐð	ðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð �]Š]‰_Œ_€FØ�ÒÐÝ�ŠÐ?ÀÈ6ÑRÔRÐRØˆuØˆ4s$   “AA6Á6A:Á=A:Ã,C<Ã<D ÄD T©Úlog_failuresr¦   c                 óÆ	  — | rt           j        nt           j        }t          ¦   «         }|s@t                                dt          j        ¦   «         t          j        ¦   «         ¦  «         dS d|› d�}dt          › d�}t          j
        d¬¦  «        }	 t          j                             ||¦  «        }t          j                             |d	¦  «        }t          j                             |d
¦  «        }t          j                             |d¦  «        }	t                                d|¦  «         	 t          |› d|› �|¦  «         t          |› d�|¦  «         n:# t          $ r-}
 |d|
¦  «         Y d}
~
t!          j        |d¬¦  «         dS d}
~
ww xY wd}t!          j        d¦  «        r´	 t          |› d�|¦  «         t          |› d�|	¦  «         t'          |||	¦  «        }|du rd}n�|du r$ |d¦  «         	 t!          j        |d¬¦  «         dS t                                d¦  «         nL# t          $ r%}
t                                d|
¦  «         Y d}
~
n"d}
~
ww xY wt                                d¦  «         t)          |||¦  «        s	 t!          j        |d¬¦  «         dS t+          j        |d¦  «        5 }|                     ¦   «         D ]P}|j        dk    s|j                             d ¦  «        r)d!|j        v rŒ1d|_        |                     ||¦  «          n0ŒQ |d"¦  «         	 ddd¦  «         t!          j        |d¬¦  «         d#S 	 ddd¦  «         n# 1 swxY w Y   t          j                             |d¦  «        }t          j                             t7          ¦   «         d¦  «        }	 t!          j        ||¦  «         nu# t:          $ rh 	 t!          j        ||¦  «         nN# t:          $ rA 	 t          j        |¦  «         n# t:          $ r Y nw xY wY Y t!          j        |d¬¦  «         d$S w xY wY nw xY wt          j         |t          j!        |¦  «        j"        tB          j#        z  tB          j$        z  tB          j%        z  ¦  «         |rd%nd&}t                                d'||¦  «         |d(ft!          j        |d¬¦  «         S # t!          j        |d¬¦  «         w xY w))a^  Download and install tirith to $HERMES_HOME/bin/tirith.

    Verifies provenance via cosign and SHA-256 checksum.
    Returns (installed_path, failure_reason).  On success failure_reason is "".
    failure_reason is a short tag used by the disk marker to decide if the
    failure is retryable (e.g. "cosign_missing" clears when cosign appears).
    z/tirith auto-install: unsupported platform %s/%s)NÚunsupported_platformztirith-z.tar.gzzhttps://github.com/z/releases/latest/downloadztirith-install-)Úprefixzchecksums.txtzchecksums.txt.sigzchecksums.txt.pemu9   tirith not found â€” downloading latest release for %s...ú/z/checksums.txtztirith download failed: %sNT)Úignore_errors)NÚdownload_failedFrE   z/checksums.txt.sigz/checksums.txt.pemz=tirith install aborted: cosign provenance verification failed)NÚcosign_verification_failedz5cosign execution failed, proceeding with SHA-256 onlyz?cosign artifacts unavailable (%s), proceeding with SHA-256 onlyu{   cosign not on PATH â€” installing tirith with SHA-256 verification only (install cosign for full supply chain verification))NÚchecksum_failedzr:gzr   z/tirithz..z"tirith binary not found in archive)NÚbinary_not_in_archive)NÚcross_device_copy_failedzcosign + SHA-256zSHA-256 onlyztirith installed to %s (%s)r+   )&r‚   r‰   Údebugrg   rƒ   rb   rc   rd   Ú_REPOÚtempfileÚmkdtempr   r3   r4   ry   r&   rF   ÚrmtreerG   rŽ   r¤   Útarfiler;   Ú
getmembersÚnameÚendswithÚextractrX   Úmover>   ÚcopyrT   ÚchmodÚstatÚst_modeÚS_IXUSRÚS_IXGRPÚS_IXOTH)r¦   ÚlogÚtargetr�   Úbase_urlÚtmpdirr�   rz   r{   r|   r�   Úcosign_verifiedÚcosign_resultÚtarÚmemberÚsrcrj   Úverifications                     r   Ú_install_tirithrÍ     sÑ  € ð )Ð
:�&Œ.ˆ.­f¬l€CåÑÔ€FØð ,Ý�ŠÐEÝ”_Ñ&Ô&­Ô(8Ñ(:Ô(:ñ	<ô 	<ð 	<à+Ð+à,˜VÐ,Ð,Ð,€LØE¥UÐEÐEÐE€HåÔÐ%6Ð7Ñ7Ô7€FðT2Ý”w—|’| F¨LÑ9Ô9ˆÝœŸš f¨oÑ>Ô>ˆÝ”7—<’< Ð(;Ñ<Ô<ˆÝ”G—L’L Ð)<Ñ=Ô=ˆ	å�ŠÐOÐQWÑXÔXÐXð	+Ý˜hÐ7Ð7¨Ð7Ð7¸ÑFÔFÐFÝ˜hÐ6Ð6Ð6¸ÑGÔGÐGÐGøÝð 	+ð 	+ð 	+ØˆCÐ,¨cÑ2Ô2Ð2Ø*Ð*Ð*Ð*õN 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1øøøøðS	+øøøð  ˆÝŒ<˜Ñ!Ô!ð 	OðYÝ (Ð>Ð>Ð>ÀÑIÔIÐIÝ (Ð>Ð>Ð>À	ÑJÔJÐJõ !/¨~¸xÈÑ SÔ S�Ø  DÐ(Ð(Ø&*�O�OØ" eÐ+Ð+ð �CÐWÑXÔXÐXØ=õb 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1õ[ —K’KÐ WÑXÔXÐXÐXøõ ð dð dð dÝ—’Ð]Ð_bÑcÔcÐcÐcÐcÐcÐcÐcøøøøðdøøøõ  �KŠKð Nñ Oô Oð Oõ   ¨n¸lÑKÔKð 	+Ø*õN 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1õK Œ\˜,¨Ñ/Ô/ð 	5°3àŸ.š.Ñ*Ô*ð 	5ð 	5�Ø”; (Ò*Ð*¨f¬k×.BÒ.BÀ9Ñ.MÔ.MÐ*Ø˜vœ{Ð*Ð*Ø Ø"*�F”KØ—K’K ¨Ñ/Ô/Ð/Ø�Eð +ð �Ð8Ñ9Ô9Ð9Ø4ð	5ð 	5ð 	5ñ 	5ô 	5ð 	5õJ 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1ð; ð	5ð 	5ð 	5ñ 	5ô 	5ð 	5ð 	5ð 	5ð 	5ð 	5ð 	5øøøð 	5ð 	5ð 	5ð 	5õ Œg�lŠl˜6 8Ñ,Ô,ˆÝŒw�|Š|�OÑ-Ô-¨xÑ8Ô8ˆð	8ÝŒK˜˜TÑ"Ô"Ð"Ð"øÝð 	8ð 	8ð 	8ð8Ý”˜C Ñ&Ô&Ð&Ð&øÝð 8ð 8ð 8ðÝ”I˜d‘O”O�O�OøÝð ð ð Ø�Dðøøøà7Ð7õ 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1ð8øøøð 'Ð&ð	8øøøõ 	Œ�•r”w˜t‘}”}Ô,­t¬|Ñ;½d¼lÑJÍTÌ\ÑYÑZÔZÐZà-<ÐPÐ)Ð)À.ˆÝ�ŠÐ1°4¸ÑFÔFÐFØ�Rˆxõ 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1ø�Œ�f¨DÐ1Ñ1Ô1Ð1Ð1øøøs  ÂBS Ä.(E ÅS Å
FÅ!F	Å-S Æ	FÆS Æ(&H) Ç'S ÈS È)
IÈ3IÉS ÉIÉ.S ÊS Ê4A2MÌ&S Í
S ÍMÍS ÍMÍAS Î.O ÏS Ï
P6ÏO%Ï$P6Ï%
P0Ï0PÐP0Ð
PÐP0ÐPÐP0ÐP6ÐS Ð/P0Ð0P6Ð3S Ð5P6Ð6A;S ÓS Úconfigured_pathc                 ó   — | dk    S )zHReturn True if the user explicitly configured a non-default tirith path.r   r0   )rÎ   s    r   Ú_is_explicit_pathrÐ   …  s   € à˜hÒ&Ð&r   c                 ó’  — t           �t           t          urt           S t          j                             | ¦  «        }t          | ¦  «        }t           t          u }|r‚t          j                             |¦  «        r#t          j        |t          j        ¦  «        r|a |S t          j
        |¦  «        }|r|a |S t                               d| ¦  «         t          a da|S t          j
        d¦  «        }|r|a dat          ¦   «          |S t          j                             t!          ¦   «         d¦  «        }t          j                             |¦  «        r3t          j        |t          j        ¦  «        r|a dat          ¦   «          |S |r6t          dk    r)t          j
        d¦  «        rda dat          ¦   «          d}n|S t"          �t"                               ¦   «         r|S t'          ¦   «         }|�t)          ¦   «         rt          a |a|S t+          ¦   «         \  }}|r|a dat          ¦   «          |S t          a |at-          |¦  «         |S )	u@  Resolve the tirith binary path, auto-installing if necessary.

    If the user explicitly set a path (anything other than the bare "tirith"
    default), that path is authoritative â€” we never fall through to
    auto-download a different binary.

    For the default "tirith":
    1. PATH lookup via shutil.which
    2. $HERMES_HOME/bin/tirith (previously auto-installed)
    3. Auto-install from GitHub releases â†’ $HERMES_HOME/bin/tirith

    Failed installs are cached for the process lifetime (and persisted to
    disk for 24h) to avoid repeated network attempts.
    Nz6Configured tirith path %r not found; scanning disabledÚexplicit_path_missingr   r+   rD   rE   F)r*   Ú_INSTALL_FAILEDr   r3   Ú
expanduserrÐ   ÚisfileÚaccessÚX_OKrF   rG   r‚   r‰   r,   rH   r4   rX   r-   Úis_aliverB   rJ   rÍ   rR   )	rÎ   ÚexpandedÚexplicitÚinstall_failedÚfoundÚ
hermes_binÚdisk_reasonÚ	installedrI   s	            r   Ú_resolve_tirith_pathrà   Š  sN  € õ$ Ð!¥n½OÐ&KÐ&KÝÐåŒw×!Ò! /Ñ2Ô2€HÝ  Ñ1Ô1€HÝ#¥Ð6€Nð ð ÝŒ7�>Š>˜(Ñ#Ô#ð 	­¬	°(½B¼GÑ(DÔ(Dð 	Ø%ˆNØˆOå”˜XÑ&Ô&ˆØð 	Ø"ˆNØˆLÝ�ŠÐOÐQ`ÑaÔaÐaÝ(ˆØ"9ÐØˆõ
 ŒL˜Ñ"Ô"€EØð ØˆØ"$ÐÝÑÔÐØˆå”—’�oÑ/Ô/°Ñ:Ô:€JÝ	„w‡~‚~�jÑ!Ô!ð ¥b¤i°
½B¼GÑ&DÔ&Dð Ø#ˆØ"$ÐÝÑÔÐØÐð
 ð Ý"Ð&6Ò6Ð6½6¼<ÈÑ;QÔ;QÐ6à!ˆNØ&(Ð#Ý!Ñ#Ô#Ð#Ø"ˆNˆNàˆOõ
 Ð"¥×'?Ò'?Ñ'AÔ'AÐ"Øˆõ
 'Ñ(Ô(€KØÐÕ#=Ñ#?Ô#?ÐÝ(ˆØ"-ÐØˆå'Ñ)Ô)Ñ€IˆvØð Ø"ˆØ"$ÐÝÑÔÐØÐõ %€NØ$ÐÝ˜Ñ Ô Ð Ø€Or   c                 ó8  — t           5  t          �	 ddd¦  «         dS t          j        d¦  «        }|r|ada	 ddd¦  «         dS t
          j                             t          ¦   «         d¦  «        }t
          j         	                    |¦  «        r1t          j
        |t
          j        ¦  «        r|ada	 ddd¦  «         dS t          | ¬¦  «        \  }}|r|adat          ¦   «          nt          a|at          |¦  «         ddd¦  «         dS # 1 swxY w Y   dS )z6Background thread target: download and install tirith.Nr   r+   r¥   )Ú_install_lockr*   rF   rG   r,   r   r3   r4   rX   rÕ   rÖ   r×   rÍ   rH   rÓ   rR   )r¦   rÜ   rÝ   rß   rI   s        r   Ú_background_installrã   í  s¾  € õ 
ð )ð )åÐ%Øð)ð )ð )ñ )ô )ð )ð )ð )õ ”˜XÑ&Ô&ˆØð 	Ø"ˆNØ&(Ð#Øð)ð )ð )ñ )ô )ð )ð )ð )õ ”W—\’\¥/Ñ"3Ô"3°XÑ>Ô>ˆ
ÝŒ7�>Š>˜*Ñ%Ô%ð 	­"¬)°JÅÄÑ*HÔ*Hð 	Ø'ˆNØ&(Ð#Øð!)ð )ð )ñ )ô )ð )ð )ð )õ$ ,¸ÐFÑFÔFÑˆ	�6Øð 	)Ø&ˆNØ&(Ð#Ý!Ñ#Ô#Ð#Ð#å,ˆNØ&,Ð#Ý  Ñ(Ô(Ð(ð5)ð )ð )ñ )ô )ð )ð )ð )ð )ð )ð )ð )øøøð )ð )ð )ð )ð )ð )s$   ˆ	DžDÁA/DÃA DÄDÄDc                 ó  — t          ¦   «         }|d         sdS t          �Wt          t          urIt          }t          j                             |¦  «        r!t          j        |t          j        ¦  «        r|S dS |d         }t          |¦  «        }t          j         	                    |¦  «        }|rgt          j                             |¦  «        r#t          j        |t          j        ¦  «        r|a|S t          j        |¦  «        }|r|a|S t          adadS t          j        d¦  «        }|r|adat          ¦   «          |S t          j                             t          ¦   «         d¦  «        }t          j                             |¦  «        r3t          j        |t          j        ¦  «        r|adat          ¦   «          |S t          t          u r4t          dk    r't          j        d¦  «        rdadat          ¦   «          ndS t!          ¦   «         }|�t#          ¦   «         rt          a|adS t$          �t$                               ¦   «         s7t)          j        t,          d	| id
¬¦  «        at$                               ¦   «          dS )a  Ensure tirith is available, downloading in background if needed.

    Quick PATH/local checks are synchronous; network download runs in a
    daemon thread so startup never blocks. Safe to call multiple times.
    Returns the resolved path immediately if available, or None.
    r   Nr   rÒ   r   r+   rD   rE   r¦   T)rÄ   ÚkwargsÚdaemon)r)   r*   rÓ   r   r3   rÕ   rÖ   r×   rÐ   rÔ   rF   rG   r,   rH   r4   rX   rB   rJ   r-   rØ   Ú	threadingÚThreadrã   Ústart)	r¦   r(   r3   rÎ   rÚ   rÙ   rÜ   rÝ   rÞ   s	            r   Úensure_installedrê     so  € õ  Ñ
!Ô
!€CØÐÔ ð Øˆtõ Ð!¥n½OÐ&KÐ&KÝˆÝŒ7�>Š>˜$ÑÔð 	¥B¤I¨dµB´GÑ$<Ô$<ð 	ØˆKØˆtà˜-Ô(€OÝ  Ñ1Ô1€HÝŒw×!Ò! /Ñ2Ô2€Hð ð 
ÝŒ7�>Š>˜(Ñ#Ô#ð 	­¬	°(½B¼GÑ(DÔ(Dð 	Ø%ˆNØˆOÝ”˜XÑ&Ô&ˆØð 	Ø"ˆNØˆLÝ(ˆØ"9ÐØˆtõ ŒL˜Ñ"Ô"€EØð ØˆØ"$ÐÝÑÔÐØˆå”—’�oÑ/Ô/°Ñ:Ô:€JÝ	„w‡~‚~�jÑ!Ô!ð ¥b¤i°
½B¼GÑ&DÔ&Dð Ø#ˆØ"$ÐÝÑÔÐØÐõ �Ð(Ð(Ý"Ð&6Ò6Ð6½6¼<ÈÑ;QÔ;QÐ6Ø!ˆNØ&(Ð#Ý!Ñ#Ô#Ð#Ð#à�4õ
 'Ñ(Ô(€KØÐÕ#=Ñ#?Ô#?ÐÝ(ˆØ"-ÐØˆtõ Ð¥o×&>Ò&>Ñ&@Ô&@ÐÝ#Ô*Ý&Ø" LÐ1Øð
ñ 
ô 
ˆõ
 	×ÒÑÔÐàˆ4r   é2   iô  Úcommandc           
      óx  — t          ¦   «         }|d         sdg ddœS t          |d         ¦  «        }|d         }|d         }|€(t                               d	¦  «         |rdg d
dœS dg ddœS 	 t	          j        |dddddd| gdd|¬¦  «        }nŠ# t          $ r>}t                               d|¦  «         |rdg d|› �dœcY d}~S dg d|› �dœcY d}~S d}~wt          j        $ r2 t                               d|¦  «         |rdg d|› d�dœcY S dg ddœcY S w xY w|j        }|dk    rd}nC|dk    rd}n:|dk    rd}n1t                               d |¦  «         |r
dg d!|› d"�dœS dg d!|› d#�dœS g }	d}
	 |j	         
                    ¦   «         rt          j        |j	        ¦  «        ni }|                     d$g ¦  «        }|dt          …         }	|                     d%d¦  «        pddt          …         }
nG# t          j        t"          f$ r. t                               d&¦  «         |dk    rd'}
n|dk    rd(}
Y nw xY w||	|
dœS ))a@  Run tirith security scan on a command.

    Exit code determines action (0=allow, 1=block, 2=warn). JSON enriches
    findings/summary. Spawn failures and timeouts respect fail_open config.
    Programming errors propagate.

    Returns:
        {"action": "allow"|"warn"|"block", "findings": [...], "summary": str}
    r   Úallowr+   )ÚactionÚfindingsÚsummaryr   r   r   Nz/tirith path resolved to None; scanning disabledztirith path unavailableÚblockz%tirith path unavailable (fail-closed)Úcheckz--jsonz--non-interactivez--shellÚposixz--Tr   ztirith spawn failed: %sztirith unavailable: z#tirith spawn failed (fail-closed): ztirith timed out after %dsztirith timed out (zs)ztirith timed out (fail-closed)r   r’   r“   Úwarnz'tirith returned unexpected exit code %dztirith exit code z (fail-open)z (fail-closed)rð   rñ   z.tirith JSON parse failed, using exit code onlyz-security issue detected (details unavailable)z/security warning detected (details unavailable))r)   rà   r‚   r‰   r„   r…   r>   r‹   rˆ   Ústdoutr=   ÚjsonÚloadsr%   Ú_MAX_FINDINGSÚ_MAX_SUMMARY_LENÚJSONDecodeErrorÚAttributeErrorr±   )rì   r(   r   rk   Ú	fail_openrŒ   r�   Ú	exit_coderï   rð   rñ   ÚdataÚraw_findingss                r   Úcheck_command_securityr  g  sd  € õ  Ñ
!Ô
!€CàÐÔ ð BØ!¨r¸bÐAÐAÐAå& s¨=Ô'9Ñ:Ô:€KØÐ"Ô#€GØÐ&Ô'€IàÐÝ�ŠÐHÑIÔIÐIØð 	]Ø%°2ÐB[Ð\Ð\Ð\Ø!¨rÐ>eÐfÐfÐfð`Ý”Ø˜' 8Ð-@Ø˜  wð0àØØð
ñ 
ô 
ˆˆøõ ð kð kð kå�ŠÐ0°#Ñ6Ô6Ð6Øð 	`Ø%°2ÐB^ÐY\ÐB^ÐB^Ð_Ð_Ð_Ð_Ð_Ð_Ð_Ð_Ø!¨rÐ>iÐdgÐ>iÐ>iÐjÐjÐjÐjÐjÐjÐjÐjøøøøÝÔ$ð `ð `ð `Ý�ŠÐ3°WÑ=Ô=Ð=Øð 	dØ%°2ÐBbÐW^ÐBbÐBbÐBbÐcÐcÐcÐcÐcØ!¨rÐ>^Ð_Ð_Ð_Ð_Ð_ð	`øøøð Ô!€IØ�A‚~€~ØˆˆØ	�aŠˆØˆˆØ	�aŠˆØˆˆõ 	�ŠÐ@À)ÑLÔLÐLØð 	oØ%°2ÐBmÐV_ÐBmÐBmÐBmÐnÐnÐnØ!¨rÐ>kÐR[Ð>kÐ>kÐ>kÐlÐlÐlð €HØ€GðHØ,2¬M×,?Ò,?Ñ,AÔ,AÐI�tŒz˜&œ-Ñ(Ô(Ð(ÀrˆØ—x’x 
¨BÑ/Ô/ˆØ ¥ Ô/ˆØ—8’8˜I rÑ*Ô*Ð0¨bÐ2CÕ3CÐ2CÔDˆˆøÝÔ ¥.Ð1ð Hð Hð Hå�ŠÐEÑFÔFÐFØ�WÒÐØEˆGˆGØ�vÒÐØGˆGøøðHøøøð ¨(¸wÐGÐGÐGsJ   Á- B Â
DÂ%CÂ=DÃCÃDÃ9DÄDÄDÅ0A>G/ Ç/AH3È2H3)r+   )rh   );Ú__doc__r™   r÷   Úloggingr   rb   rF   r¾   r„   r¶   r³   rç   r9   Úurllib.requestrp   Úhermes_constantsr   Ú	getLoggerÚ__name__r‚   r²   r†   r‡   r/   Úboolr   r   r   Údictr)   r*   Ú__annotations__rÓ   r,   ÚLockrâ   r-   rè   r:   r1   r5   rB   rJ   rR   rH   rX   rg   ry   rŽ   r¤   ÚtuplerÍ   rÐ   rà   rã   rê   rù   rú   r  r0   r   r   ú<module>r     sO  ððð ð ð, €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ø €€€Ø €€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð à ,Ð ,Ð ,Ð ,Ð ,Ð ,à	ˆÔ	˜8Ñ	$Ô	$€à€ð g°ÐfÐfÐfÐ Ø>€ð/�3ð / ð /¨$ð /ð /ð /ð /ð�#ð  ð ¨ð ð ð ð ð˜tð ð ð ð ð8 %)€��d‘
˜TÑ!Ð (Ð (Ñ (Ø€Ø!Ð ˜Ð !Ð !Ñ !ð �	”Ñ Ô €Ø+/€�Ô! DÑ(Ð /Ð /Ñ /ð €ð"˜#ð "ð "ð "ð "ð
F˜cð Fð Fð Fð Fð
˜c D™jð ð ð ð ð" Dð ð ð ð ð"ð  ð ð ð ð ð"ð ð ð˜ð ð ð ð ð˜˜d™
ð ð ð ð ð.$ð $˜ð $ 3ð $°ð $ð $ð $ð $ð% 3ð %°#ð %À#ð %È$ÐQUÉ+ð %ð %ð %ð %ðP 3ð ¸ð È3ð ÐSWð ð ð ð ð2 -1ð h2ð h2ð h2 Tð h2°U¸3À¹:Às¸?Ô5Kð h2ð h2ð h2ð h2ðV' sð '¨tð 'ð 'ð 'ð 'ð
`¨#ð `°#ð `ð `ð `ð `ðF 15ð )ð )ð )¨ð )ð )ð )ð )ð@ .2ð Oð Oð O dð Oð Oð Oð Oðl €ØÐ ðLH Cð LH¨Dð LHð LHð LHð LHð LHð LHr   