
    <i*                         d Z ddlZddlmZmZ ddlmZ ddlmZ ddl	m
Z
  G d de          Z G d	 d
e          Z G d de          ZdZdZ G d de          Z G d de          Z G d de          ZdS )a  Sanitization strategies for key and collection names.

This module provides strategies for sanitizing keys and collection names to comply
with backend store requirements. Different stores have different character restrictions
and length limits, so multiple strategies are provided.

The strategies also prevent collision between user-provided keys and sanitized keys
by using reserved prefixes (H_ for hashed keys, S_ for sanitized keys) and validating
that user input doesn't use these prefixes.
    N)ABCabstractmethod)Enum)InvalidKeyError)sanitize_characters_in_stringc                   "    e Zd ZdZdZ	 dZ	 dZdS )HashFragmentModez4Mode for adding hash fragments to sanitized strings.alwaysonly_if_changedneverN)__name__
__module____qualname____doc__ALWAYSONLY_IF_CHANGEDNEVER     /Users/kimhansen/Desktop/03 Workspace/ceo-agents/chl-effectiveness/mcp-servers/whoop/.venv/lib/python3.11/site-packages/key_value/shared/utils/sanitization.pyr	   r	      s+        >>FC'OPE""r   r	   c                   h    e Zd ZdZededefd            Zededdfd            Zdededz  fdZdS )SanitizationStrategya  Base class for key/collection sanitization strategies.

    Sanitization strategies convert user-provided keys and collection names into
    formats that are compatible with backend store requirements. This includes:
    - Replacing invalid characters
    - Truncating to maximum length
    - Adding hash fragments for uniqueness
    - Prefixing to prevent collisions with user keys
    valuereturnc                     dS )zSanitize a key or collection name for storage.

        Args:
            value: The user-provided key or collection name.

        Returns:
            The sanitized value suitable for storage.
        Nr   selfr   s     r   sanitizezSanitizationStrategy.sanitize,         r   Nc                     dS )zValidate that a user-provided value doesn't use reserved patterns.

        Args:
            value: The user-provided key or collection name.

        Raises:
            InvalidKeyError: If the value uses reserved prefixes or patterns.
        Nr   r   s     r   validatezSanitizationStrategy.validate7   r   r   c                     dS )ad  Attempt to reverse sanitization (for debugging/enumeration).

        This is optional and may not be possible for all strategies (e.g., hashing
        is irreversible). The default implementation returns None.

        Args:
            value: The sanitized value.

        Returns:
            The original value if recoverable, None otherwise.
        Nr   r   s     r   try_unsanitizez#SanitizationStrategy.try_unsanitizeB   s	     tr   )	r   r   r   r   r   strr   r!   r#   r   r   r   r   r   !   s          c c    ^ c d    ^C C$J      r   r   c                   H    e Zd ZdZdedefdZdeddfdZdededz  fdZdS )PassthroughStrategyzPass-through strategy that performs no sanitization.

    Use this for stores that have no character or length restrictions (e.g., Redis,
    DynamoDB, MongoDB when using document fields for keys).
    r   r   c                     |S )zReturn the value unchanged.r   r   s     r   r   zPassthroughStrategy.sanitizeX       r   Nc                     dS )z/No validation needed for pass-through strategy.Nr   r   s     r   r!   zPassthroughStrategy.validate\   r   r   c                     |S z:Return the value unchanged since no sanitization occurred.r   r   s     r   r#   z"PassthroughStrategy.try_unsanitize_   r(   r   )r   r   r   r   r$   r   r!   r#   r   r   r   r&   r&   Q   s         c c    >c >d > > > >C C$J      r   r&      @   c                   Z    e Zd ZdZddeddfdZdedefdZdeddfd	Zdededz  fd
Z	dS )AlwaysHashStrategyz!Strategy that always hashes keys.r-   hash_lengthr   Nc                     |t           k    s|t          k    r$dt            dt           d| }t          |          || _        dS )zInitialize the always hash strategy.

        Args:
            hash_length: The length of the hash to generate. Must be greater than 8 and less than 64.
        z!hash_length must be greater than z and less than z: N)MINIMUM_HASH_LENGTHMAXIMUM_HASH_LENGTH
ValueErrorr0   )r   r0   msgs      r   __init__zAlwaysHashStrategy.__init__k   sU     ---?R1R1R~6I~~Zm~~q|~~CS//! +r   r   c                     t          j        |                                                                          d| j                 S )zHash the value.N)hashlibsha256encode	hexdigestr0   r   s     r   r   zAlwaysHashStrategy.sanitizew   s4    ~ellnn--7799:LD<L:LMMr   c                     dS )z.No validation needed for always hash strategy.Nr   r   s     r   r!   zAlwaysHashStrategy.validate{   r   r   c                     |S r+   r   r   s     r   r#   z!AlwaysHashStrategy.try_unsanitize~   r(   r   )r-   )
r   r   r   r   intr6   r$   r   r!   r#   r   r   r   r/   r/   h   s        ++
, 
,C 
, 
, 
, 
, 
,Nc Nc N N N N=c =d = = = =C C$J      r   r/   c                   D    e Zd ZdZd
deddfdZdedefdZdeddfd	ZdS )HashExcessLengthStrategya  Strategy that hashes keys exceeding a maximum length.

    This strategy is used by stores like Memcached that accept any characters but
    have strict length limits. Keys exceeding the limit are hashed using SHA256
    and prefixed with 'H_' to prevent collisions with user-provided keys.

    Args:
        max_length: Maximum key length before hashing is applied. Defaults to 240.
       
