mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
fix(proxy): never log raw virtual keys in key insertion debug output (#33268)
* fix(proxy): never log raw virtual keys in key insertion debug output * fix(proxy): tolerate None token in insert_data debug log redaction
This commit is contained in:
parent
668df9494a
commit
f31dacbcd4
4 changed files with 89 additions and 3 deletions
|
|
@ -42,7 +42,7 @@ from litellm.proxy._experimental.mcp_server.db import (
|
|||
rotate_mcp_user_env_vars_master_key,
|
||||
)
|
||||
from litellm.proxy._types import *
|
||||
from litellm.proxy._types import LiteLLM_VerificationToken
|
||||
from litellm.proxy._types import LiteLLM_VerificationToken, hash_token
|
||||
from litellm.proxy.auth.auth_checks import (
|
||||
_delete_cache_key_object,
|
||||
can_team_access_model,
|
||||
|
|
@ -3777,7 +3777,10 @@ async def generate_key_helper_fn(
|
|||
return user_data
|
||||
|
||||
## CREATE KEY
|
||||
verbose_proxy_logger.debug("prisma_client: Creating Key= %s", key_data)
|
||||
verbose_proxy_logger.debug(
|
||||
"prisma_client: Creating Key= %s",
|
||||
{**key_data, "token": hash_token(token=token)},
|
||||
)
|
||||
create_key_response = await prisma_client.insert_data(data=key_data, table_name="key")
|
||||
|
||||
key_data["token_id"] = getattr(create_key_response, "token", None)
|
||||
|
|
|
|||
|
|
@ -3592,7 +3592,10 @@ class PrismaClient:
|
|||
"""
|
||||
start_time = time.time()
|
||||
try:
|
||||
verbose_proxy_logger.debug("PrismaClient: insert_data: %s", data)
|
||||
verbose_proxy_logger.debug(
|
||||
"PrismaClient: insert_data: %s",
|
||||
{**data, "token": self.hash_token(token=data["token"])} if data.get("token") is not None else data,
|
||||
)
|
||||
if table_name == "key":
|
||||
token = data["token"]
|
||||
hashed_token = self.hash_token(token=token)
|
||||
|
|
|
|||
|
|
@ -529,6 +529,56 @@ async def test_key_generation_with_object_permission(monkeypatch):
|
|||
assert key_insert_calls[0]["data"].get("object_permission_id") == "objperm123"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_generate_key_debug_log_never_contains_raw_token(monkeypatch, caplog):
|
||||
"""Regression for LIT-4356: /key/generate must never emit the raw virtual key
|
||||
to a logger, even for short keys that bypass the regex-based
|
||||
SecretRedactionFilter."""
|
||||
import hashlib
|
||||
import logging
|
||||
|
||||
mock_prisma_client = AsyncMock()
|
||||
mock_prisma_client.jsonify_object = lambda data: data
|
||||
mock_prisma_client.db = MagicMock()
|
||||
|
||||
async def _insert_data_side_effect(*args, **kwargs):
|
||||
if kwargs.get("table_name") == "user":
|
||||
return MagicMock(models=[], spend=0)
|
||||
return MagicMock(
|
||||
token="hashed_token_456",
|
||||
litellm_budget_table=None,
|
||||
object_permission=None,
|
||||
)
|
||||
|
||||
mock_prisma_client.insert_data = AsyncMock(side_effect=_insert_data_side_effect)
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.prisma_client", mock_prisma_client)
|
||||
monkeypatch.setattr(
|
||||
"litellm.proxy.management_endpoints.key_management_endpoints.get_ui_settings_cached",
|
||||
AsyncMock(return_value={}),
|
||||
)
|
||||
|
||||
from litellm.proxy._types import GenerateKeyRequest, LitellmUserRoles
|
||||
from litellm.proxy.auth.user_api_key_auth import UserAPIKeyAuth
|
||||
from litellm.proxy.management_endpoints.key_management_endpoints import (
|
||||
generate_key_fn,
|
||||
)
|
||||
|
||||
raw_key = "sk-short-secret"
|
||||
with caplog.at_level(logging.DEBUG, logger="LiteLLM Proxy"):
|
||||
await generate_key_fn(
|
||||
data=GenerateKeyRequest(key=raw_key),
|
||||
user_api_key_dict=UserAPIKeyAuth(
|
||||
user_role=LitellmUserRoles.PROXY_ADMIN,
|
||||
api_key="sk-1234",
|
||||
user_id="user-1",
|
||||
),
|
||||
)
|
||||
|
||||
log_text = "\n".join(record.getMessage() for record in caplog.records)
|
||||
assert raw_key not in log_text
|
||||
assert hashlib.sha256(raw_key.encode()).hexdigest() in log_text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize(
|
||||
"field,request_kwargs,expected_in_error",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from __future__ import annotations
|
|||
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
from types import SimpleNamespace
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
|
@ -103,6 +104,35 @@ async def test_insert_data_user_organization_fk_raises_400(
|
|||
assert raised.status_code == 400
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_insert_data_debug_log_hashes_token(
|
||||
prisma_client: PrismaClient, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""Regression for LIT-4356: the raw virtual key must never reach a logger,
|
||||
even for short/nonstandard key formats that bypass the regex-based
|
||||
SecretRedactionFilter."""
|
||||
token = "sk-short-secret"
|
||||
expected_hash = hashlib.sha256(token.encode()).hexdigest()
|
||||
prisma_client.db.litellm_verificationtoken.upsert = AsyncMock(return_value=SimpleNamespace(token=expected_hash))
|
||||
with caplog.at_level(logging.DEBUG, logger="LiteLLM Proxy"):
|
||||
await prisma_client.insert_data(data={"token": token, "key_alias": "redaction-repro"}, table_name="key")
|
||||
log_text = "\n".join(record.getMessage() for record in caplog.records)
|
||||
assert token not in log_text
|
||||
assert expected_hash in log_text
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_insert_data_debug_log_tolerates_none_token(
|
||||
prisma_client: PrismaClient, caplog: pytest.LogCaptureFixture
|
||||
) -> None:
|
||||
"""A None token must not crash the redacting debug log added for LIT-4356."""
|
||||
prisma_client.db.litellm_usertable.upsert = AsyncMock(return_value=SimpleNamespace(user_id="u1"))
|
||||
with caplog.at_level(logging.DEBUG, logger="LiteLLM Proxy"):
|
||||
result = await prisma_client.insert_data(data={"user_id": "u1", "token": None}, table_name="user")
|
||||
assert result.user_id == "u1"
|
||||
assert any("insert_data" in record.getMessage() for record in caplog.records)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_insert_data_logs_and_raises_generic_error(
|
||||
prisma_client: PrismaClient,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue