
    i:                        d Z ddlZddlZddlZ ej        e          Z eh d          Z eh d          Z	 ej
        dd                                          dv Zg dZd	Z ej        d
e d          ZdZ ej        de dej                  Z ej        dej                  Z ej        d          Z ej        d          Z ej        dej                  Z ej        d          Z ej        d          Z ej        d          Z ej        d          Z ej        d          Z ej        d          Z ej        dd                    e          z   dz             Zddddddd e d!e!d"e!d#e!d$e d%e d&e fd'Z"d(e d&e fd)Z#d*e d&e fd+Z$d,e d&e fd-Z%d,e d&e fd.Z&d,e d&e fd/Z'd0d1d,e d2e(d&e fd3Z) G d4 d5ej*                  Z+dS )6a2  Regex-based secret redaction for logs and tool output.

Applies pattern matching to mask API keys, tokens, and credentials
before they reach log files, verbose output, or gateway logs.

Short tokens (< 18 chars) are fully masked. Longer tokens preserve
the first 6 and last 4 characters for debuggability.
    N>   x-amz-signaturejwtkeyauthcodetokenapikeysecretapi_keysessionid_tokenpassword	signatureaccess_tokenclient_secretrefresh_token>   r   r   r   r   r	   r
   r   r   r   private_keyr   authorizationr   r   HERMES_REDACT_SECRETS )1trueyeson)#zsk-[A-Za-z0-9_-]{10,}zghp_[A-Za-z0-9]{10,}zgithub_pat_[A-Za-z0-9_]{10,}zgho_[A-Za-z0-9]{10,}zghu_[A-Za-z0-9]{10,}zghs_[A-Za-z0-9]{10,}zghr_[A-Za-z0-9]{10,}zxox[baprs]-[A-Za-z0-9-]{10,}zAIza[A-Za-z0-9_-]{30,}zpplx-[A-Za-z0-9]{10,}zfal_[A-Za-z0-9_-]{10,}zfc-[A-Za-z0-9]{10,}zbb_live_[A-Za-z0-9_-]{10,}zgAAAA[A-Za-z0-9_=-]{20,}zAKIA[A-Z0-9]{16}zsk_live_[A-Za-z0-9]{10,}zsk_test_[A-Za-z0-9]{10,}zrk_live_[A-Za-z0-9]{10,}zSG\.[A-Za-z0-9_-]{10,}zhf_[A-Za-z0-9]{10,}zr8_[A-Za-z0-9]{10,}znpm_[A-Za-z0-9]{10,}zpypi-[A-Za-z0-9_-]{10,}zdop_v1_[A-Za-z0-9]{10,}zdoo_v1_[A-Za-z0-9]{10,}zam_[A-Za-z0-9_-]{10,}zsk_[A-Za-z0-9_]{10,}ztvly-[A-Za-z0-9]{10,}zexa_[A-Za-z0-9]{10,}zgsk_[A-Za-z0-9]{10,}zsyt_[A-Za-z0-9]{10,}zretaindb_[A-Za-z0-9]{10,}zhsk-[A-Za-z0-9]{10,}zmem0_[A-Za-z0-9]{10,}zbrv_[A-Za-z0-9]{10,}z9(?:API_?KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)z([A-Z0-9_]{0,50}z&[A-Z0-9_]{0,50})\s*=\s*(['\"]?)(\S+)\2z(?:api_?[Kk]ey|token|secret|password|access_token|refresh_token|auth_token|bearer|secret_value|raw_secret|secret_input|key_material)z("z")\s*:\s*"([^"]+)"z!(Authorization:\s*Bearer\s+)(\S+)z#(bot)?(\d{8,}):([-A-Za-z0-9_]{30,})zH-----BEGIN[A-Z ]*PRIVATE KEY-----[\s\S]*?-----END[A-Z ]*PRIVATE KEY-----zK((?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis|amqp)://[^:]+:)([^@]+)(@)z2eyJ[A-Za-z0-9_-]{10,}(?:\.[A-Za-z0-9_=-]{4,}){0,2}z<@!?(\d{17,20})>z (\+[1-9]\d{6,14})(?![A-Za-z0-9])z;(https?|wss?|ftp)://([^\s/?#]+)([^\s?#]*)\?([^\s#]+)(#\S*)?z+(https?|wss?|ftp)://([^/\s:@]+):([^/\s@]+)@zH^[A-Za-z_][A-Za-z0-9_.-]*=[^&\s]*(?:&[A-Za-z_][A-Za-z0-9_.-]*=[^&\s]*)+$z(?<![A-Za-z0-9_-])(|z)(?![A-Za-z0-9_-])      ***)headtailfloorplaceholderemptyvaluer   r    r!   r"   r#   returnc                d    | s|S t          |           |k     r|S | d|          d| | d          S )u  Mask a secret for display, preserving ``head`` and ``tail`` characters.

    Canonical helper for display-time redaction across Hermes — used by
    ``hermes config``, ``hermes status``, ``hermes dump``, and anywhere
    a secret needs to be shown truncated for debuggability while still
    keeping the bulk hidden.

    Args:
        value:       The secret to mask. ``None``/empty returns ``empty``.
        head:        Leading characters to preserve. Default 4.
        tail:        Trailing characters to preserve. Default 4.
        floor:       Values shorter than ``head + tail + floor_margin`` are
                     fully masked (returns ``placeholder``). Default 12 —
                     matches the existing config/status/dump convention.
        placeholder: Value returned for too-short inputs. Default ``"***"``.
        empty:       Value returned when ``value`` is falsy (None, ""). The
                     caller can override this to e.g. ``color("(not set)",
                     Colors.DIM)`` for user-facing display.

    Examples:
        >>> mask_secret("sk-proj-abcdef1234567890")
        'sk-p...7890'
        >>> mask_secret("short")                         # fully masked
        '***'
        >>> mask_secret("")                              # empty default
        ''
        >>> mask_secret("", empty="(not set)")           # empty override
        '(not set)'
        >>> mask_secret("long-token", head=6, tail=4, floor=18)
        '***'
    Nz...)len)r$   r   r    r!   r"   r#   s         1/home/ubuntu/.hermes/hermes-agent/agent/redact.pymask_secretr)      sO    P  
5zzEETEl..udUVV}...    r   c                 0    | sdS t          | ddd          S )uO   Mask a log token — conservative 18-char floor, preserves 6 prefix / 4 suffix.r      r      )r   r    r!   )r)   )r   s    r(   _mask_tokenr.      s(      uu11B7777r*   queryc                 `   | s| S g }|                      d          D ]~}d|vr|                    |           |                    d          \  }}}|                                t          v r|                    | d           i|                    |           d                    |          S )a   Redact sensitive parameter values in a URL query string.

    Handles `k=v&k=v` format. Sensitive keys (case-insensitive) have values
    replaced with `***`. Non-sensitive keys pass through unchanged.
    Empty or malformed pairs are preserved as-is.
    &=z=***)splitappend	partitionlower_SENSITIVE_QUERY_PARAMSjoin)r/   partspairr   _r$   s         r(   _redact_query_stringr<      s      EC    d??LLs++Q99;;111LLC&&&&LL88E??r*   textc                 f    dt           j        dt          fd}t                              ||           S )u   Scan text for URLs with query strings and redact sensitive params.

    Catches opaque tokens that don't match vendor prefix regexes, e.g.
    `https://example.com/cb?code=ABC123&state=xyz` → `...?code=***&state=xyz`.
    mr%   c                    |                      d          }|                      d          }|                      d          }t          |                      d                    }|                      d          pd}| d| | d| | S )	N         r      r   ://?)groupr<   )r?   scheme	authoritypathr/   fragments         r(   _subz&_redact_url_query_params.<locals>._sub  s    GGAJJ	wwqzz$QWWQZZ00771::#@@Y@@@u@h@@@r*   )reMatchstr_URL_WITH_QUERY_REsub)r=   rL   s     r(   _redact_url_query_paramsrR     sD    A AS A A A A !!$---r*   c                 :    t                               d |           S )zStrip `user:password@` from HTTP/WS/FTP URLs.

    DB protocols (postgres, mysql, mongodb, redis, amqp) are handled
    separately by `_DB_CONNSTR_RE`.
    c                 ^    |                      d           d|                      d           dS )NrA   rE   rB   z:***@rG   r?   s    r(   <lambda>z&_redact_url_userinfo.<locals>.<lambda>  s+    QWWQZZ55AGGAJJ555 r*   )_URL_USERINFO_RErQ   r=   s    r(   _redact_url_userinforZ     s%     55  r*   c                     | rd| v sd| vr| S t                               |                                           s| S t          |                                           S )uN  Redact sensitive values in a form-urlencoded body.

    Only applies when the entire input looks like a pure form body
    (k=v&k=v with no newlines, no other text). Single-line non-form
    text passes through unchanged. This is a conservative pass — the
    `_redact_url_query_params` function handles embedded query strings.
    
