fix: address CodeQL realtime auth findings

This commit is contained in:
jibanez-staticduo 2026-09-18 14:39:09 +02:00
parent 7f3455272e
commit a4accccac3
No known key found for this signature in database
6 changed files with 20 additions and 8 deletions

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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:

View file

@ -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

View file

@ -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)
],