From 1da16cfc572c37e4c85aacbf073d7a2376b8c700 Mon Sep 17 00:00:00 2001 From: Junghwan <70629228+shaun0927@users.noreply.github.com> Date: Thu, 16 Apr 2026 17:49:19 +0900 Subject: [PATCH] fix: harden voice prompt cache loading and SPA path guard (#429) Two small safety improvements: 1. Voice prompt cache (cache.py): add weights_only=True to torch.load() so cached .prompt files are loaded using the safe unpickler instead of the unrestricted pickle deserializer. This follows the PyTorch 2.6+ best practice of opting in to safe loading for all torch.load() calls. 2. SPA catch-all (app.py): replace str.startswith() path guard with Path.is_relative_to(). The string prefix check passes for sibling paths like /app/frontend_evil/ that share the /app/frontend prefix. is_relative_to() correctly tests directory containment. --- backend/app.py | 2 +- backend/utils/cache.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app.py b/backend/app.py index 1293460a..b19d1bf4 100644 --- a/backend/app.py +++ b/backend/app.py @@ -135,7 +135,7 @@ def _mount_frontend(application: FastAPI) -> None: async def serve_spa(full_path: str): file_path = (frontend_dir / full_path).resolve() # Guard against path traversal — only serve files inside frontend_dir - if full_path and file_path.is_file() and str(file_path).startswith(str(frontend_dir)): + if full_path and file_path.is_file() and file_path.is_relative_to(frontend_dir): return FileResponse(file_path) return FileResponse(frontend_dir / "index.html", media_type="text/html") diff --git a/backend/utils/cache.py b/backend/utils/cache.py index cace2bdd..dd4b9f83 100644 --- a/backend/utils/cache.py +++ b/backend/utils/cache.py @@ -64,7 +64,7 @@ def get_cached_voice_prompt( cache_file = _get_cache_dir() / f"{cache_key}.prompt" if cache_file.exists(): try: - prompt = torch.load(cache_file) + prompt = torch.load(cache_file, weights_only=True) _memory_cache[cache_key] = prompt return prompt except Exception: