{"record":{"id":"72a10bd1392bd14a","repo":"BerriAI/litellm","slug":"mavvrik-focus-destination-label-must-be-a-gcs-e","errorCode":null,"errorMessage":"Mavvrik FOCUS destination: {label} must be a GCS endpoint (storage.googleapis.com), got '{hostname}'","messagePattern":"Mavvrik FOCUS destination: (.+?) must be a GCS endpoint \\(storage\\.googleapis\\.com\\), got '(.+?)'","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/integrations/focus/destinations/mavvrik_destination.py","lineNumber":47,"sourceCode":"\n\ndef _validate_api_endpoint(api_endpoint: str) -> None:\n    if not api_endpoint.startswith(\"https://\"):\n        raise ValueError(\"MAVVRIK_API_ENDPOINT must be an HTTPS URL\")\n    hostname: Final = (urlparse(api_endpoint).hostname or \"\").lower()\n    if not any(hostname.endswith(suffix) for suffix in _MAVVRIK_ALLOWED_SUFFIXES):\n        raise ValueError(\n            \"MAVVRIK_API_ENDPOINT host must be a Mavvrik domain (e.g. https://api.mavvrik.dev/<tenant_id>)\"\n        )\n\n\ndef _validate_gcs_url(url: str, label: str) -> None:\n    parsed: Final = urlparse(url)\n    if parsed.scheme != \"https\":\n        raise ValueError(f\"Mavvrik FOCUS destination: {label} must be HTTPS, got scheme '{parsed.scheme}'\")\n    hostname: Final = (parsed.hostname or \"\").lower()\n    if not (hostname == \"storage.googleapis.com\" or hostname.endswith(\".storage.googleapis.com\")):\n        raise ValueError(\n            f\"Mavvrik FOCUS destination: {label} must be a GCS endpoint (storage.googleapis.com), got '{hostname}'\"\n        )\n\n\nclass FocusMavvrikDestination(FocusDestination):\n    \"\"\"Upload FOCUS CSV exports to Mavvrik via GCS signed URL.\"\"\"\n\n    def __init__(\n        self,\n        *,\n        prefix: str,\n        config: dict[str, Any] | None = None,\n    ) -> None:\n        config = config or {}\n        api_key: Final = config.get(\"api_key\")\n        api_endpoint: Final = config.get(\"api_endpoint\")\n        connection_id: Final = config.get(\"connection_id\")\n","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/integrations/focus/destinations/mavvrik_destination.py#L29-L65","documentation":"The second _validate_gcs_url() check requires the hostname of a Mavvrik-provided URL to be exactly storage.googleapis.com or a subdomain ending in .storage.googleapis.com. Any other host (e.g. a different cloud provider, an attacker domain, or a CNAME alias) is rejected, preventing the gzip'd FOCUS data from being exfiltrated to a third party.","triggerScenarios":"The signed URL or resumable-session Location returned by the Mavvrik API points to a non-GCS host, e.g. https://uploads.mavvrik.dev/signed or an S3 URL; validation fires inside _get_signed_url or after session init.","commonSituations":"Mavvrik migrating their upload infrastructure to a proxy/CNAME not under storage.googleapis.com; a misconfigured or compromised Mavvrik tenant; API response shape change putting a dashboard URL in the 'url' field.","solutions":["Verify your MAVVRIK_API_ENDPOINT is an official domain and the tenant/connection_id are correct (misrouted tenants can return odd URLs).","Report to Mavvrik support if the URL legitimately should be a proxy domain — the allow-list only accepts storage.googleapis.com.","Upgrade LiteLLM in case the allow-list was updated for new Mavvrik infrastructure.","Treat repeated occurrences as a potential security event and audit what host was returned."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"from urllib.parse import urlparse\n\ndef assert_gcs_host(url: str, label: str) -> None:\n    host = (urlparse(url).hostname or \"\").lower()\n    if host != \"storage.googleapis.com\" and not host.endswith(\".storage.googleapis.com\"):\n        raise SecurityAlert(f\"{label} points at non-GCS host '{host}' — refusing upload\")","typeGuard":null,"tryCatchPattern":"try:\n    await dest.deliver(content=csv, time_window=tw, filename=\"f.csv\")\nexcept (RuntimeError, ValueError) as e:\n    if \"must be a GCS endpoint\" in str(e):\n        security_alert(\"Mavvrik redirecting FOCUS exports off Google Cloud Storage\")\n    raise","preventionTips":["Log the rejected hostname whenever this fires — it identifies where data would have been sent.","Stay on a supported LiteLLM version so allow-list updates for legitimate Mavvrik infra changes arrive."],"tags":["focus","mavvrik","gcs","security","ssrf","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}