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:
Praveen Ghuge 2026-05-13 11:03:48 +05:30
parent 388e2a0c08
commit d0dcc2e76e
2 changed files with 21 additions and 4 deletions

View file

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

View file

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