diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index 06178d385c..1699129f2a 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -3947,6 +3947,19 @@ AUDIO_STT_SUPPORTED_CONTENT_TYPES = PersistentConfig( ], ) +AUDIO_STT_ALLOWED_EXTENSIONS = PersistentConfig( + 'AUDIO_STT_ALLOWED_EXTENSIONS', + 'audio.stt.allowed_extensions', + [ + ext.strip() + for ext in os.environ.get( + 'AUDIO_STT_ALLOWED_EXTENSIONS', + 'mp3,wav,m4a,webm,ogg,flac,mp4,mpga,mpeg', + ).split(',') + if ext.strip() + ], +) + AUDIO_STT_AZURE_API_KEY = PersistentConfig( 'AUDIO_STT_AZURE_API_KEY', 'audio.stt.azure.api_key', diff --git a/backend/open_webui/main.py b/backend/open_webui/main.py index ba503b40a9..e4ee823b4f 100644 --- a/backend/open_webui/main.py +++ b/backend/open_webui/main.py @@ -199,6 +199,7 @@ from open_webui.config import ( AUDIO_STT_ENGINE, AUDIO_STT_MODEL, AUDIO_STT_SUPPORTED_CONTENT_TYPES, + AUDIO_STT_ALLOWED_EXTENSIONS, AUDIO_STT_OPENAI_API_BASE_URL, AUDIO_STT_OPENAI_API_KEY, AUDIO_STT_AZURE_API_KEY, @@ -1289,6 +1290,7 @@ app.state.config.IMAGES_EDIT_COMFYUI_WORKFLOW_NODES = IMAGES_EDIT_COMFYUI_WORKFL app.state.config.STT_ENGINE = AUDIO_STT_ENGINE app.state.config.STT_MODEL = AUDIO_STT_MODEL app.state.config.STT_SUPPORTED_CONTENT_TYPES = AUDIO_STT_SUPPORTED_CONTENT_TYPES +app.state.config.STT_ALLOWED_EXTENSIONS = AUDIO_STT_ALLOWED_EXTENSIONS app.state.config.STT_OPENAI_API_BASE_URL = AUDIO_STT_OPENAI_API_BASE_URL app.state.config.STT_OPENAI_API_KEY = AUDIO_STT_OPENAI_API_KEY @@ -2876,7 +2878,13 @@ async def serve_cache_file( raise HTTPException(status_code=404, detail='File not found') if not os.path.isfile(file_path): raise HTTPException(status_code=404, detail='File not found') - return FileResponse(file_path) + + mime, _ = mimetypes.guess_type(file_path) + inline_safe = mime and mime.split('/', 1)[0] in {'image', 'audio', 'video'} + headers = {'X-Content-Type-Options': 'nosniff'} + if not inline_safe: + headers['Content-Disposition'] = f'attachment; filename="{os.path.basename(file_path)}"' + return FileResponse(file_path, headers=headers) def swagger_ui_html(*args, **kwargs): diff --git a/backend/open_webui/routers/audio.py b/backend/open_webui/routers/audio.py index c69be124e5..c653a370e0 100644 --- a/backend/open_webui/routers/audio.py +++ b/backend/open_webui/routers/audio.py @@ -178,6 +178,7 @@ class STTConfigForm(BaseModel): ENGINE: str MODEL: str SUPPORTED_CONTENT_TYPES: list[str] = [] + ALLOWED_EXTENSIONS: list[str] = [] WHISPER_MODEL: str DEEPGRAM_API_KEY: str AZURE_API_KEY: str @@ -219,6 +220,7 @@ async def get_audio_config(request: Request, user=Depends(get_admin_user)): 'ENGINE': request.app.state.config.STT_ENGINE, 'MODEL': request.app.state.config.STT_MODEL, 'SUPPORTED_CONTENT_TYPES': request.app.state.config.STT_SUPPORTED_CONTENT_TYPES, + 'ALLOWED_EXTENSIONS': request.app.state.config.STT_ALLOWED_EXTENSIONS, 'WHISPER_MODEL': request.app.state.config.WHISPER_MODEL, 'DEEPGRAM_API_KEY': request.app.state.config.DEEPGRAM_API_KEY, 'AZURE_API_KEY': request.app.state.config.AUDIO_STT_AZURE_API_KEY, @@ -254,6 +256,7 @@ async def update_audio_config(request: Request, form_data: AudioConfigUpdateForm request.app.state.config.STT_ENGINE = form_data.stt.ENGINE request.app.state.config.STT_MODEL = form_data.stt.MODEL request.app.state.config.STT_SUPPORTED_CONTENT_TYPES = form_data.stt.SUPPORTED_CONTENT_TYPES + request.app.state.config.STT_ALLOWED_EXTENSIONS = form_data.stt.ALLOWED_EXTENSIONS request.app.state.config.WHISPER_MODEL = form_data.stt.WHISPER_MODEL request.app.state.config.DEEPGRAM_API_KEY = form_data.stt.DEEPGRAM_API_KEY @@ -295,6 +298,7 @@ async def update_audio_config(request: Request, form_data: AudioConfigUpdateForm 'ENGINE': request.app.state.config.STT_ENGINE, 'MODEL': request.app.state.config.STT_MODEL, 'SUPPORTED_CONTENT_TYPES': request.app.state.config.STT_SUPPORTED_CONTENT_TYPES, + 'ALLOWED_EXTENSIONS': request.app.state.config.STT_ALLOWED_EXTENSIONS, 'WHISPER_MODEL': request.app.state.config.WHISPER_MODEL, 'DEEPGRAM_API_KEY': request.app.state.config.DEEPGRAM_API_KEY, 'AZURE_API_KEY': request.app.state.config.AUDIO_STT_AZURE_API_KEY, @@ -1242,7 +1246,14 @@ async def transcription( try: safe_name = os.path.basename(file.filename) if file.filename else '' - ext = safe_name.rsplit('.', 1)[-1] if '.' in safe_name else '' + ext = safe_name.rsplit('.', 1)[-1].lower() if '.' in safe_name else '' + + allowed_extensions = getattr(request.app.state.config, 'STT_ALLOWED_EXTENSIONS', []) + if allowed_extensions and ext not in allowed_extensions: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail='Invalid audio file extension', + ) id = uuid.uuid4()