Ë
    ãß²jÌ%  ã                   ó6  — 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mZ	 ddl
mZmZmZ  ej                  d«      Zdededz  fd„Zd	edefd
„Zdededz  fd„ZdZddededefd„Zdedefd„Z ed«      Z ej4                  d«      Zdedefd„Zdedefd„Zy)a–  Pre-gate floors for :mod:`tools.approval`: decisions that never reach a prompt.

Unconditional blocks (hardline, ``sudo -S`` password piping, the user's own
``approvals.deny`` globs) and the permanent command allowlist match. All of
them run BEFORE yolo / ``approvals.mode: off`` / cron approve-mode; the
allowlist runs after. Session state stays in ``tools.approval`` and is read
through it at call time.
é    N)Úapproval_context)Ú_MALFORMED_EXEC_DESCRIPTIONÚ_PARSER_LIMIT_DESCRIPTIONÚ_deny_command_variantsztools.approvalÚcommandÚreturnc                 ó¾  — 	 t        j                  «       j                  d«      xs g }|D �cg c]4  }t	        |t
        «      sŒ|j                  «       sŒ%|j                  «       ‘Œ6 }}|syt        | «      D ]Q  }|j                  «       j                  «       }|D ],  }t        j                  ||j                  «       «      sŒ(|c c S  ŒS y# t        $ r Y yw xY wc c}w )u­  Return the matching ``approvals.deny`` glob, or None. User-defined fnmatch
    globs that block unconditionally â€” like the hardline floor, a match fires
    BEFORE the yolo / mode=off bypass ("never let the agent run this, even under
    yolo"). Case-insensitive, run over the same normalized/deobfuscated variants
    the dangerous-pattern detector uses so quoting tricks (``r\m``,
    ``git st""atus``) can't sidestep a rule.ÚdenyN)Ú_ctxÚ_get_approval_configÚgetÚ	ExceptionÚ
isinstanceÚstrÚstripr   ÚlowerÚfnmatchÚfnmatchcase)r   Údeny_patternsÚpÚglobsÚcommand_variantÚ	candidateÚpatterns          ú:/home/ubuntu/.hermes/hermes-agent/tools/approval_floors.pyÚ_match_user_deny_ruler      sÇ   € ðÜ×1Ñ1Ó3×7Ñ7¸Ó?ÒEÀ2ˆð !.ÖR˜1´¸A¼sÕ1CÈÏÉÍ	ˆQ�W‰W�YÐR€EÐRÙØÜ1°'Ó:ò ˆØ#×)Ñ)Ó+×1Ñ1Ó3ˆ	Øò 	ˆGÜ×"Ñ" 9¨g¯m©m«oÕ>Ø”ñ	ðð
 øô ò ÙðüâRs"   ‚'C ­CÁCÁCÃ	CÃCr   c                 ó   — ddd| › d�dœS )z@Build the standard block result for an ``approvals.deny`` match.FTz:BLOCKED: this command matches the user-defined deny rule 'uË   ' (approvals.deny in config.yaml). It cannot be executed via the agent â€” not even with --yolo, /yolo, or approvals.mode=off. Do NOT retry or rephrase this command; the user has explicitly forbidden it.)ÚapprovedÚ	user_denyÚmessage© )r   s    r   Ú_user_deny_block_resultr"   -   s)   € à¨DðØˆ9ð kð	kñmð mó    c                 ó°  — 	 ddl m}  |«       dz  dz  }|j                  dd¬«       t        j                  «       dz
  }|j	                  d«      D ]Q  }t        j                  t        «      5  |j                  «       j                  |k  r|j                  «        d	d	d	«       ŒS |d
t        t        j                  «       «      › dt        j                  «       j                  d	d › d�z  }|j                  d|› d�| z   | j!                  d«      rdndz   dd¬«       t#        |«      S # 1 sw Y   ŒßxY w# t$        $ r t&        j)                  dd¬«       Y y	w xY w)u÷  Persist a parser-limit-blocked command as a runnable script. That block
    fires on payload SIZE/shape, not the operation â€” usually a legitimate script
    the model inlined. Saving it makes recovery one turn (`bash <file>`) instead
    of two, and is strictly safer than the hint-only path: the file goes through
    the normal execution pipeline (including the referenced-script content guard)
    and nothing runs here. Returns the path, or None on any failure (hint falls
    back to write_file).r   )Úget_hermes_homeÚcachezblocked-scriptsT)ÚparentsÚexist_oki€:	 zblocked-*.shNzblocked-Ú-é   z.shz¦#!/bin/bash