r1   )_FORM_BODY_REmatchstripr<   rY   s    r(   _redact_form_bodyr`   $  sZ      44<<3d??tzz||,, 

---r*   F)forcera   c                   | dS t          | t                    st          |           } | s| S |s	t          s| S t                              d |           } d }t
                              ||           } d }t                              ||           } t                              d |           } d }t                              ||           } t                              d|           } t                              d |           } t                              d	 |           } t          |           } t          |           } t          |           } t                              d
 |           } d }t                               ||           } | S )ug  Apply all redaction patterns to a block of text.

    Safe to call on any string -- non-matching text passes through unchanged.
    Disabled by default — enable via security.redact_secrets: true in config.yaml.
    Set force=True for safety boundaries that must never return raw secrets
    regardless of the user's global logging redaction preference.
    Nc                 F    t          |                     d                    S )NrA   r.   rG   rV   s    r(   rW   z'redact_sensitive_text.<locals>.<lambda>F  s    K

$;$; r*   c                     |                      d          |                      d          |                      d          }}}| d| t          |           | S )NrA   rB   rC   r2   rG   r.   )r?   namequoter$   s       r(   _redact_envz*redact_sensitive_text.<locals>._redact_envI  sS    WWQZZQWWQZZUe;;;E 2 2;E;;;r*   c                     |                      d          |                      d          }}| dt          |           dS )NrA   rB   z: ""rf   )r?   r   r$   s      r(   _redact_jsonz+redact_sensitive_text.<locals>._redact_jsonO  s>    WWQZZU//+e,,////r*   c                 r    |                      d          t          |                      d                    z   S )NrA   rB   rf   rV   s    r(   rW   z'redact_sensitive_text.<locals>.<lambda>V  s'    !''!**{1771::666 r*   c                 h    |                      d          pd}|                      d          }| | dS )NrA   r   rB   z:***rU   )r?   prefixdigitss      r(   _redact_telegramz/redact_sensitive_text.<locals>._redact_telegram[  s9    !r&&&&&&r*   z[REDACTED PRIVATE KEY]c                 \    |                      d           d|                      d           S )NrA   r   rC   rU   rV   s    r(   rW   z'redact_sensitive_text.<locals>.<lambda>e  s(    1771::(F(F!''!**(F(F r*   c                 F    t          |                     d                    S )Nr   rd   rV   s    r(   rW   z'redact_sensitive_text.<locals>.<lambda>h  s    QWWQZZ!8!8 r*   c                 @    dd|                      d          v rdnd dS )Nz<@!r   r   z***>rU   rV   s    r(   rW   z'redact_sensitive_text.<locals>.<lambda>u  s+    -X

9J9J##PR-X-X-X r*   c                     |                      d          }t          |          dk    r|d d         dz   |dd          z   S |d d         dz   |dd          z   S )NrA      rB   z****r   )rG   r'   )r?   phones     r(   _redact_phonez,redact_sensitive_text.<locals>._redact_phonex  s`    

u::??!9v%bcc
22RaRy6!E"##J..r*   )
isinstancerO   _REDACT_ENABLED
_PREFIX_RErQ   _ENV_ASSIGN_RE_JSON_FIELD_RE_AUTH_HEADER_RE_TELEGRAM_RE_PRIVATE_KEY_RE_DB_CONNSTR_RE_JWT_RErZ   rR   r`   _DISCORD_MENTION_RE_SIGNAL_PHONE_RE)r=   ra   ri   rl   rq   r{   s         r(   redact_sensitive_textr   4  s    |tdC   4yy  _  >>;;TBBD< < < k400D0 0 0 lD11D 66 D' ' ' ,d33D 7>>D FFMMD ;;88$??D  %%D $D))D T""D ""#X#XZ^__D/ / /
 t44DKr*   c                   B     e Zd ZdZd fd	Zdej        def fdZ xZ	S )	RedactingFormatterz9Log formatter that redacts secrets from all log messages.N%c                 @     t                      j        |||fi | d S N)super__init__)selffmtdatefmtstylekwargs	__class__s        r(   r   zRedactingFormatter.__init__  s,    gu7777777r*   recordr%   c                 d    t                                          |          }t          |          S r   )r   formatr   )r   r   originalr   s      r(   r   zRedactingFormatter.format  s&    77>>&))$X...r*   )NNr   )
__name__
__module____qualname____doc__r   logging	LogRecordrO   r   __classcell__)r   s   @r(   r   r     sr        CC8 8 8 8 8 8/W. /3 / / / / / / / / / /r*   r   ),r   r   osrM   	getLoggerr   logger	frozensetr7   _SENSITIVE_BODY_KEYSgetenvr6   r}   _PREFIX_PATTERNS_SECRET_ENV_NAMEScompiler   _JSON_KEY_NAMES
IGNORECASEr   r   r   r   r   r   r   r   rP   rX   r]   r8   r~   rO   intr)   r.   r<   rR   rZ   r`   boolr   	Formatterr    r*   r(   <module>r      s     				 						8	$	$
 $) % % %   , !y " " "   . ")3R88>>@@D^^$ $ $ N Q U+UUU 
 Z-/---M  "*(M  rz* 
 "*O  RM  "*%  !bj!455  2:ABB 
  RZ   2:2   
O 
 RZSXX&6777:OO 
 ,/ ,/ ,/,/ ,/ 	,/
 ,/ ,/ ,/ 	,/ ,/ ,/ ,/^8s 8s 8 8 8 8     ,.3 .3 . . . . 	s 	s 	 	 	 	.C .C . . . .  7< K K K Kt K K K K K\/ / / / /* / / / / /r*   