Ë
    tPêj®i  ã                   ó”  — U d Z ddlZddlmZmZ ddlmZmZmZm	Z	 ddl
mZ erddlmZ ddlmZ ddlmZmZmZmZmZmZ ed	   Ze G d
„ d«      «       Ze G d„ d«      «       Zdedefd„Zd9dededefd„Zd:dededefd„Zde eef   de!e   de eef   fd„Z"d;d„Z#dedede!e   dedef
d„Z$dededefd„Z%deded   dedefd „Z&d!e eef   fd"„Z'dedefd#„Z(g d$¢Z)ee*d%<   d&efd'„Z+d(ededdfd)„Z,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!e   fd0„Z0d1d2gZ1ee*d3<   d1gZ2ee*d4<   ded5ede!e   fd6„Z3	 d<ded5ededefd7„Z4dedefd8„Z5y)=uå  
At-rest credential re-encryption migration.

Switches every encrypted-at-rest value from the legacy XSalsa20-Poly1305 (nacl)
format to the versioned AES-256-GCM (``v2:gcm:``) format produced by
``encrypt_decrypt_utils`` when ``general_settings.encryption_algorithm`` is set to
``aes-256-gcm``.

Design properties (see case 2026-06-24 fix plan):

* **Same key, new algorithm.** The migration does not change the encryption key;
  it re-encrypts existing ciphertext under the same derived key but in the new
  AES format. This is achieved by decrypting with the format-detecting reader and
  re-encrypting through ``encrypt_value_helper`` with the AES gate enabled.
* **Idempotent.** A value already carrying the ``v2:gcm:`` prefix is recognised
  and left untouched, so re-running the migration is a no-op on migrated rows.
* **Resumable.** Walkers commit per row (or per small table), so an interrupted
  run leaves a clean mixed state that a re-run completes.
* **Skip-on-undecryptable.** A value that cannot be decrypted is never
  overwritten â€” corrupt rows are preserved and reported, never destroyed.
* **Attestable.** :func:`check_encryption` is a read-only scan that classifies
  every value as ``migrated`` / ``legacy`` / ``plaintext`` / ``undecryptable``.
  A residual ``legacy == 0`` is the compliance attestation.

Coverage. The covered tables (model table, credentials table, MCP credential/env
tables, config ``environment_variables``) already have a re-encryption path in
``_rotate_master_key``; this module delegates to it in *same-key* mode and adds
walkers for the locations that had no rotation path: team / verification-token
``callback_vars`` metadata, the ``vantage_settings`` / ``cloudzero_settings``
config rows, and the SSO config table.
é    N)Ú	dataclassÚfield)ÚTYPE_CHECKINGÚFinalÚLiteralÚcast)Úverbose_proxy_logger)ÚUserAPIKeyAuth)ÚPrismaClient)Ú_ALGO_AES_GCMÚ_ENCRYPTION_ALGORITHM_SETTINGÚ_V2_GCM_PREFIXÚ_get_salt_keyÚdecrypt_value_helperÚencrypt_value_helper)ÚmigratedÚlegacyÚ	plaintextÚundecryptableúnot-a-stringc                   ó†   — e Zd ZU dZeed<   dZeed<   dZeed<   dZ	eed<   dZ
eed<   dZeed<   dZeed	<   d
eeef   fd„Zy)ÚLocationReportz5Per-location counters for one migration / check pass.Úlocationr   Úscannedr   Ú
already_v2r   r   r   Úreturnc                 óŒ   — | j                   | j                  | j                  | j                  | j                  | j
                  dœS )N©r   r   r   r   r   r   r   ©Úselfs    ú|/home/oem/CyberBrain26-unified/.venv/lib/python3.12/site-packages/litellm/proxy/management_endpoints/credential_migration.pyÚas_dictzLocationReport.as_dictD   s8   € à—|‘|ØŸ™ØŸ/™/ØŸ™Ø!×/Ñ/Ø—k‘kñ
ð 	
ó    N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__ÚstrÚ__annotations__r   Úintr   r   r   r   r   Údictr"   © r#   r!   r   r   6   s[   … á?àƒMØ€GˆSÓØ€HˆcÓØ€J�ÓØ€IˆsÓØ€M�3Óð €FˆCƒOð
˜˜c 3˜h™ô 
r#   r   c                   ó†   — e Zd ZU dZ ee¬«      Zee   ed<   deddfd„Z	e
defd„«       Ze
defd	„«       Zdeeef   fd
„Zy)ÚMigrationReportz&Aggregate report across all locations.)Údefault_factoryÚ	locationsÚreportr   Nc                 ó:   — | j                   j                  |«       y ©N)r0   Úappend)r    r1   s     r!   ÚaddzMigrationReport.addU   s   € Ø�‰×Ñ˜fÕ%r#   c                 ó:   — t        d„ | j                  D «       «      S )zGTotal legacy ciphertext still un-migrated (the TRO attestation number).c              3   ó4   K  — | ]  }|j                   –— Œ y ­wr3   )r   ©Ú.0Úlocs     r!   ú	<genexpr>z2MigrationReport.residual_legacy.<locals>.<genexpr>[   s   è ø€ Ò8 #�3—:•:Ñ8ùó   ‚©Úsumr0   r   s    r!   Úresidual_legacyzMigrationReport.residual_legacyX   s   € ô Ñ8¨¯©Ô8Ó8Ð8r#   c                 ó:   — t        d„ | j                  D «       «      S )Nc              3   ó4   K  — | ]  }|j                   –— Œ y ­wr3   )r   r8   s     r!   r;   z6MigrationReport.total_undecryptable.<locals>.<genexpr>_   s   è ø€ Ò?¨�3×$Õ$Ñ?ùr<   r=   r   s    r!   Útotal_undecryptablez#MigrationReport.total_undecryptable]   s   € äÑ?°·±Ô?Ó?Ð?r#   c                 ó¢   — | j                   | j                  | j                  D �ci c]  }|j                  |j	                  «       “Œ c}dœS c c}w )N)r?   rB   r0   )r?   rB   r0   r   r"   )r    r:   s     r!   r"   zMigrationReport.as_dicta   sE   € à#×3Ñ3Ø#'×#;Ñ#;ØAEÇÁÖP¸#˜#Ÿ,™,¨¯©«Ñ5ÒPñ
ð 	
ùò Qs   ¥"A)r$   r%   r&   r'   r   Úlistr0   r   r)   r5   Úpropertyr*   r?   rB   r+   r(   Úobjectr"   r,   r#   r!   r.   r.   O   sz   … á0á&+¸DÔ&A€Iˆt�NÑ#ÓAð&˜.ð &¨Tó &ð ð9 ò 9ó ð9ð ð@ Sò @ó ð@ð
˜˜c 6˜kÑ*ô 
r#   r.   Úvaluer   c                 óP   — t        | t        «      xr | j                  t        «      S )zETrue if ``value`` is already an AES-256-GCM (``v2:gcm:``) ciphertext.)Ú
isinstancer(   Ú
startswithr   )rG   s    r!   Úis_migratedrK   n   s   € ä�eœSÓ!ÒF e×&6Ñ&6´~Ó&FÐFr#   Úkeyc                 ó‚   — t        | t        «      sy| dk(  ry| j                  t        «      ryt	        | |dd¬«      }|€yy)	uˆ  Classify a stored value for the residual scanner.

    * ``not-a-string`` â€” not a string (numbers/bools/None left as-is on disk).
    * ``migrated`` â€” carries the ``v2:gcm:`` prefix.
    * ``legacy`` â€” decrypts under the legacy nacl reader (still needs migrating).
    * ``plaintext`` â€” a non-empty string that does not decrypt and is not v2;
      treated as legacy plaintext (nothing to migrate).
    * ``undecryptable`` â€” reserved for callers that already know a value is
      ciphertext but cannot decrypt it; ``classify_value`` itself cannot tell a
      corrupt ciphertext from plaintext, so it returns ``plaintext`` for both.
    r   Ú r   r   ÚdebugF©rG   rL   Úexception_typeÚreturn_original_valuer   )rI   r(   rJ   r   r   ©rG   rL   Ú	decrypteds      r!   Úclassify_valuerU   s   sJ   € ô �eœSÔ!ØØ�‚{ØØ×ÑœÔ'ØÜ+°%¸SÐQXÐpuÔv€IØÐàØr#   c                 óš   — t        | t        «      r| dk(  r| S | j                  t        «      r| S t	        | |dd¬«      }|€| S t        |«      S )a^  Re-encrypt a single stored string into the configured (AES) format.

    Returns the value unchanged if it is not a string, is already ``v2:``, or
    cannot be decrypted (skip-on-undecryptable). Otherwise decrypts under the
    format-detecting reader and re-encrypts through ``encrypt_value_helper``
    (which writes AES when the gate is on).
    rN   rO   FrP   )rI   r(   rJ   r   r   r   rS   s      r!   Úreencrypt_valuerW   Œ   sV   € ô �eœSÔ! U¨b¢[ØˆØ×ÑœÔ'ØˆÜ+°%¸SÐQXÐpuÔv€IØÐð ˆÜ 	Ó*Ð*r#   ÚdataÚsensitive_keysc                 ór   — t        | «      }|D ]&  }|j                  |«      }|€Œt        ||¬«      ||<   Œ( |S )z¿Return a copy of ``data`` with only ``sensitive_keys`` re-encrypted.

    Non-sensitive fields (e.g. ``base_url``, ``connection_id``) are left as-is.
    Null/missing fields are skipped.
    ©rL   )r+   ÚgetrW   )rX   rY   ÚoutÚkÚvs        r!   Úreencrypt_selective_dictr`       sH   € ô �d“€CØò +ˆØ�G‰G�A‹JˆØˆ9ØÜ  ¨Ô*ˆˆAŠð	+ð
 €Jr#   c                  ó´   — ddl m}  | j                  t        «      }t	        |t
        «      r|j                  «       t        k(  st        dt        › d|›d�«      ‚y)uØ   Fail fast if the AES algorithm gate is not enabled.

    Running the migration with the gate off would decrypt then re-encrypt right
    back into the legacy format â€” a no-op that silently fails the migration.
    r   )Úgeneral_settingszFEncryption migration requires general_settings.encryption_algorithm: 'z'. Current value: zW. Set it before migrating so re-encrypted values are written in the AES-256-GCM format.N)	Úlitellm.proxy.proxy_serverrb   r\   r   rI   r(   Úlowerr   ÚRuntimeError)rb   Úalgos     r!   Ú_assert_aes_gate_enabledrg   ¯   s_   € õ <à"×&Ñ&Ô'DÓE€DÜ�tœSÔ! d§j¡j£l´mÒ&CÜðÜˆÐ0°°ð 9LðLó
ð 	
ð 'Dr#   Úprisma_clientÚ
param_nameÚsensitive_fieldsÚdry_runc              ƒ   ól  K  — t        |¬«      }| j                  j                  j                  d|i¬«      ƒ d{  –—† }|�|j                  €|S |j                  }t        |t        «      rt        j                  |«      }t        |t        «      s|S d}|D ]Ê  }|j                  |«      }	|	€Œ|xj                  dz  c_        t        |	|¬«      }
|
dk(  r|xj                  dz  c_        ŒT|
d	k(  r]|r|xj                  dz  c_        Œqt        |	|¬«      }||	k7  r|||<   |xj                   dz  c_        d
}Œ |xj                  dz  c_        Œ¶|xj"                  dz  c_        ŒÌ |rH|sF| j                  j                  j%                  d|idt        j&                  |«      i¬«      ƒ d{  –—†  |S 7 �Œu7 Œ	­w)z�Migrate a single ``LiteLLM_Config`` row whose ``param_value`` is a JSON
    dict with selected sensitive fields (vantage_settings / cloudzero_settings).
    ©r   ri   ©ÚwhereNFé   r[   r   r   TÚparam_value©ro   rX   )r   ÚdbÚlitellm_configÚfind_uniquerq   rI   r(   ÚjsonÚloadsr+   r\   r   rU   r   r   rW   r   r   ÚupdateÚdumps)rh   ri   rj   rk   r1   ÚrecordÚsettingsÚchangedÚfldr_   ÚclsÚnew_vs               r!   Ú_migrate_config_settings_rowr€   Ç   s¬  è ø€ ô #¨JÔ7€FØ'×*Ñ*×9Ñ9×EÑEÈ\Ð[eÐLfÐEÓg×g€FØ€~˜×+Ñ+Ð3Øˆà×!Ñ!€HÜ�(œCÔ Ü—:‘:˜hÓ'ˆÜ�h¤Ô%Øˆà€GØò "ˆØ�L‰L˜ÓˆØˆ9ØØ�Š˜!Ñ�Ü˜Q CÔ(ˆØ�*ÒØ×Ò Ñ"ÕØØ�(Š?Ùð —’ Ñ"•ØÜ# A¨3Ô/ˆEØ˜ŠzØ %�˜‘Ø—’ 1Ñ$•Ø‘ð —’ Ñ"–à×Ò Ñ!Öð3"ñ6 ‘wØ×Ñ×-Ñ-×4Ñ4Ø Ð,Ø¤§¡¨HÓ!5Ð6ð 5ó 
÷ 	
ð 	
ð €MðW hùðN	
ús"   ‚8F4ºF/»E-F4Æ(F2Æ)F4Æ2F4c              ƒ   ó¨  K  — t        d¬«      }| j                  j                  j                  ddi¬«      ƒ d{  –—† }|�|j                  €|S |j                  }t        |t        «      rt        j                  |«      }t        |t        «      s|S t        |«      }d}|j                  «       D ]Ï  \  }}t        |t        «      r|dk(  rŒ|xj                  dz  c_        t        ||¬	«      }	|	d
k(  r|xj                  dz  c_        ŒY|	dk(  r]|r|xj                  dz  c_        Œvt        ||¬	«      }
|
|k7  r|
||<   |xj                   dz  c_        d}Œ¥|xj                  dz  c_        Œ»|xj"                  dz  c_        ŒÑ |rH|sF| j                  j                  j%                  ddidt        j&                  |«      i¬«      ƒ d{  –—†  |S 7 �Œ“7 Œ	­w)z¿Migrate the ``LiteLLM_SSOConfig`` row. All non-null fields are encrypted
    (via the same ``_encrypt_env_variables`` path used on save), so we re-encrypt
    every present string field.
    Ú
sso_configrm   Úidrn   NFrN   rp   r[   r   r   TÚsso_settingsrr   )r   rs   Úlitellm_ssoconfigru   r„   rI   r(   rv   rw   r+   Úitemsr   rU   r   r   rW   r   r   rx   ry   )rh   rk   r1   rz   r{   Únew_settingsr|   r}   r_   r~   r   s              r!   Ú_migrate_sso_configrˆ   ÿ   s¿  è ø€ ô
 #¨LÔ9€FØ'×*Ñ*×<Ñ<×HÑHÐPTÐVbÐOcÐHÓd×d€FØ€~˜×,Ñ,Ð4Øˆà×"Ñ"€HÜ�(œCÔ Ü—:‘:˜hÓ'ˆÜ�h¤Ô%Øˆä˜x›.€LØ€GØ—.‘.Ó"ò "‰ˆˆQÜ˜!œSÔ! Q¨"¢WØØ�Š˜!Ñ�Ü˜Q CÔ(ˆØ�*ÒØ×Ò Ñ"ÕØØ�(Š?Ùð —’ Ñ"•ØÜ# A¨3Ô/ˆEØ˜ŠzØ$)�˜SÑ!Ø—’ 1Ñ$•Ø‘ð —’ Ñ"–à×Ò Ñ!Öð1"ñ4 ‘wØ×Ñ×0Ñ0×7Ñ7Ø˜Ð&Ø ¤$§*¡*¨\Ó":Ð;ð 8ó 
÷ 	
ð 	
ð €MðW eùðN	
ús"   ‚8GºG»FGÇGÇGÇGÚ
table_name)ÚteamÚverification_tokenc              ƒ   óø  K  — ddl m}m} t        |› d�¬«      }|dk(  r| j                  j
                  }d}n| j                  j                  }d}|j                  «       ƒ d{  –—† }|xs g D �])  }	t        |	d	d«      }
t        |
t        «      rd
|
vrd|
vrŒ*d}t        |
«      D ]r  }|j                  «       D ]]  }|xj                  dz  c_        t        |«      }|dk(  r|xj                  dz  c_        Œ>|dk(  r|dz  }ŒI|xj                   dz  c_        Œ_ Œt |dk(  rŒ²|r|xj"                  |z  c_        ŒÊ	  | ||
«      «      }|xj.                  |z  c_        |j1                  |t        |	|«      id	t3        j4                  |«      i¬«      ƒ d{  –—†  �Œ, |S 7 �Œ:# t$        $ rL}t'        j(                  d|t        |	|d«      t+        |«      «       |xj,                  |z  c_        Y d}~�Œ‚d}~ww xY w7 Œd­w)a³  Migrate callback-var credentials on the team or verification-token table.

    Covers both shapes the ``decrypt_callback_vars`` / ``encrypt_callback_vars``
    transforms understand: ``metadata.logging[*].callback_vars.<sensitive>`` and
    the top-level ``metadata.callback_settings.callback_vars.<sensitive>``. Reuses
    those proven transforms (selective, prefix-marked; legacy plaintext is left
    alone until re-encrypted).
    r   )Údecrypt_callback_varsÚencrypt_callback_varsz.callback_varsrm   rŠ   Úteam_idÚtokenNÚmetadataÚloggingÚcallback_settingsrp   r   r   z7Skipping %s row %s callback_vars (transform failed): %sú?rr   )Ú)litellm.proxy.common_utils.callback_utilsr�   rŽ   r   rs   Úlitellm_teamtableÚlitellm_verificationtokenÚ	find_manyÚgetattrrI   r+   Ú_iter_callback_var_dictsÚvaluesr   Ú_classify_callback_valuer   r   r   Ú	Exceptionr	   Úwarningr(   r   r   rx   rv   ry   )rh   r‰   rk   r�   rŽ   r1   ÚtableÚpkÚrowsÚrowr‘   Ú
row_legacyÚcvsr_   r~   Úre_encryptedÚes                    r!   Ú_migrate_callback_vars_tabler§   3  s  è ø€ ÷ô
 #¨z¨l¸.Ð,IÔJ€Fà�VÒØ× Ñ ×2Ñ2ˆØ‰à× Ñ ×:Ñ:ˆØˆàŸ™Ó)×)€DØŠz�ró 0
ˆÜ˜3 
¨DÓ1ˆÜ˜(¤DÔ)¨i¸xÑ.GÐL_ÐgoÑLoØð ˆ
Ü+¨HÓ5ò 		*ˆCØ—Z‘Z“\ò *�Ø—’ !Ñ#•Ü.¨qÓ1�Ø˜*Ò$Ø×%Ò%¨Ñ*Ö%Ø˜H’_Ø !‘O‘Jà×$Ò$¨Ñ)Ö$ñ*ð		*ð ˜Š?Øáà�MŠM˜ZÑ'�MØð
	Ù0Ñ1FÀxÓ1PÓQˆLð 	�Š˜:Ñ%�Ø�l‰lØ”w˜s BÓ'Ð(ØœdŸj™j¨Ó6Ð7ð ó 
÷ 	
ò 	
ð[0
ðd €Mðg *úôH ò 	Ü ×(Ñ(ØIØÜ˜˜R Ó%Ü�A“ô	ð × Ò  JÑ.Õ Ýûð	úð	
úsK   ‚A!G:Á#FÁ$CG:Ä9F ÅAG:ÆG8Æ
G:Æ 	G5Æ)AG0Ç*G:Ç0G5Ç5G:r‘   c              #   ó@  K  — | j                  dg «      xs g D ]9  }t        |t        «      sŒ|j                  d«      }t        |t        «      sŒ6|–— Œ; | j                  d«      }t        |t        «      r'|j                  d«      }t        |t        «      r|–— yyy­w)as  Yield each ``callback_vars`` dict in a metadata structure.

    Mirrors ``_transform_callback_vars``: credentials live both under
    ``logging[*].callback_vars`` and under the top-level
    ``callback_settings.callback_vars``. Counting only the former would let the
    walker report success while leaving ``callback_settings`` secrets in legacy
    format at rest.
    r’   Úcallback_varsr“   N)r\   rI   r+   )r‘   Úentryr¤   r“   s       r!   rš   rš   „  s”   è ø€ ð —‘˜i¨Ó,Ò2°ò ˆÜ�eœTÕ"Ø—)‘)˜OÓ,ˆCÜ˜#œtÕ$Ø“	ð	ð
  (Ÿ|™|Ð,?Ó@ÐÜÐ#¤TÔ*Ø×#Ñ# OÓ4ˆÜ�cœ4Ô Ø‹Ið !ð +ùs   ‚*B­!BÁABc                 óp   — ddl m} t        | t        «      sy| }|j	                  |«      }t        |d¬«      S )a  Classify one stored callback-var value, independent of the AES gate.

    Encrypted callback vars carry the ``litellm_enc::`` marker in front of the
    ciphertext; strip it, then classify the inner value the same way the
    covered-table scanner does (``v2:gcm:`` prefix -> migrated, nacl-decryptable
    -> legacy, otherwise plaintext). Detecting legacy by decrypt rather than by a
    re-encrypt delta is what makes the ``check_encryption`` attestation correct
    even when run with the AES write gate off.
    r   )Ú_CALLBACK_VAR_ENCRYPTED_PREFIXr   Úcallbackr[   )r•   r¬   rI   r(   ÚremoveprefixrU   )rG   r¬   Úinners      r!   rœ   rœ   ™  s9   € õô �eœSÔ!ØØ€EØ×ÑÐ=Ó>€EÜ˜% ZÔ0Ð0r#   ))Úmodel_tableÚlitellm_proxymodeltable)Úlitellm_paramsr,   )ÚcredentialsÚlitellm_credentialstable)Úcredential_valuesr,   )Ú
mcp_serverÚlitellm_mcpservertable)r³   Úenv_varsr,   )Úmcp_user_credentialsÚlitellm_mcpusercredentialsr,   )Úcredential_b64)Úmcp_user_env_varsÚlitellm_mcpuserenvvarsr,   )Ú
values_b64Ú_COVERED_TABLE_SPECSÚobjc              #   ó
  K  — | g}|rz|j                  «       }t        |t        «      r|–— nQt        |t        «      r |j	                  |j                  «       «       n!t        |t        «      r|j	                  |«       |rŒyyy­w)a,  Yield every string leaf in a nested dict/list/scalar structure.

    Iterative (explicit stack) on purpose: recursion here is banned by the
    code-quality recursive-function detector (unbounded nesting has caused CPU
    spikes in the past), and an explicit stack walks arbitrary depth safely.
    N)ÚpoprI   r(   r+   Úextendr›   rD   )rÀ   ÚstackÚcurs      r!   Ú_iter_encrypted_stringsrÆ   Â  sc   è ø€ ð #& €EÙ
Ø�i‰i‹kˆÜ�cœ3ÔØ‹IÜ˜œTÔ"Ø�L‰L˜Ÿ™›Õ&Ü˜œTÔ"Ø�L‰L˜Ôô ùs   ‚A>BÂBr1   c                 óÞ   — | xj                   dz  c_         t        |d¬«      }|dk(  r| xj                  dz  c_        y|dk(  r| xj                  dz  c_        y| xj                  dz  c_        y)u'  Classify one stored string and bump the matching read-only counter.

    Only genuine nacl ciphertext lands in ``legacy``; non-secret strings (model
    names, base URLs, â€¦) do not decrypt and fall through to ``plaintext``, so
    over-scanning a column is harmless to the residual count.
    rp   Úscanr[   r   r   N)r   rU   r   r   r   )r1   rG   r~   s      r!   Ú_classify_into_reportrÉ   Ô  s]   € ð ‡N‚N�aÑ…NÜ ¨6Ô2€CØ
ˆjÒØ×Ò˜QÑÖØ	�ŠØ�Š˜ÑŽà×Ò˜AÑÖr#   r   Údb_attrÚjson_columnsÚscalar_columnsc              ƒ   óV  K  — t        |¬«      }t        | j                  |d «      }|€|S 	 |j                  «       ƒ d {  –—† }|xs g D ]Œ  }	|D ]T  }
t        |	|
d «      }|€Œt        |t        «      r	 t        j                  |«      }t        |«      D ]  }t        ||«       Œ ŒV |D ],  }
t        |	|
d «      }t        |t        «      sŒ!t        ||«       Œ. ŒŽ |S 7 Œ›# t        $ r,}t        j                  d|t        |«      «       |cY d }~S d }~ww xY w# t        t        f$ r Y Œžw xY w­w)Nrm   zscan: %s unavailable: %s)r   r™   rs   r˜   r�   r	   rO   r(   rI   rv   rw   Ú
ValueErrorÚ	TypeErrorrÆ   rÉ   )rh   r   rÊ   rË   rÌ   r1   rŸ   r¡   r¦   r¢   ÚcolÚrawÚsr_   s                 r!   Ú_scan_one_tablerÓ   å  s<  è ø€ ô #¨HÔ5€FÜ˜=×+Ñ+¨W°dÓ;€EØ€}ØˆðØ!ŸO™OÓ-×-ˆð Šz�rò 1ˆØò 
	1ˆCÜ˜#˜s DÓ)ˆCØˆ{ØÜ˜#œsÔ#ðÜŸ*™* S›/�Cô -¨SÓ1ò 1�Ü% f¨aÕ0ñ1ð
	1ð "ò 	1ˆCÜ˜˜S $Ó'ˆAÜ˜!œSÕ!Ü% f¨aÕ0ñ	1ð1ð  €Mð) .ùÜò Ü×"Ñ"Ð#=¸xÌÈQËÔPØ�ûðûô #¤IÐ.ò Ùðüso   ‚(D)«C ¾C¿C Á.D)Á2DÂA D)ÃD)ÃC Ã	DÃ%!DÄDÄD)ÄDÄD)ÄD&Ä#D)Ä%D&Ä&D)c              ƒ   óä  K  — t        d¬«      }	 | j                  j                  j                  ddi¬«      ƒ d{  –—† }|�|j                  €|S |j                  }t        |t        «      r	 t        j                  |«      }t        |«      D ]  }t        ||«       Œ |S 7 Œd# t        $ r+}t        j                  dt        |«      «       |cY d}~S d}~ww xY w# t        t        f$ r i }Y Œjw xY w­w)zEScan the ``environment_variables`` config row (``param_value`` dict).Úconfig_environment_variablesrm   ri   Úenvironment_variablesrn   Nz%scan: config env vars unavailable: %s)r   rs   rt   ru   r�   r	   rO   r(   rq   rI   rv   rw   rÎ   rÏ   rÆ   rÉ   )rh   r1   rz   r¦   rG   rÒ   s         r!   Ú_scan_config_env_varsr×     sí   è ø€ ä"Ð,JÔK€FðØ+×.Ñ.×=Ñ=×IÑIÐQ]Ð_vÐPwÐIÓx×xˆð €~˜×+Ñ+Ð3ØˆØ×Ñ€EÜ�%œÔð	Ü—J‘J˜uÓ%ˆEô % UÓ+ò )ˆÜ˜f aÕ(ð)à€Mð yùÜò Ü×"Ñ"Ð#JÌCÐPQËFÔSØ�ûðûô œIÐ&ò 	ØŠEð	üsh   ‚C0�+B" »B ¼B" Á ,C0Á-C ÂC0Â B" Â"	CÂ+ CÃCÃC0ÃCÃC0ÃC-Ã*C0Ã,C-Ã-C0c           
   ƒ   óÊ   K  — g }t         D ]-  \  }}}}|j                  t        | ||||«      ƒ d{  –—† «       Œ/ |j                  t        | «      ƒ d{  –—† «       |S 7 Œ.7 Œ­w)zDRead-only classification of every rotation-covered table. No writes.N)r¿   r4   rÓ   r×   )rh   Úreportsr   rÊ   Ú	json_colsÚscalar_colss         r!   Ú_scan_covered_tablesrÜ     sf   è ø€ à+-€GÜ5Iò hÑ1ˆ�'˜9 kØ�‰œ_¨]¸HÀgÈyÐZeÓf×fÕgðhà‡N�NÔ.¨}Ó=×=Ô>Ø€Nð gøØ=ús!   ‚.A#°A
±#A#ÁA!ÁA#Á!A#Úapi_keyÚintegration_tokenÚ_VANTAGE_SENSITIVEÚ_CLOUDZERO_SENSITIVEÚuser_api_key_dictc              ƒ   óÊ  K  — ddl m} t        | «      ƒ d{  –—† D �ci c]  }|j                  |“Œ }}t	        «       }|€t        d«      ‚ |t        d| «      t        d|«      ||¬«      ƒ d{  –—†  t        | «      ƒ d{  –—† }|D ]K  }|j                  |j                  «      }|r|j                  nd}	t        d|	|j                  z
  «      |_
        ŒM |S 7 ŒÀc c}w 7 Œp7 Œ_­w)aO  Re-encrypt the tables already covered by ``_rotate_master_key`` (model
    table, credentials, MCP credential/env tables, config environment_variables)
    by running that orchestrator in *same-key* mode. With the AES gate on, the
    re-encrypt writes land in ``v2:`` format.

    ``_rotate_master_key`` returns no counts, so we bracket it with read-only
    scans: the pre-scan's legacy total minus the post-scan's gives the number
    actually migrated per location, and the post-scan supplies the residual /
    already-v2 / scanned figures. Returns one report per covered location.
    r   )Ú_rotate_master_keyNzfCannot migrate covered tables: no salt key / master key is set. Set LITELLM_SALT_KEY before migrating.r   r
   )rh   rá   Úcurrent_master_keyÚnew_master_key)Ú;litellm.proxy.management_endpoints.key_management_endpointsrã   rÜ   r   r   re   r   r\   r   Úmaxr   )
rh   rá   rã   ÚrÚpreÚcurrent_keyÚpostÚpost_reportÚ
pre_reportÚ
pre_legacys
             r!   Ú_migrate_covered_tablesrï   /  sø   è ø€ õô 0DÀMÓ/R×)RÖS A�!—*‘*˜a‘-ÐS€CÐSä&›€KØÐÜØtó
ð 	
ñ Ü˜>¨=Ó9ÜÐ/Ð1BÓCØ&Ø"ô	÷ ð ô -¨]Ó;×;€DØò GˆØ—W‘W˜[×1Ñ1Ó2ˆ
Ù*4�Z×&Ò&¸!ˆ
ô  # 1 j°;×3EÑ3EÑ&EÓFˆÕðGð €Kð+ *SúÒSðøð <ús>   ‚C#—C˜C#žC²<C#Á.CÁ/C#ÂC!ÂAC#ÃC#Ã!C#c              ƒ   óz  K  — t        «        t        «       }|r*t        | «      ƒ d{  –—† D ]  }|j                  |«       Œ n*t	        | |«      ƒ d{  –—† D ]  }|j                  |«       Œ |j                  t        | d|«      ƒ d{  –—† «       |j                  t        | d|«      ƒ d{  –—† «       |j                  t        | dt        |«      ƒ d{  –—† «       |j                  t        | dt        |«      ƒ d{  –—† «       |j                  t        | |«      ƒ d{  –—† «       |S 7 �Œ7 ŒÜ7 Œ¨7 Œ†7 Œ_7 Œ87 Œ­w)aÒ  Run the full at-rest re-encryption migration.

    Requires ``general_settings.encryption_algorithm == 'aes-256-gcm'`` so writes
    are produced in the AES format. Idempotent and resumable: re-running skips
    already-migrated values and finishes any partial run.

    A ``dry_run`` performs no writes: the covered tables are scanned read-only
    (so their residual legacy still counts toward the attestation) and the
    net-new walkers run in dry-run mode.
    NrŠ   r‹   Úvantage_settingsÚcloudzero_settings)
rg   r.   rÜ   r5   rï   r§   r€   rß   rà   rˆ   )rh   rá   rk   r1   Úcovereds        r!   Úmigrate_encryptionrô   V  s  è ø€ ô Ôä#Ó%€Fñ
 Ü1°-Ó@×@ò 	 ˆGØ�J‰J�wÕñ	 ô 5°]ÐDUÓV×Vò 	 ˆGØ�J‰J�wÕð	 ð ‡J�JÔ1°-ÀÈÓQ×QÔRØ
‡J�JÔ1°-ÐAUÐW^Ó_×_Ô`Ø
‡J�JÔ1°-ÐASÔUgÐipÓq×qÔrØ
‡J�JÔ1°-ÐAUÔWkÐmtÓu×uÔvØ
‡J�JÔ(¨¸Ó@×@ÔAà€Mð Aùð Wøð RøØ_øØqøØuøØ@ús{   ‚%D;§D,¨*D;ÁD/Á5D;ÂD1Â	#D;Â,D3Â-(D;ÃD5Ã(D;Ã>D7Ã?"D;Ä!D9Ä"D;Ä/D;Ä1D;Ä3D;Ä5D;Ä7D;Ä9D;c              ƒ   ó  K  — t        «       }t        | «      ƒ d{  –—† D ]  }|j                  |«       Œ |j                  t        | dd¬«      ƒ d{  –—† «       |j                  t        | dd¬«      ƒ d{  –—† «       |j                  t	        | dt
        d¬«      ƒ d{  –—† «       |j                  t	        | dt        d¬«      ƒ d{  –—† «       |j                  t        | d¬«      ƒ d{  –—† «       |S 7 ŒÞ7 Œ©7 Œ†7 Œ^7 Œ67 Œ­w)aß  Read-only residual scan across **every** at-rest location. No writes.

    Covers both the rotation-managed tables (model / credentials / MCP credential
    and env-var tables / config ``environment_variables``) and the net-new walker
    locations (team and verification-token ``callback_vars``, vantage / cloudzero
    config rows, SSO config). Reports how many values are still ``legacy``;
    ``residual_legacy == 0`` across this full scan is the compliance attestation.
    NrŠ   T)rk   r‹   rñ   rò   )r.   rÜ   r5   r§   r€   rß   rà   rˆ   )rh   r1   ró   s      r!   Úcheck_encryptionrö   }  sä   è ø€ ô $Ó%€Fô .¨mÓ<×<ò ˆØ�
‰
�7Õðð ‡J�JÔ1°-ÀÐQUÔV×VÔWØ
‡J�JÔ1°-ÐAUÐ_cÔd×dÔeØ
‡J�JÔ1°-ÐASÔUgÐquÔv×vÔwØ
‡J�JÜ*¨=Ð:NÔPdÐnrÔs×sôð ‡J�JÔ(¨ÀÔE×EÔFØ€Mð =øð WøØdøØvøàsøàEúsi   ‚D›C:œ6DÁC<Á$DÁ7C>Á8)DÂ!D Â")DÃDÃ#DÃ/DÃ0DÃ<DÃ>DÄ DÄDÄD)rÈ   )Úmigrate)r   N)F)6r'   rv   Údataclassesr   r   Útypingr   r   r   r   Úlitellm._loggingr	   Úlitellm.proxy._typesr
   Úlitellm.proxy.utilsr   Ú0litellm.proxy.common_utils.encrypt_decrypt_utilsr   r   r   r   r   r   Ú
ValueClassr   r.   rF   ÚboolrK   r(   rU   rW   r+   rD   r`   rg   r€   rˆ   r§   rš   rœ   r¿   r)   rÆ   rÉ   ÚtuplerÓ   r×   rÜ   rß   rà   rï   rô   rö   r,   r#   r!   ú<module>r     sÄ  ðòó@ ß (ß 6Ó 6å 1áÝ3Ý0÷÷ ð ÐWÑX€
ð ÷
ð 
ó ð
ð0 ÷
ð 
ó ð
ð<G�vð G $ó Gñ
˜&ð  sð ¸
ó ñ2+˜6ð +¨ð +¸Fó +ð( 4¨¨V¨Ñ#4ð ÀdÈ3Áið ÐTXÐY\Ð^dÐYdÑTeó ó
ð05Øð5àð5ð ˜3‘ið5ð ð	5ð
 ó5ðp1¨Vð 1¸dð 1À~ó 1ðhNØðNàÐ4Ñ5ðNð ðNð ó	Nðb t¨C°¨KÑ'8ó ð*1 Fð 1¨zó 1ò@Ð �eó ð ó ð$ .ð ¸ð Àó ð" Øð àð ð ð ð ð	 ð
 ð ð ó ðF¨vð ¸.ó ð*¨fð ¸¸nÑ9Mó ð 'Ð(;Ð<Ð �EÓ <Ø(˜kÐ �eÓ )ð$°ð $ÈFð $ÐW[Ð\jÑWkó $ðT ñ$Øð$àð$ð ð$ð ó	$ðN¨&ð °_ô r#   