# Auto-saved by Hermes: this command exceeded the inline command
# parser limit and was blocked from direct execution. Review it,
# then run it via: bash Ú
Ú zutf-8Úreplace)ÚencodingÚerrorszfailed to save blocked payload)Úexc_info)Úhermes_constantsr%   ÚmkdirÚtimeÚglobÚ
contextlibÚsuppressÚOSErrorÚstatÚst_mtimeÚunlinkÚintÚuuidÚuuid4ÚhexÚ
write_textÚendswithr   r   ÚloggerÚdebug)r   r%   Ú
script_dirÚcutoffÚoldÚpaths         r   Ú_save_blocked_payloadrG   6   s[  € ðÝ4Ù$Ó&¨Ñ0Ð3DÑDˆ
Ø×Ñ °ÐÔ5ä—‘“˜yÑ(ˆØ—?‘? >Ó2ò 	!ˆCÜ×$Ñ$¤WÓ-ñ !Ø—8‘8“:×&Ñ&¨Ò/Ø—J‘J”L÷!ð !ð	!ð ˜h¤s¬4¯9©9«;Ó'7Ð&8¸¼$¿*¹*»,×:JÑ:JÈ2ÈAÐ:NÐ9OÈsÐSÑSˆØ�‰ð'ð (, f¨Bð0ð 3:ñ:ð DK×CSÑCSÐTXÔCY¹RÐ_cñeð  Yð 	ô 	
ô �4‹yÐ÷#!ð !ûô$ ò Ü�‰Ð5ÀˆÔEÙðús+   ‚A*D2 Á,.D&ÂBD2 Ä&D/	Ä+D2 Ä2 EÅEzˆ RECOVERY: this block fires on oversized/unparseable inline command payloads (heredocs, giant one-liners), not on the operation itself. Údescriptionc                 ó˜   — d| › d�}| t         t        fv r1|rt        |«      nd}|r|t        d|› d|› d�z   z  }n|t        dz   z  }dd	|d
œS )z5Build the standard block result for a hardline match.zBLOCKED (hardline): uò   . This command is on the unconditional blocklist and cannot be executed via the agent â€” not even with --yolo, /yolo, approvals.mode=off, or cron approve mode. If you genuinely need to run it, run it yourself in a terminal outside the agent.NzYour command was saved to u1    â€” review it, then run: terminal(command="bash z"). Do not retry inline.z”Write the script to a file with write_file, then run it: terminal(command="bash /path/script.sh") or "python3 /path/script.py". Do not retry inline.FT)r   Úhardliner    )r   r   rG   Ú_RECOVERY_PREFIX)rH   r   r    Úsaveds       r   Ú_hardline_block_resultrM   b   s•   € ð ˜{˜mð ,Kð 	Kð ð Ô0Ô2MÐNÑNÙ29Ô% gÔ.¸tˆÙØÔ'Ø,¨U¨GÐ3eÐfkÐelð m'ð 'ññ ‰Gð
 Ô'ðDññ ˆGð
 ¨4¸GÑDÐDr#   c                 ó   — dd| › d�dœS )z5Build the standard block result for sudo stdin guard.Fz	BLOCKED: uÎ   . Do not pipe passwords to 'sudo -S' â€” this is a brute-force attack vector. Set SUDO_PASSWORD in your .env file if the agent needs passwordless sudo, or run the sudo command manually in your own terminal.)r   r    r!   )rH   s    r   Ú_sudo_stdin_block_resultrO   }   s$   € àØ
�K�=ð !`ð 	`ñbð br#   z
;&|<>`$()z:(?:^|[ \t])(?:-[^-\s]*[ce]|--(?:command|eval))(?:[= \t]|$)c                 ó”  — | xs d} d}d}d}t        | «      }||k  rˆ| |   }|dk(  r'|dk7  r"|dz   |k  r| |dz      nd}|t        v rd}|d	z  }Œ6|�||k(  rd}nB|d
k(  r|dv ry|t        v r0d}n-|dv r|}n&|dk(  r|dz   |k  r| |dz      dk(  ry|t        v r|dvry|dz  }||k  rŒˆ|�y|xr t        t        j	                  | «      «      S )aò  Return True when a command is too compound for the allowlist shortcut.
    Quote-aware: metacharacters inside quotes or behind a backslash are literal
    arguments (``cargo bench -- '^a(b|c)$'``), not shell syntax. Still
    disqualifying: ``$`` or backtick inside DOUBLE quotes (expansion stays
    active), and any quoted/escaped control character when the command also
    carries a ``-c``/``-e``/``--command``/``--eval``-style option that would
    hand the quoted text to another interpreter.r,   NFr   ú\ú'é   Té   ú")ú`ú$)rR   rU   rW   ú(z())ÚlenÚ_SHELL_CONTROL_CHARSÚboolÚ_REINTERPRETED_ARGUMENT_REÚsearch)r   ÚquoteÚhas_reinterpretableÚiÚnÚchÚnxts          r   Ú_has_allowlist_shell_operatorrd   �   s'  € ð Šm˜€GØ€EØÐØ	€AÜˆG‹€AØ
