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 logging
|
||||||
import re
|
import re
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import quote, urlparse
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from aiocache import cached
|
from aiocache import cached
|
||||||
@@ -772,6 +772,21 @@ def is_openai_new_model(model: str) -> bool:
|
|||||||
return False
|
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):
|
def convert_to_azure_payload(url, payload: dict, api_version: str):
|
||||||
model = payload.get('model', '')
|
model = payload.get('model', '')
|
||||||
|
|
||||||
@@ -795,6 +810,9 @@ def convert_to_azure_payload(url, payload: dict, api_version: str):
|
|||||||
# Filter out unsupported parameters
|
# Filter out unsupported parameters
|
||||||
payload = {k: v for k, v in payload.items() if k in allowed_params}
|
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}'
|
url = f'{url}/openai/deployments/{model}'
|
||||||
return url, payload
|
return url, payload
|
||||||
|
|
||||||
@@ -1364,7 +1382,7 @@ async def responses(
|
|||||||
else:
|
else:
|
||||||
api_version = api_config.get('api_version', '2023-03-15-preview')
|
api_version = api_config.get('api_version', '2023-03-15-preview')
|
||||||
headers['api-version'] = api_version
|
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}'
|
request_url = f'{url}/openai/deployments/{model}/responses?api-version={api_version}'
|
||||||
else:
|
else:
|
||||||
request_url = f'{url}/responses'
|
request_url = f'{url}/responses'
|
||||||
@@ -1404,6 +1422,8 @@ async def responses(
|
|||||||
|
|
||||||
return response_data
|
return response_data
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
@@ -1515,6 +1535,8 @@ async def proxy(path: str, request: Request, user=Depends(get_verified_user)):
|
|||||||
|
|
||||||
return response_data
|
return response_data
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception(e)
|
log.exception(e)
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
|
|||||||
Reference in New Issue
Block a user