mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-03 02:22:24 +00:00
fix(mavvrik): restrict api_endpoint to mavvrik.dev and mavvrik.ai domains
Prevents SSRF by validating the netloc ends with .mavvrik.dev or .mavvrik.ai in both MavvrikInitRequest and MavvrikSettingsUpdate validators. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
388e2a0c08
commit
d0dcc2e76e
2 changed files with 21 additions and 4 deletions
|
|
@ -3,9 +3,12 @@ Mavvrik endpoint Pydantic models for LiteLLM Proxy admin API.
|
|||
"""
|
||||
|
||||
from typing import Any, Dict, Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
_MAVVRIK_ALLOWED_SUFFIXES = (".mavvrik.dev", ".mavvrik.ai", ".mavvrik.app")
|
||||
|
||||
|
||||
class MavvrikInitRequest(BaseModel):
|
||||
"""Request body for POST /mavvrik/init — stores encrypted settings in LiteLLM_Config."""
|
||||
|
|
@ -31,9 +34,15 @@ class MavvrikInitRequest(BaseModel):
|
|||
|
||||
@field_validator("api_endpoint")
|
||||
@classmethod
|
||||
def must_be_https(cls, v: str) -> str:
|
||||
def must_be_https_mavvrik_host(cls, v: str) -> str:
|
||||
if not v.startswith("https://"):
|
||||
raise ValueError("api_endpoint must be an HTTPS URL")
|
||||
netloc = urlparse(v).netloc.split(":")[0] # strip port if present
|
||||
if not any(netloc.endswith(suffix) for suffix in _MAVVRIK_ALLOWED_SUFFIXES):
|
||||
raise ValueError(
|
||||
f"api_endpoint host must be a Mavvrik domain "
|
||||
f"(e.g. https://api.mavvrik.dev/...)"
|
||||
)
|
||||
return v
|
||||
|
||||
|
||||
|
|
@ -138,7 +147,15 @@ class MavvrikSettingsUpdate(BaseModel):
|
|||
|
||||
@field_validator("api_endpoint")
|
||||
@classmethod
|
||||
def must_be_https_if_set(cls, v: Optional[str]) -> Optional[str]:
|
||||
if v is not None and not v.startswith("https://"):
|
||||
def must_be_https_mavvrik_host_if_set(cls, v: Optional[str]) -> Optional[str]:
|
||||
if v is None:
|
||||
return v
|
||||
if not v.startswith("https://"):
|
||||
raise ValueError("api_endpoint must be an HTTPS URL")
|
||||
netloc = urlparse(v).netloc.split(":")[0]
|
||||
if not any(netloc.endswith(suffix) for suffix in _MAVVRIK_ALLOWED_SUFFIXES):
|
||||
raise ValueError(
|
||||
f"api_endpoint host must be a Mavvrik domain "
|
||||
f"(e.g. https://api.mavvrik.dev/...)"
|
||||
)
|
||||
return v
|
||||
|
|
|
|||
|
|
@ -745,7 +745,7 @@ class TestAdminGate:
|
|||
from fastapi import HTTPException
|
||||
|
||||
req = MavvrikInitRequest(
|
||||
api_key="k", api_endpoint="https://e.com/t", connection_id="c"
|
||||
api_key="k", api_endpoint="https://api.mavvrik.dev/t", connection_id="c"
|
||||
)
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await init_mavvrik_settings(req, user_api_key_dict=_non_admin_user())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue