mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
fix: address CodeQL realtime auth findings
This commit is contained in:
parent
7f3455272e
commit
a4accccac3
6 changed files with 20 additions and 8 deletions
|
|
@ -264,8 +264,8 @@ class Litellm_EntityType(enum.Enum):
|
|||
def hash_token(token: str):
|
||||
import hashlib
|
||||
|
||||
# Hash the string using SHA-256
|
||||
hashed_token: Final = hashlib.sha256(token.encode()).hexdigest()
|
||||
# This digest is an opaque lookup identifier, not a password hash.
|
||||
hashed_token: Final = hashlib.sha256(token.encode(), usedforsecurity=False).hexdigest()
|
||||
|
||||
return hashed_token
|
||||
|
||||
|
|
|
|||
|
|
@ -6024,7 +6024,9 @@ def is_model_allowed_by_pattern(model: str, allowed_model_pattern: str) -> bool:
|
|||
bool: True if model matches the pattern, False otherwise
|
||||
"""
|
||||
if "*" in allowed_model_pattern:
|
||||
pattern: Final = f"^{allowed_model_pattern.replace('*', '.*')}$"
|
||||
# Treat the configured model pattern as a glob; only '*' is special.
|
||||
escaped_pattern: Final = re.escape(allowed_model_pattern)
|
||||
pattern: Final = "^" + escaped_pattern.replace("\\*", ".*") + "$"
|
||||
return bool(re.match(pattern, model))
|
||||
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -218,7 +218,10 @@ def decode_call(token: str, authorization: str) -> CodexRealtimeCall:
|
|||
call: Final = CodexRealtimeCall.model_validate_json(plaintext or "")
|
||||
except (ValueError, TypeError, UnicodeError) as exc:
|
||||
raise HTTPException(403, "Invalid realtime call") from exc
|
||||
if call.expires_at < time.time() or call.owner != hashlib.sha256(authorization.encode()).hexdigest():
|
||||
if (
|
||||
call.expires_at < time.time()
|
||||
or call.owner != hashlib.sha256(authorization.encode(), usedforsecurity=False).hexdigest()
|
||||
):
|
||||
raise HTTPException(403, "Invalid or expired realtime call")
|
||||
return call
|
||||
|
||||
|
|
@ -231,6 +234,7 @@ async def _cache_bounded_offer_body(request: Request) -> None:
|
|||
if int(request.headers.get("content-length", "")) > MAX_REALTIME_OFFER_BYTES:
|
||||
raise HTTPException(413, "Realtime offer exceeds the 8 MiB limit")
|
||||
except ValueError:
|
||||
# A missing or non-numeric content length is checked while streaming below.
|
||||
pass
|
||||
if hasattr(request, "_body"):
|
||||
if len(request._body) > MAX_REALTIME_OFFER_BYTES: # pyright: ignore[reportPrivateUsage] # validate Starlette's cached body without consuming it again
|
||||
|
|
@ -396,7 +400,9 @@ async def _create_codex_realtime_call(request: Request) -> Response:
|
|||
call: Final = parse_call_response(
|
||||
response,
|
||||
alias=model,
|
||||
owner=hashlib.sha256(f"Bearer {owner_key}".encode()).hexdigest(),
|
||||
owner=hashlib.sha256(
|
||||
f"Bearer {owner_key}".encode(), usedforsecurity=False
|
||||
).hexdigest(),
|
||||
expires_at=time.time() + 3600,
|
||||
)
|
||||
except ValueError as exc:
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ def rewrite_session_ids(value: JsonValue | Mapping[str, JsonValue], raw_id: str,
|
|||
def _owner(auth: UserAPIKeyAuth) -> str:
|
||||
if not auth.api_key:
|
||||
raise HTTPException(403, "Live sessions require an authenticated API key")
|
||||
return hashlib.sha256(auth.api_key.encode()).hexdigest()
|
||||
return hashlib.sha256(auth.api_key.encode(), usedforsecurity=False).hexdigest()
|
||||
|
||||
|
||||
async def _auth(request: Request) -> UserAPIKeyAuth:
|
||||
|
|
@ -1475,11 +1475,13 @@ async def websocket_live_session(websocket: WebSocket, session_id: str | None =
|
|||
try:
|
||||
await websocket.close(code=1008, reason="Live session rejected")
|
||||
except RuntimeError:
|
||||
# The peer may have closed the socket before the rejection response.
|
||||
pass
|
||||
except Exception:
|
||||
try:
|
||||
await websocket.close(code=1011, reason="Live upstream connection failed")
|
||||
except RuntimeError:
|
||||
# The peer may have closed the socket before the failure response.
|
||||
pass
|
||||
finally:
|
||||
if state.connection is not None:
|
||||
|
|
|
|||
|
|
@ -4291,7 +4291,7 @@ class PrismaClient:
|
|||
|
||||
def hash_token(self, token: str):
|
||||
# Hash the string using SHA-256
|
||||
hashed_token: Final = hashlib.sha256(token.encode()).hexdigest()
|
||||
hashed_token: Final = hashlib.sha256(token.encode(), usedforsecurity=False).hexdigest()
|
||||
|
||||
return hashed_token
|
||||
|
||||
|
|
@ -6718,7 +6718,7 @@ def hash_token(token: str):
|
|||
import hashlib
|
||||
|
||||
# Hash the string using SHA-256
|
||||
hashed_token: Final = hashlib.sha256(token.encode()).hexdigest()
|
||||
hashed_token: Final = hashlib.sha256(token.encode(), usedforsecurity=False).hexdigest()
|
||||
|
||||
return hashed_token
|
||||
|
||||
|
|
|
|||
|
|
@ -547,6 +547,8 @@ async def test_virtual_key_max_budget_check(
|
|||
False,
|
||||
), # don't match on pattern
|
||||
("openai/gpt-4o", ["openai/*"], True), # openai wildcard access
|
||||
("openai/gpt+4", ["openai/gpt+*"], True), # regex metacharacters stay literal
|
||||
("openai/gpttt4", ["openai/gpt+*"], False), # regex metacharacters do not overmatch
|
||||
("gpt-4", ["gpt-3.5-turbo"], False), # model not in allowed list
|
||||
("claude-3", [], True), # empty model list (allows all)
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue