mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(proxy): stop logging raw API key in AuditLog on key update
The /key/update audit hook stored the raw request key in object_id and updated_values, unlike the create/rotate/delete hooks which reference the hashed token. Use existing_key_row.token for object_id and drop the raw key from updated_values so the AuditLog never persists the plaintext sk- value. Fixes #31620 Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
d25bac5a41
commit
494d2166fa
2 changed files with 56 additions and 3 deletions
|
|
@ -88,7 +88,7 @@ class KeyManagementEventHooks:
|
|||
@staticmethod
|
||||
async def async_key_updated_hook(
|
||||
data: UpdateKeyRequest,
|
||||
existing_key_row: Any,
|
||||
existing_key_row: LiteLLM_VerificationToken,
|
||||
response: Any,
|
||||
user_api_key_dict: UserAPIKeyAuth,
|
||||
litellm_changed_by: Optional[str] = None,
|
||||
|
|
@ -107,7 +107,7 @@ class KeyManagementEventHooks:
|
|||
|
||||
# Enterprise Feature - Audit Logging. Enable with litellm.store_audit_logs = True
|
||||
if litellm.store_audit_logs is True:
|
||||
_updated_values = json.dumps(data.json(exclude_none=True), default=str)
|
||||
_updated_values = json.dumps(data.json(exclude_none=True, exclude={"key"}), default=str)
|
||||
|
||||
_before_value = existing_key_row.json(exclude_none=True)
|
||||
_before_value = json.dumps(_before_value, default=str)
|
||||
|
|
@ -124,7 +124,7 @@ class KeyManagementEventHooks:
|
|||
),
|
||||
changed_by_api_key=user_api_key_dict.api_key,
|
||||
table_name=LitellmTableNames.KEY_TABLE_NAME,
|
||||
object_id=data.key,
|
||||
object_id=existing_key_row.token or "",
|
||||
action="updated",
|
||||
updated_values=_updated_values,
|
||||
before_value=_before_value,
|
||||
|
|
|
|||
|
|
@ -155,6 +155,59 @@ class TestKeyManagementEventHooksIndependentOperations:
|
|||
assert email_called["called"] is True
|
||||
|
||||
|
||||
class TestKeyUpdateAuditLogDoesNotLeakRawKey:
|
||||
"""Regression tests for issue #31620 - raw API key leaked in AuditLog."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_updated_hook_uses_hashed_token_and_hides_raw_key(self):
|
||||
"""
|
||||
/key/update must reference the key by its hashed token in the audit log,
|
||||
never the raw sk- value, in either object_id or updated_values.
|
||||
"""
|
||||
import asyncio
|
||||
|
||||
from litellm.proxy._types import (
|
||||
LiteLLM_VerificationToken,
|
||||
UpdateKeyRequest,
|
||||
UserAPIKeyAuth,
|
||||
)
|
||||
|
||||
raw_key = "sk-super-secret-raw-key-should-never-be-logged"
|
||||
hashed_token = "a" * 64
|
||||
|
||||
data = UpdateKeyRequest(key=raw_key, max_budget=2000.0)
|
||||
existing_key_row = LiteLLM_VerificationToken(
|
||||
token=hashed_token,
|
||||
key_name="sk-...oged",
|
||||
max_budget=100.0,
|
||||
)
|
||||
|
||||
captured = {}
|
||||
|
||||
async def fake_create_audit_log_for_update(request_data):
|
||||
captured["log"] = request_data
|
||||
|
||||
with (
|
||||
patch(
|
||||
"litellm.proxy.management_helpers.audit_logs.create_audit_log_for_update",
|
||||
side_effect=fake_create_audit_log_for_update,
|
||||
),
|
||||
patch("litellm.store_audit_logs", True),
|
||||
):
|
||||
await KeyManagementEventHooks.async_key_updated_hook(
|
||||
data=data,
|
||||
existing_key_row=existing_key_row,
|
||||
response=existing_key_row,
|
||||
user_api_key_dict=UserAPIKeyAuth(api_key="hashed-admin", user_id="default_user_id"),
|
||||
)
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
log = captured["log"]
|
||||
assert log.object_id == hashed_token
|
||||
assert raw_key not in (log.object_id or "")
|
||||
assert raw_key not in (log.updated_values or "")
|
||||
|
||||
|
||||
class TestRotateVirtualKeyInSecretManager:
|
||||
"""Tests for _rotate_virtual_key_in_secret_manager with team_id support."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue