From 96395c146979b406ba8a797b5d3402f426ee95ca Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 14 Mar 2026 21:12:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E9=9D=99=E6=80=81=E6=96=87=E4=BB=B6API=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/plugin.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/api/endpoints/plugin.py b/app/api/endpoints/plugin.py index 50fa36bf..831c1ee8 100644 --- a/app/api/endpoints/plugin.py +++ b/app/api/endpoints/plugin.py @@ -360,7 +360,18 @@ async def plugin_static_file(plugin_id: str, filepath: str): raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden") plugin_base_dir = AsyncPath(settings.ROOT_PATH) / "app" / "plugins" / plugin_id.lower() - plugin_file_path = plugin_base_dir / filepath + plugin_file_path = plugin_base_dir / filepath.lstrip('/') + + try: + resolved_base = await plugin_base_dir.resolve() + resolved_file = await plugin_file_path.resolve() + except Exception: + raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid path") + + if not resolved_file.is_relative_to(resolved_base): + logger.warning(f"Static File API: Path traversal attempt detected: {plugin_id}/{filepath}") + raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Forbidden") + if not await plugin_file_path.exists(): raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"{plugin_file_path} 不存在") if not await plugin_file_path.is_file():