ˆaŠ%Ø�Q‰ZˆØ�Š:˜% 3š,Ø$%¨¡E¨A¢I�'˜!˜a™%’.°2ˆCØÔ*Ñ*Ø&*Ð#Ø�‰FˆAØØÐØ�UŠ{Ø‘Ø˜#’ "¨
Ñ"2ØØÔ+Ñ+Ø&*Ñ#Ø�:ÑØ‰EØ�3ŠYð �1‰u�qŠy˜W Q¨¡U™^¨sÒ2ØØÔ'Ñ'¨B°d©NØØ	ˆQ‰ˆð1 ˆa‹%ð4 ÐØØÒS¤4Ô(B×(IÑ(IÈ'Ó(RÓ#SÐSr#   c                 ó†  ‡— ddl m} | xs dj                  «       } | rt        | «      ry|j                  5  t        |j                  «       «      }ddd«       D ]Y  Št        ‰t        «      r‰j                  «       ndŠ‰sŒ(| ‰k(  s,t        ˆfd„dD «       «      sŒBt        j                  | ‰«      sŒY y y# 1 sw Y   ŒhxY w)	a  True when command_allowlist holds this exact command text or a matching
    glob. Permanent approvals historically store dangerous-pattern keys such as
    ``recursive delete``; manual entries are command text, possibly with
    shell-style wildcards like ``podman *``.r   )Úapprovalr,   FNc              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­w)Nr!   )Ú.0rb   r   s     €r   ú	<genexpr>z7_command_matches_permanent_allowlist.<locals>.<genexpr>É   s   øè ø€ Ò2QÀR°2¸´=Ñ2Qùs   ƒz*?[T)Útoolsrf   r   rd   Ú_lockÚtupleÚ_permanent_setr   r   Úanyr   r   )r   Ú_aÚpatternsr   s      @r   Ú$_command_matches_permanent_allowlistrq   ¼   s¬   ø€ õ
 %ØŠ}˜"×#Ñ#Ó%€GÙÔ3°GÔ<ØØ	�‰ñ .Ü˜×*Ñ*Ó,Ó-ˆ÷.àò ˆÜ%/°¼Ô%=�'—-‘-”/À2ˆÚ˜ 7Ò*¬sÓ2QÈ5Ô2QÕ/QÜ3:×3FÑ3FÀwÐPWÕ3XÙð	ð
 ÷.ð .ús   ¶B7Â7C )r,   )Ú__doc__r5   r   ÚloggingÚrer3   r<   rj   r   r   Útools.approval_detectionr   r   r   Ú	getLoggerrA   r   r   Údictr"   rG   rK   rM   rO   Ú	frozensetrZ   Úcompiler\   r[   rd   rq   r!   r#   r   ú<module>rz      s  ðñó Û Û Û 	Û Û Ý *÷Tñ Tð 
ˆ×	Ñ	Ð+Ó	,€ð 3ð ¨3°©:ó ð,m Sð m¨Tó mð# 3ð #¨3°©:ó #ðNSð ñE¨ð E°cð EÀ4ó Eð6b¨#ð b°$ó bñ ! Ó1Ð à'˜RŸZ™ZÐ(eÓfÐ ð)T¨3ð )T°4ó )TðX°#ð ¸$ô r#   