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:
Classic298
2026-04-12 19:29:45 +02:00
committed by GitHub
parent 4498c21f4c
commit a2a9a3a42a

View File

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