max_lengthr   Nc                     || _         dS )z~Initialize the hashing strategy.

        Args:
            max_length: Maximum key length before hashing is applied.
        N)rB   )r   rB   s     r   r6   z!HashExcessLengthStrategy.__init__   s     %r   r   c                     t          |          | j        k    rMt          j        |                                                                          }d|d| j        dz
            S |S )a  Hash the value if it exceeds max_length, otherwise return unchanged.

        Keys exceeding max_length are hashed using SHA256 and prefixed with 'H_'.
        The hash is truncated to (max_length - 2) characters to fit within the limit.

        Args:
            value: The key to sanitize.

        Returns:
            The original value if within limit, or 'H_' + hash if too long.
        H_N   )lenrB   r8   r9   r:   r;   )r   r   sha256_hashs      r   r   z!HashExcessLengthStrategy.sanitize   s_     u::''!.88BBDDK<$9do&9$9:<<<r   c                 X    |                     d          rd| }t          |          dS )zValidate that the value doesn't start with reserved 'H_' prefix.

        Args:
            value: The user-provided key.

        Raises:
            InvalidKeyError: If the value starts with 'H_' or 'S_'.
        rE   S_7Keys cannot start with reserved prefixes 'H_' or 'S_': N
startswithr   r   r   r5   s      r   r!   z!HashExcessLengthStrategy.validate   ?     L)) 	'SESSC!#&&&	' 	'r   )rA   )	r   r   r   r   r>   r6   r$   r   r!   r   r   r   r@   r@      s         % %3 % % % % %c c    $'c 'd ' ' ' ' ' 'r   r@   c                   n    e Zd ZdZdddej        dfdededz  ded	ed
eddfdZdedefdZ	deddfdZ
dS )HybridSanitizationStrategya.  Strategy that replaces invalid characters and adds hash fragments.

    This strategy is used by stores with character restrictions (e.g., Elasticsearch,
    Keyring, Windows Registry). Invalid characters are replaced with a safe character,
    and a hash fragment is added for uniqueness. The result is prefixed with 'S_' to
    prevent collisions with user-provided keys.

    Args:
        max_length: Maximum length after sanitization. Defaults to 240.
        allowed_characters: List of allowed characters. Defaults to alphanumeric + dash + underscore.
        replacement_character: Character to use for invalid characters. Defaults to underscore.
        hash_fragment_mode: When to add hash fragments. Defaults to ONLY_IF_CHANGED.
        hash_fragment_length: Length of hash fragment. Defaults to 8.
    rA   N_r,   rB   allowed_charactersreplacement_characterhash_fragment_modehash_fragment_lengthr   c                 L    || _         || _        || _        || _        || _        dS )a  Initialize the character sanitization strategy.

        Args:
            max_length: Maximum length after sanitization.
            allowed_characters: String of allowed characters. Defaults to None (all characters allowed).
            replacement_character: Character to use for invalid characters.
            hash_fragment_mode: When to add hash fragments.
            hash_fragment_length: Length of hash fragment.
        N)rB   rT   rU   rV   rW   )r   rB   rT   rU   rV   rW   s         r   r6   z#HybridSanitizationStrategy.__init__   s1    " %.@%:""4$8!!!r   r   c                 z   |}| j         rt          || j         | j                  }||k    }| j        t          j        k    p| j        t          j        k    o|}|r~t          j        |	                                          
                                d| j                 }d| j        z   }| j        |z
  }t          |          |k    r
|d|         }d| d| S t          |          | j        k    }|r|d| j                 }d}|r6d| }	t          |	          | j        k    r|d| j        dz
           }d| }	|	S |S )a
  Replace invalid characters and add hash fragment if needed.

        The sanitization process:
        1. Replace invalid characters with replacement_character
        2. If changed (and mode is ONLY_IF_CHANGED), add hash fragment
        3. Truncate to max_length (accounting for prefix and hash fragment)
        4. Prefix with 'S_' if sanitization occurred

        Args:
            value: The key or collection name to sanitize.

        Returns:
            The sanitized value with 'S_' prefix if modified.
        )r   rT   replace_withN   rK   -TrF   )rT   r   rU   rV   r	   r   r   r8   r9   r:   r;   rW   rB   rG   )
r   r   	sanitizedchangedadd_hashhash_fragmentoverheadmax_value_lengthneeds_truncationprefixeds
             r   r   z#HybridSanitizationStrategy.sanitize   s    	" 	50GVZVp  I
 u$ *.>.EE 
#'7'GGSG 	  	4#N5<<>>::DDFFGbIbGbcM t88H  $99~~ 000%&7'7&78	 4	33M333 y>>DO; 	!"3DO"34IG 	'I''H8}}t..%&;!(;&;<	+	++Or   c                 X    |                     d          rd| }t          |          dS )zValidate that the value doesn't start with reserved prefixes.

        Args:
            value: The user-provided key or collection name.

        Raises:
            InvalidKeyError: If the value starts with 'H_' or 'S_'.
        rJ   rL   NrM   rO   s      r   r!   z#HybridSanitizationStrategy.validate  rP   r   )r   r   r   r   r	   r   r>   r$   r6   r   r!   r   r   r   rR   rR      s         " )-%(/?/O$%9 99  $J9  #	9
 -9 "9 
9 9 9 9.;c ;c ; ; ; ;z'c 'd ' ' ' ' ' 'r   rR   )r   r8   abcr   r   enumr   !key_value.shared.errors.key_valuer   key_value.shared.utils.sanitizer   r	   r   r&   r2   r3   r/   r@   rR   r   r   r   <module>rj      s  	 	  # # # # # # # #       = = = = = = I I I I I I
# 
# 
# 
# 
#t 
# 
# 
#- - - - -3 - - -`    .   &       -   60' 0' 0' 0' 0'3 0' 0' 0'fo' o' o' o' o'!5 o' o' o' o' o'r   