diff --git a/acestep/api/train_api_service.py b/acestep/api/train_api_service.py index f22e9649..e370b747 100644 --- a/acestep/api/train_api_service.py +++ b/acestep/api/train_api_service.py @@ -174,10 +174,21 @@ async def export_lora(request: ExportLoRARequest, _: None = Depends(verify_api_k try: export_path = request.export_path.strip() - os.makedirs(os.path.dirname(export_path) if os.path.dirname(export_path) else ".", exist_ok=True) + # Constrain export_path to a safe base directory and prevent path traversal + safe_base = os.path.abspath("./exports") + # Strip leading separators to prevent os.path.join from treating input as absolute + export_path = export_path.lstrip("/").lstrip("\\") + export_path = os.path.abspath(os.path.join(safe_base, export_path)) + if not export_path.startswith(safe_base + os.sep) and export_path != safe_base: + raise HTTPException(status_code=400, detail="Invalid export path") + + os.makedirs(os.path.dirname(export_path), exist_ok=True) if os.path.exists(export_path): shutil.rmtree(export_path) shutil.copytree(source_path, export_path) return wrap_response({"message": "LoRA exported successfully", "export_path": export_path, "source": source_path}) + except HTTPException: + # Re-raise HTTP exceptions to preserve intended status codes + raise except Exception as exc: return wrap_response(None, code=500, error=f"Export failed: {exc}")