mirror of
https://github.com/open-webui/open-webui.git
synced 2026-05-06 02:48:13 -05:00
fix: prevent path traversal via model name in Azure deployment URLs (#23629)
The model name from user input was interpolated directly into Azure deployment URL paths without validation. A user could send a model name like '../../management/foo' to traverse the URL path and hit unintended Azure endpoints with the admin's API key. Adds _sanitize_model_for_url that rejects path separators and traversal sequences, and percent-encodes the name. Applied at convert_to_azure_payload (covers chat completions + proxy) and the responses endpoint's direct URL construction.
This commit is contained in:
@@ -4,7 +4,7 @@ import json
|
||||
import logging
|
||||
import re
|
||||
from typing import Optional
|
||||
from urllib.parse import urlparse
|
||||
from urllib.parse import quote, urlparse
|
||||
|
||||
import aiohttp
|
||||
from aiocache import cached
|
||||
@@ -772,6 +772,21 @@ def is_openai_new_model(model: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _sanitize_model_for_url(model: str) -> str:
|
||||
"""Sanitize a model name before interpolating it into a URL path.
|
||||
|
||||
Rejects path traversal attempts (../, /, \\) and percent-encodes
|
||||
the name so it is safe to use as a single URL path segment
|
||||
(e.g. Azure deployment name).
|
||||
"""
|
||||
if not model or '..' in model or '/' in model or '\\' in model:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail='Invalid model name: must not be empty or contain path separators or traversal sequences',
|
||||
)
|
||||
return quote(model, safe='')
|
||||
|
||||
|
||||
def convert_to_azure_payload(url, payload: dict, api_version: str):
|
||||
model = payload.get('model', '')
|
||||
|
||||
@@ -795,6 +810,9 @@ def convert_to_azure_payload(url, payload: dict, api_version: str):
|
||||
# Filter out unsupported parameters
|
||||
payload = {k: v for k, v in payload.items() if k in allowed_params}
|
||||
|
||||
# Sanitize model name to prevent path traversal in the deployment URL
|
||||
model = _sanitize_model_for_url(model)
|
||||
|
||||
url = f'{url}/openai/deployments/{model}'
|
||||
return url, payload
|
||||
|
||||
@@ -1364,7 +1382,7 @@ async def responses(
|
||||
else:
|
||||
api_version = api_config.get('api_version', '2023-03-15-preview')
|
||||
headers['api-version'] = api_version
|
||||
model = payload.get('model', '')
|
||||
model = _sanitize_model_for_url(payload.get('model', ''))
|
||||
request_url = f'{url}/openai/deployments/{model}/responses?api-version={api_version}'
|
||||
else:
|
||||
request_url = f'{url}/responses'
|
||||
@@ -1404,6 +1422,8 @@ async def responses(
|
||||
|
||||
return response_data
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
raise HTTPException(
|
||||
@@ -1515,6 +1535,8 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
||||
|
||||
return response_data
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
raise HTTPException(
|
||||
|
||||
Reference in New Issue
Block a user