§
    øžyjý„  ã            	       ól  — 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<   dZ"da#eed<   da$eed<   dDd„Z% ej&        ¦   «         Z'da(ej)        dz  ed<    e*¦   «         Z+e*e         ed<    ej&        ¦   «         Z,deded
dfd„Z-dDd„Z.dZ/d
efd„Z0d
efd„Z1d
edz  fd„Z2d
efd„Z3dEd efd!„Z4d"„ Z5d
efd#„Z6d
edz  fd$„Z7d
efd%„Z8dFd'ed(ed)efd*„Z9d+ed,ed-ed
edz  fd.„Z:d/ed+ed0ed
efd1„Z;d2e	j<        d3ed
e=edz  ef         fd4„Z>d5d6œd7ed
e=edz  ef         fd8„Z?d9ed
efd:„Z@d9ed
efd;„ZAd5d6œd7efd<„ZBd5d6œd7efd=„ZCd>ZDd?ZEd@ed
efdA„ZFdBed
efdC„ZGdS )Guù  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ÚyesÚtrue)ÚosÚgetenvÚlower©r   r   Úvals      ú;/home/ragecks/.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_readonlyÚ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ðØ:Ð:Ð:Ð:Ð:Ð:Ø"Ð"Ñ$Ô$×(Ò(¨°RÑ8Ô8Ð>¸BˆˆøÝð ð ð Øˆˆˆðøøøõ $Ð$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é   Ú_crash_countÚ_circuit_openc                  ó‚   — t           dz  a t           t          k    r$dat                               dt           ¦  «         dS dS )zCIncrement the crash counter and open the circuit breaker if needed.é   Tzbtirith circuit breaker opened after %d consecutive failures; disabling for the rest of the processN)r.   Ú_CRASH_LIMITr/   ÚloggerÚwarning© r   r   Ú_record_tirith_crashr6   u   sN   € õ �AÑ€LÝ•|Ò#Ð#ØˆÝ�Šð4åñ	
ô 	
ð 	
ð 	
ð 	
ð $Ð#r   Ú_install_threadÚ_warned_messagesÚmessagec                 óÊ   — t           5  | t          v r	 ddd¦  «         dS t                               | ¦  «         ddd¦  «         n# 1 swxY w Y   t          j        |g|¢R Ž  dS )z±``logger.warning`` but at-most-once per ``key`` for the process
    lifetime. Used to avoid drowning the log when a fail-open tirith
    misconfiguration fires on every command.N)Ú_warned_lockr8   Úaddr3   r4   )r   r9   Úargss      r   Ú
_warn_oncer>   Ž   sÌ   € õ 
ð "ð "ØÕ"Ð"Ð"Øð"ð "ð "ñ "ô "ð "ð "ð "õ 	×Ò˜SÑ!Ô!Ð!ð"ð "ð "ñ "ô "ð "ð "ð "ð "ð "ð "øøøð "ð "ð "ð "õ „N�7Ð"˜TÐ"Ð"Ð"Ð"Ð"Ð"s   ˆA AÁA
ÁA
c                  óx   — t           5  t                               ¦   «          ddd¦  «         dS # 1 swxY w Y   dS )u­   Clear the warn-once dedupe set. Called when tirith is freshly
    (re)installed so a subsequent failure surfaces again â€” e.g. user
    deletes the binary mid-session.
    N)r;   r8   Úclearr5   r   r   Ú_reset_spawn_warning_staterA   ™   s€   € õ
 
ð !ð !Ý×ÒÑ Ô Ð ð!ð !ð !ñ !ô !ð !ð !ð !ð !ð !ð !ð !øøøð !ð !ð !ð !ð !ð !s   ˆ/¯3¶3i€Q c                  ó8   — t          t          ¦   «         ¦  «        S )zAReturn the Hermes home directory, respecting HERMES_HOME env var.)Ústrr   r5   r   r   Ú_get_hermes_homerD   ¥   s   € å�Ñ Ô Ñ!Ô!Ð!r   c                  óZ   — t           j                             t          ¦   «         d¦  «        S )z3Return the path to the install-failure marker file.z.tirith-install-failed)r   ÚpathÚjoinrD   r5   r   r   Ú_failure_marker_pathrH   ª   s    € åŒ7�<Š<Õ(Ñ*Ô*Ð,DÑEÔEÐEr   c                  óf  — 	 t          ¦   «         } t          j                             | ¦  «        }t	          j        ¦   «         |z
  t
          k    rdS t          | d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úutf-8©Úencoding)
rH   r   rF   ÚgetmtimeÚtimeÚ_MARKER_TTLÚopenÚreadÚstripÚOSError)ÚpÚmtimeÚfs      r   Ú_read_failure_reasonrX   ¯   sï   € ðÝ Ñ"Ô"ˆÝ”× Ò  Ñ#Ô#ˆÝŒI‰KŒK˜%Ñ¥KÒ/Ð/Ø�4Ý�!�S 7Ð+Ñ+Ô+ð 	$¨qØ—6’6‘8”8—>’>Ñ#Ô#ð	$ð 	$ð 	$ð 	$ñ 	$ô 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øøøð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øåð ð ð Øˆtˆtðøøøs<   ‚AB" ÁB" Á"&BÂB" ÂBÂB" ÂBÂB" Â"
B0Â/B0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)rX   ÚshutilÚwhichÚ_clear_install_failed)Úreasons    r   Ú_is_install_failed_on_diskr`   À   sJ   € õ "Ñ#Ô#€FØ€~ØˆuØÐ!Ò!Ð!¥f¤l°8Ñ&<Ô&<Ð!ÝÑÔÐØˆuØˆ4r   r_   c                 ó,  — 	 t          ¦   «         }t          j        t          j                             |¦  «        d¬¦  «         t          |d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ÚwrK   rL   N)rH   r   ÚmakedirsrF   ÚdirnamerQ   ÚwriterT   )r_   rU   rW   s      r   Ú_mark_install_failedrh   Ñ   sÜ   € ðÝ Ñ"Ô"ˆÝ
Œ•B”G—O’O AÑ&Ô&°Ð6Ñ6Ô6Ð6Ý�!�S 7Ð+Ñ+Ô+ð 	¨qØ�GŠG�F‰OŒOˆOð	ð 	ð 	ñ 	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	øøøð 	ð 	ð 	ð 	ð 	ð 	øåð ð ð Øˆˆðøøøs6   ‚AB ÁA8Á+B Á8A<Á<B Á?A<Â B Â
BÂBc                  ó†   — t          ¦   «          	 t          j        t          ¦   «         ¦  «         dS # t          $ r Y dS w xY w)z3Remove the failure marker after successful install.N)rA   r   ÚunlinkrH   rT   r5   r   r   r^   r^   â   sT   € õ
 Ñ Ô Ð ðÝ
Œ	Õ&Ñ(Ô(Ñ)Ô)Ð)Ð)Ð)øÝð ð ð Øˆˆðøøøs   � 2 ²
A ¿A c                  óŠ   — t           j                             t          ¦   «         d¦  «        } t          j        | d¬¦  «         | S )z/Return $HERMES_HOME/bin, creating it if needed.ÚbinTrb   )r   rF   rG   rD   re   )Úds    r   Ú_hermes_bin_dirrn   î   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 )u  Return the Rust target triple for the current platform, or None.

    Windows is intentionally unsupported â€” tirith does not ship a Windows
    build. Callers should treat `None` as "this platform will never have
    tirith" and silently fall back to pattern-matching guards.
    ÚDarwinzapple-darwin>   ÚLinuxÚAndroidzunknown-linux-gnuN>   Úamd64Úx86_64rt   >   Úarm64Úaarch64rv   Ú-)ÚplatformÚsystemÚmachiner   )ry   rz   ÚplatÚarchs       r   Ú_detect_targetr}   õ   s–   € õ Œ_ÑÔ€FÝÔÑ Ô ×&Ò&Ñ(Ô(€Gð �ÒÐØˆˆØ	Ð'Ð	'Ð	'Ø"ˆˆàˆtàÐ%Ð%Ð%ØˆˆØ	Ð(Ð	(Ð	(ØˆˆàˆtàÐÐ�TÐÐÐr   c                  ó"   — t          ¦   «         duS )u  True when tirith ships a prebuilt binary for this OS+arch.

    Used by callers (CLI banner, etc.) to distinguish "tirith failed to
    install" from "tirith was never going to install here" â€” the latter
    is silent because there is nothing the user can do about it.
    N)r}   r5   r   r   Úis_platform_supportedr     s   € õ ÑÔ 4Ð'Ð'r   é
   ÚurlÚdestÚtimeoutc                 óŠ  — t           j                             | ¦  «        }ddlm}  |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.r   )Ú
get_secretÚGITHUB_TOKENÚAuthorizationztoken )rƒ   ÚwbN)
ÚurllibÚrequestÚRequestÚagent.secret_scoper…   Ú
add_headerÚurlopenrQ   r\   Úcopyfileobj)r�   r‚   rƒ   Úreqr…   ÚtokenÚresprW   s           r   Ú_download_filer“     sN  € å
Œ.×
 Ò
  Ñ
%Ô
%€CØ-Ð-Ð-Ð-Ð-Ð-ØˆJ�~Ñ&Ô&€EØð :Ø�Š�Ð(8°Ð(8Ð(8Ñ9Ô9Ð9Ý	Œ×	Ò	 ¨WÐ	Ñ	5Ô	5ð $¸½tÀDÈ$Ñ?OÔ?Oð $ÐSTÝÔ˜4 Ñ#Ô#Ð#ð$ð $ð $ñ $ô $ð $ð $ð $ð $ð $ð $øøøð $ð $ð $ð $ð $ð $ð $ñ $ô $ð $ð $ð $ð $ð $ð $ð $øøøð $ð $ð $ð $ð $ð $s6   Á-B8Á>B ÂB8Â B$	Â$B8Â'B$	Â(B8Â8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
ddt          j        ¬¦  «        }|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.
    r[   zcosign not found on PATHNzverify-blobz--certificatez--signaturez--certificate-identity-regexpz--certificate-oidc-issuerTrK   Úreplaceé   ©Úcapture_outputÚtextrM   Úerrorsrƒ   Ústdinr   z%cosign provenance verification passedz(cosign verification failed (exit %d): %sFzcosign execution failed: %s)r\   r]   r3   ÚinfoÚ
subprocessÚrunÚ_COSIGN_IDENTITY_REGEXPÚ_COSIGN_ISSUERÚDEVNULLÚ
returncoder4   ÚstderrrS   rT   ÚTimeoutExpired)r”   r•   r–   r[   Ú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 ÃDÃ%DÄDÚarchive_pathÚarchive_namec                 ó†  ‡	— d}t          |d¬¦  «        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.NrK   rL   z  r1   é   r   zNo checksum entry for %sFÚrbc                  ó.   •— ‰                       d¦  «        S )Ni    )rR   )rW   s   €r   ú<lambda>z"_verify_checksum.<locals>.<lambda>_  s   ø€  !§&¢&¨¡,¤,€ r   r   z&Checksum mismatch: expected %s, got %sT)rQ   rS   ÚsplitÚlenr3   r4   ÚhashlibÚsha256ÚiterÚupdateÚ	hexdigest)
r«   r”   r¬   ÚexpectedÚlineÚpartsÚshaÚchunkÚactualrW   s
            @r   Ú_verify_checksumr¿   O  sÐ  ø€ à€HÝ	ˆn wÐ	/Ñ	/Ô	/ð °1Øð 	ð 	ˆ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A8Á8A<Á?A<Ã,C>Ã>DÄDÚtarÚdest_dirc                 ój  — |                       ¦   «         D �]}|j        dk    s|j                             d¦  «        réd|j        v rŒ2|                     ¦   «         s |d|j        ¦  «          dS |                      |¦  «        }|€ |d¦  «          dS t
          j                             |d¦  «        }	 t          |d	¦  «        5 }t          j
        ||¦  «         ddd¦  «         n# 1 swxY w Y   |                     ¦   «          n# |                     ¦   «          w xY w|d
fc S �Œ |d¦  «         dS )z?Extract the tirith binary from a release archive into dest_dir.r   z/tirithz..z/tirith archive member is not a regular file: %s)NÚbinary_not_regular_fileNz,tirith binary could not be read from archive)NÚbinary_extract_failedrˆ   r+   z"tirith binary not found in archive)NÚbinary_not_in_archive)Ú
getmembersÚnameÚendswithÚisfileÚextractfiler   rF   rG   rQ   r\   r�   Úclose)rÀ   rÁ   ÚlogÚmemberÚsrc_fileÚ	dest_pathÚouts          r   Ú_extract_tirith_binaryrÑ   h  s”  € à—.’.Ñ"Ô"ð !ñ !ˆØŒ;˜(Ò"Ð" f¤k×&:Ò&:¸9Ñ&EÔ&EÐ"Ø�v”{Ð"Ð"ØØ—=’=‘?”?ð 7Ø�ÐEÀvÄ{ÑSÔSÐSØ6Ð6Ð6Ø—’ vÑ.Ô.ˆHØÐØ�ÐBÑCÔCÐCØ4Ð4Ð4åœŸš X¨xÑ8Ô8ˆIð!Ý˜) TÑ*Ô*ð 6¨cÝÔ& x°Ñ5Ô5Ð5ð6ð 6ð 6ñ 6ô 6ð 6ð 6ð 6ð 6ð 6ð 6øøøð 6ð 6ð 6ð 6ð —’Ñ Ô Ð Ð ø�—’Ñ Ô Ð Ð øøøØ˜b�=Ð Ð Ð ñ# #ð& €CÐ,Ñ-Ô-Ð-Ø(Ð(s0   Â5DÃC'ÃDÃ'C+	Ã+DÃ.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¬¦  «        }n$# t          $ r} |d	|¦  «         Y d
}~dS d
}~ww xY w	 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 }t1          |||¦  «        \  }}|€&d
|fcd
d
d
¦  «         t#          j        |d¬¦  «         S 	 d
d
d
¦  «         n# 1 swxY w Y   t          j                             t3          ¦   «         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        t<          j         z  t<          j!        z  t<          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-)Úprefixz1tirith install failed: cannot create temp dir: %sN)NÚno_spacezchecksums.txtzchecksums.txt.sigzchecksums.txt.pemu9   tirith not found â€” downloading latest release for %s...ú/z/checksums.txtztirith download failed: %sT)Úignore_errors)NÚdownload_failedFr[   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   )NÚcross_device_copy_failedzcosign + SHA-256zSHA-256 onlyztirith installed to %s (%s)r+   )#r3   r4   Údebugr}   rŸ   rx   ry   rz   Ú_REPOÚtempfileÚmkdtemprT   r   rF   rG   r“   r&   r\   Úrmtreer]   rª   r¿   ÚtarfilerQ   rÑ   rn   ÚmoveÚcopyrj   ÚchmodÚstatÚst_modeÚS_IXUSRÚS_IXGRPÚS_IXOTH)rÓ   rÌ   Útargetr¬   Úbase_urlÚtmpdirr©   r«   r”   r•   r–   Úcosign_verifiedÚcosign_resultrÀ   Úsrcr_   r‚   Úverifications                     r   Ú_install_tirithró   ‚  s¡  € ð )Ð
:�&Œ.ˆ.­f¬l€CåÑÔ€FØð ,Ý�ŠÐEÝ”_Ñ&Ô&­Ô(8Ñ(:Ô(:ñ	<ô 	<ð 	<à+Ð+à,˜VÐ,Ð,Ð,€LØE¥UÐEÐEÐE€Hð ÝÔ!Ð):Ð;Ñ;Ô;ˆˆøÝð  ð  ð  ØˆÐ?ÀÑEÔEÐEØÐÐÐÐÐøøøøð øøøðK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Ø*Ð*Ð*Ð*õ| 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1øøøøðA	+øøøð  ˆÝŒ<˜Ñ!Ô!ð 	OðYÝ (Ð>Ð>Ð>ÀÑIÔIÐIÝ (Ð>Ð>Ð>À	ÑJÔJÐJõ !/¨~¸xÈÑ SÔ S�Ø  DÐ(Ð(Ø&*�O�OØ" eÐ+Ð+ð �CÐWÑXÔXÐXØ=õP 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1õI —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ð 	+Ø*õ< 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1Ð1õ9 Œ\˜,¨Ñ/Ô/ð 	$°3Ý0°°f¸cÑBÔB‰KˆC�Øˆ{Ø˜V�|ð	$ð 	$ð 	$ð 	$ñ 	$ô 	$ð 	$õ8 	Œ�f¨DÐ1Ñ1Ô1Ð1Ð1ð5 ð	$ð 	$ð 	$ñ 	$ô 	$ð 	$ð 	$ð 	$ð 	$ð 	$ð 	$øøøð 	$ð 	$ð 	$ð 	$õ
 Œ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 Â
B4ÂB/Â/B4Â8BQ6 Å(E= Å<Q6 Å=
F4ÆF/ÆQ6 Æ/F4Æ4Q6 Ç&I Ç4'Q6 È4Q6 É
I>ÉI9É4Q6 É9I>É>.Q6 ËQ6 ËL$Ë4Q6 ÌQ6 Ì$L(Ì(Q6 Ì+L(Ì,/Q6 ÍM2 Í1Q6 Í2
O$Í=NÎO$Î
OÎN3Î2OÎ3
O Î=OÎ?O Ï OÏO$ÏQ6 ÏOÏO$Ï!Q6 Ï#O$Ï$A;Q6 Ñ6RÚconfigured_pathc                 ó   — | dk    S )zHReturn True if the user explicitly configured a non-default tirith path.r   r5   )rô   s    r   Ú_is_explicit_pathrö   è  s   € à˜hÒ&Ð&r   c                 óÈ  — t           �t           t          urt           S t          j                             | ¦  «        }t          | ¦  «        }t           t          u }|st          ¦   «         st          a da|S |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.
    NrÕ   z6Configured tirith path %r not found; scanning disabledÚexplicit_path_missingr   r+   rZ   r[   F)r*   Ú_INSTALL_FAILEDr   rF   Ú
expanduserrö   r   r,   rÉ   ÚaccessÚX_OKr\   r]   r3   r4   r^   rG   rn   r7   Úis_aliverX   r`   ró   rh   )	rô   ÚexpandedÚexplicitÚinstall_failedÚfoundÚ
hermes_binÚdisk_reasonÚ	installedr_   s	            r   Ú_resolve_tirith_pathr  í  sv  € õ$ Ð!¥n½OÐ&KÐ&KÝÐåŒw×!Ò! /Ñ2Ô2€HÝ  Ñ1Ô1€HÝ#¥Ð6€Nð ð Õ1Ñ3Ô3ð Ý(ˆØ"8ÐØˆð ð ÝŒ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*   r\   r]   r,   r   rF   rG   rn   rÉ   rû   rü   ró   r^   rù   rh   )rÓ   r  r  r  r_   s        r   Ú_background_installr  Y  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                 ó6  — t          ¦   «         }|d         sdS t          �Wt          t          urIt          }t          j                             |¦  «        r!t          j        |t          j        ¦  «        r|S dS t          ¦   «         st          ada	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   r+   rZ   r[   rÓ   T)rì   ÚkwargsÚdaemon)r)   r*   rù   r   rF   rÉ   rû   rü   r   r,   rö   rú   r\   r]   r^   rG   rn   rX   r`   r7   rý   Ú	threadingÚThreadr  Ústart)	rÓ   r(   rF   rô   rÿ   rþ   r  r  r  s	            r   Úensure_installedr  y  s�  € õ  Ñ
!Ô
!€CØÐÔ ð Øˆtõ Ð!¥n½OÐ&KÐ&KÝˆÝŒ7�>Š>˜$ÑÔð 	¥B¤I¨dµB´GÑ$<Ô$<ð 	ØˆKØˆtõ
 !Ñ"Ô"ð Ý(ˆØ"8ÐØˆ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           
      ó€  — t          ¦   «         }|d         sdg ddœS t          rdg ddœS t          ¦   «         sdg ddœS t          |d         ¦  «        }|d         }|d         }|€t	          d
