refactor: implement safe_relative_path utility for secure file handling
This commit is contained in:
@@ -12,6 +12,7 @@ from app.api.routers.task_runtime import set_task_stage
|
|||||||
from app.config import AppConfig, CACHE_DIR
|
from app.config import AppConfig, CACHE_DIR
|
||||||
from app.processor import ProcessLogger
|
from app.processor import ProcessLogger
|
||||||
from app.services.cell_data import CellDataProcessor, refresh_cell_data
|
from app.services.cell_data import CellDataProcessor, refresh_cell_data
|
||||||
|
from app.utils.files import safe_relative_path
|
||||||
|
|
||||||
router = APIRouter(tags=["cell-data"])
|
router = APIRouter(tags=["cell-data"])
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ async def upload_and_start_cell_data_processing(files: list[UploadFile] = File(.
|
|||||||
for file in files:
|
for file in files:
|
||||||
if not file.filename:
|
if not file.filename:
|
||||||
continue
|
continue
|
||||||
target = upload_dir / file.filename.replace("\\", "/")
|
target = upload_dir / safe_relative_path(file.filename)
|
||||||
target.parent.mkdir(parents=True, exist_ok=True)
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
target.write_bytes(await file.read())
|
target.write_bytes(await file.read())
|
||||||
saved_count += 1
|
saved_count += 1
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from fastapi import APIRouter, File, HTTPException, UploadFile
|
|||||||
|
|
||||||
from app import state
|
from app import state
|
||||||
from app.config import CACHE_DIR
|
from app.config import CACHE_DIR
|
||||||
|
from app.utils.files import safe_relative_path
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(tags=["upload"])
|
router = APIRouter(tags=["upload"])
|
||||||
@@ -70,12 +71,14 @@ async def upload_files(
|
|||||||
if not file.filename:
|
if not file.filename:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
file_path = work_dir / file.filename
|
relative_path = safe_relative_path(file.filename)
|
||||||
|
file_path = work_dir / relative_path
|
||||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
file_path.write_bytes(await file.read())
|
file_path.write_bytes(await file.read())
|
||||||
|
|
||||||
saved_files.append(file.filename)
|
saved_name = str(relative_path).replace("\\", "/")
|
||||||
session["files"].append(file.filename)
|
saved_files.append(saved_name)
|
||||||
|
session["files"].append(saved_name)
|
||||||
|
|
||||||
record = state.history_manager.get(session_id)
|
record = state.history_manager.get(session_id)
|
||||||
if record:
|
if record:
|
||||||
@@ -107,4 +110,3 @@ async def complete_upload_session(session_id: str):
|
|||||||
state.history_manager.update(session_id, file_count=len(session["files"]))
|
state.history_manager.update(session_id, file_count=len(session["files"]))
|
||||||
|
|
||||||
return {"success": True, "session_id": session_id, "total_files": len(session["files"])}
|
return {"success": True, "session_id": session_id, "total_files": len(session["files"])}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,17 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def safe_relative_path(value: str, fallback: str = "upload.bin") -> Path:
|
||||||
|
parts = [
|
||||||
|
part
|
||||||
|
for part in str(value or "").replace("\\", "/").split("/")
|
||||||
|
if part and part not in {".", ".."}
|
||||||
|
]
|
||||||
|
if not parts:
|
||||||
|
parts = [fallback]
|
||||||
|
return Path(*parts)
|
||||||
|
|
||||||
|
|
||||||
def remove_file_safely(path: Path) -> None:
|
def remove_file_safely(path: Path) -> None:
|
||||||
try:
|
try:
|
||||||
path.unlink()
|
path.unlink()
|
||||||
|
|||||||
Reference in New Issue
Block a user