fix(security): sanitize user-supplied IDs to prevent path traversal

simulation_id, project_id, report_id, and platform parameters from
API requests are used directly in os.path.join() to construct file
paths. An attacker can use values like "../../etc" to read/write
files or create directories outside the intended data directory.

Added validation: reject any ID that differs from its os.path.basename(),
which catches path separators and traversal sequences.
This commit is contained in:
warren618 2026-03-23 02:50:12 +08:00
parent 1536a79334
commit 56789a2c98
3 changed files with 16 additions and 4 deletions

View File

@ -112,7 +112,10 @@ class ProjectManager:
@classmethod
def _get_project_dir(cls, project_id: str) -> str:
"""获取项目目录路径"""
return os.path.join(cls.PROJECTS_DIR, project_id)
safe_id = os.path.basename(project_id)
if not safe_id or safe_id != project_id:
raise ValueError(f"Invalid project_id: {project_id}")
return os.path.join(cls.PROJECTS_DIR, safe_id)
@classmethod
def _get_project_meta_path(cls, project_id: str) -> str:

View File

@ -1909,7 +1909,10 @@ class ReportManager:
@classmethod
def _get_report_folder(cls, report_id: str) -> str:
"""获取报告文件夹路径"""
return os.path.join(cls.REPORTS_DIR, report_id)
safe_id = os.path.basename(report_id)
if not safe_id or safe_id != report_id:
raise ValueError(f"Invalid report_id: {report_id}")
return os.path.join(cls.REPORTS_DIR, safe_id)
@classmethod
def _ensure_report_folder(cls, report_id: str) -> str:

View File

@ -137,7 +137,10 @@ class SimulationManager:
def _get_simulation_dir(self, simulation_id: str) -> str:
"""获取模拟数据目录"""
sim_dir = os.path.join(self.SIMULATION_DATA_DIR, simulation_id)
safe_id = os.path.basename(simulation_id)
if not safe_id or safe_id != simulation_id:
raise ValueError(f"Invalid simulation_id: {simulation_id}")
sim_dir = os.path.join(self.SIMULATION_DATA_DIR, safe_id)
os.makedirs(sim_dir, exist_ok=True)
return sim_dir
@ -484,7 +487,10 @@ class SimulationManager:
raise ValueError(f"模拟不存在: {simulation_id}")
sim_dir = self._get_simulation_dir(simulation_id)
profile_path = os.path.join(sim_dir, f"{platform}_profiles.json")
safe_platform = os.path.basename(platform)
if not safe_platform or safe_platform != platform:
raise ValueError(f"Invalid platform: {platform}")
profile_path = os.path.join(sim_dir, f"{safe_platform}_profiles.json")
if not os.path.exists(profile_path):
return []