d¦  «         |rdg ddœS dg ddœS 	 t          j        |dddddd| gdddd|t
          j        ¬¦  «        }n¾# t          $ rk}dt          |¦  «        j
        › dt          |dd¦  «        › �}t	          |d|¦  «         t          ¦   «          |rdg d|› �dœcY d	}~S dg d|› �dœcY d	}~S d	}~wt
          j        $ r9 t	          d|› �d |¦  «         t          ¦   «          |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}	d$anQ|d%k    rd}	nH|d&k    rd'}	n?t                                d(|¦  «         t          ¦   «          |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	t0          …         }nG# t(          j        t4          f$ r. t                                d.¦  «         |	dk    rd/}n|	d'k    rd0}Y nw xY w|	d'k    r|
rd1„ |
D ¦   «         }|sd}	g }
d}|	|
|dœS )2a@  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Úsummaryz!tirith disabled (circuit breaker)r   r   r   NÚtirith_path_nonez/tirith path resolved to None; scanning disabledztirith path unavailableÚblockz%tirith path unavailable (fail-closed)Úcheckz--jsonz--non-interactivez--shellÚposixz--TrK   r˜   rš   ztirith_spawn_failed:ú:Úerrnoztirith spawn failed: %sztirith unavailable: z#tirith spawn failed (fail-closed): ztirith_timeout:ztirith timed out after %dsztirith timed out (zs)ztirith timed out (fail-closed)r   r1   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)c                 ó0   — g | ]}t          |¦  «        °|‘ŒS r5   )Ú_is_app_tld_finding)Ú.0rW   s     r   ú
<listcomp>z*check_command_security.<locals>.<listcomp>Q  s&   € ÐNÐNÐN !Õ7JÈ1Ñ7MÔ7MÐN˜AÐNÐNÐNr   )r)   r/   r   r  r>   r    r¡   r¤   rT   ÚtypeÚ__name__Úgetattrr6   r§   r¥   r.   r3   r4   ÚstdoutrS   ÚjsonÚloadsr%   Ú_MAX_FINDINGSÚ_MAX_SUMMARY_LENÚJSONDecodeErrorÚAttributeErrorrÞ   )r  r(   r   rƒ   Ú	fail_openr¨   r©   Ú	spawn_keyÚ	exit_coder  r  r  ÚdataÚraw_findingsÚnon_suppressibles                  r   Úcheck_command_securityr2  Û  sR  € õ  Ñ
!Ô
!€CàÐÔ ð BØ!¨r¸bÐAÐAÐAõ ð cØ!¨rÐ>aÐbÐbÐbõ
 !Ñ"Ô"ð BØ!¨r¸bÐAÐAÐAå& s¨=Ô'9Ñ:Ô:€KØÐ"Ô#€GØÐ&Ô'€IàÐÝØØ=ñ	
ô 	
ð 	
ð ð 	]Ø%°2ÐB[Ð\Ð\Ð\Ø!¨rÐ>eÐfÐfÐfð`Ý”Ø˜' 8Ð-@Ø˜  wð0àØ °	ØÝÔ$ð
ñ 
ô 
ˆˆøõ ð kð kð kð \­4°©9¬9Ô+=Ð[Ð[ÅÈÈWÐVXÑ@YÔ@YÐ[Ð[ˆ	Ý�9Ð7¸Ñ=Ô=Ð=ÝÑÔÐØð 	`Ø%°2ÐB^ÐY\ÐB^ÐB^Ð_Ð_Ð_Ð_Ð_Ð_Ð_Ð_Ø!¨rÐ>iÐdgÐ>iÐ>iÐjÐjÐjÐjÐjÐjÐjÐjøøøøÝÔ$ð 	`ð 	`ð 	`ÝØ'˜gÐ'Ð'Ø(Øñ	
ô 	
ð 	
õ
 	ÑÔÐØð 	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øøøð �ÒÐ˜HÐØNÐN xÐNÑNÔNÐØð 	ØˆFØˆHØˆGà¨(¸wÐGÐGÐGsL   Â-B2 Â2
E-Â<AD"ÄE-ÄD"ÄE-Ä"A E-Å$E-Å,E-ÇA>I ÉAJÊJÚfindingc                 óî   — t          | t          ¦  «        sdS |                      d¦  «        dk    rdS dD ]?}|                      |¦  «        }|�&dt          |¦  «                             ¦   «         v r dS Œ@dS )zÈReturn True if this finding is a lookalike_tld warning for the .app TLD only.

    Checks the rule_id and inspects common value/detail field names that
    Tirith may use to carry the TLD string.
    FÚrule_idÚlookalike_tld)ÚvalueÚtldÚdetailÚdescriptionr9   Nz.appT)Ú
isinstanceÚdictr%   rC   r   )r3  Úfieldr   s      r   r  r  Z  sƒ   € õ �g�tÑ$Ô$ð ØˆuØ‡{‚{�9ÑÔ Ò0Ð0ØˆuØEð ð ˆØ�kŠk˜%Ñ Ô ˆØˆ?˜v­¨S©¬¯ªÑ)9Ô)9Ð9Ð9Ø�4�4øØˆ5r   )r   N)r+   )r€   )HÚ__doc__r´   r&  Úloggingr   rx   r\   rç   r    rã   rà   r  rO   Úurllib.requestr‰   Úhermes_constantsr   Ú	getLoggerr#  r3   rß   r¢   r£   rC   Úboolr   r   r   r<  r)   r*   Ú__annotations__rù   r,   r2   r.   r/   r6   ÚLockr  r7   r  Úsetr8   r;   r>   rA   rP   rD   rH   rX   r`   rh   r^   rn   r}   r   r“   rª   r¿   ÚTarFileÚtuplerÑ   ró   rö   r  r  r  r(  r)  r2  r  r5   r   r   ú<module>rI     sg  ððð ð ð, €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ø €€€Ø €€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð à ,Ð ,Ð ,Ð ,Ð ,Ð ,à	ˆÔ	˜8Ñ	$Ô	$€à€ð g°ÐfÐfÐfÐ Ø>€ð/�3ð / ð /¨$ð /ð /ð /ð /ð�#ð  ð ¨ð ð ð ð ð˜tð ð ð ð ð8 %)€��d‘
˜TÑ!Ð (Ð (Ñ (Ø€Ø!Ð ˜Ð !Ð !Ñ !ð €Ø€ˆcÐ Ð Ñ Ø€ˆtÐ Ð Ñ ð

ð 

ð 

ð 

ð �	”Ñ Ô €Ø+/€�Ô! DÑ(Ð /Ð /Ñ /ð !˜S™UœUÐ �#�c”(Ð "Ð "Ñ "ØˆyŒ~ÑÔ€ð#�Cð # #ð #°ð #ð #ð #ð #ð!ð !ð !ð !ð €ð"˜#ð "ð "ð "ð "ð
F˜cð Fð Fð Fð Fð
˜c D™jð ð ð ð ð" Dð ð ð ð ð"ð  ð ð ð ð ð"	ð 	ð 	ð˜ð ð ð ð ð˜˜d™
ð ð ð ð ð8(˜tð (ð (ð (ð (ð$ð $˜ð $ 3ð $°ð $ð $ð $ð $ð& 3ð &°#ð &À#ð &È$ÐQUÉ+ð &ð &ð &ð &ðR 3ð ¸ð È3ð ÐSWð ð ð ð ð2) ¤ð )¸3ð )ÈÈcÐTXÉjÐZ]ÈoÔH^ð )ð )ð )ð )ð4 -1ð c2ð c2ð c2 Tð c2°U¸3À¹:Às¸?Ô5Kð c2ð c2ð c2ð c2ðL' sð '¨tð 'ð 'ð 'ð 'ð
i¨#ð i°#ð ið ið ið iðX 15ð )ð )ð )¨ð )ð )ð )ð )ð@ .2ð Wð Wð W dð Wð Wð Wð Wð| €ØÐ ð|H Cð |H¨Dð |Hð |Hð |Hð |Hð~ ð ¨$ð ð ð ð ð ð r   