T09 · Insecure Skill Coding Practices
- Location
lib/long_task_manager.py:110- Finding
Path Traversal Through Unvalidated Task and Agent Identifiers
- Content
View full analysis
Vulnerability Details
File Location:
lib/long_task_manager.py, lines 110–124; additional sinks at lines 151–164, 189–192, 204–209, 222–227, 300, and 344–345
Vulnerability Type: Unrestricted filesystem path construction / path traversal
Risk Level: HighVulnerable Code
python def _get_task_path(self, task_id: str) -> Path: """获取任务目录""" return self.task_dir / task_id def _get_config_path(self, task_id: str) -> Path: """获取配置文件路径""" return self._get_task_path(task_id) / "config.json" def _get_status_path(self, task_id: str) -> Path: """获取状态文件路径""" return self._get_task_path(task_id) / "status.json" def _get_result_path(self, task_id: str) -> Path: """获取结果文件路径""" return self._get_task_path(task_id) / "result.json"The unvalidated paths are used by submission and file-access operations:
python # 生成任务ID task_id = self._generate_task_id(agent_id) task_path = self._get_task_path(task_id) task_path.mkdir(parents=True, exist_ok=True) # 保存配置 config = { "task_id": task_id, "agent_id": agent_id, "priority": priority, "config": task_config, "submitted_at": datetime.now().isoformat(), "status": TaskStatus.PENDING.value } with open(self._get_config_path(task_id), 'w', encoding='utf-8') as f: json.dump(config, f, indent=2, ensure_ascii=False)python def get_status(self, task_id: str) -> Optional[Dict]: status_path = self._get_status_path(task_id) if not status_path.exists(): return None with open(status_path, 'r', encoding='utf-8') as f: return json.load(f)python def get_result(self, task_id: str) -> Optional[Dict]: result_path = self._get_result_path(task_id) if not result_path.exists(): return None with open(result_path, 'r', encoding='utf-8') as f: return json.load(f)python with open(self._get_result_path(task_id), 'w', encoding='utf-8') as f: json.dump(asdict(result_data), f, inden ...[truncated 3497 chars]- Remediation
View remediation
Remediation Suggestions
- Generate opaque task identifiers internally. Do not incorporate
agent_idinto a filesystem path. Use a UUID as the directory name and retain the agent identifier only inside metadata.
python def _generate_task_id(self, agent_id: str) -> str: return uuid.uuid4().hex- Validate every externally supplied identifier. If readable identifiers are required, enforce a strict allowlist and length limit:
python import re _TASK_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]{1,128}$") def _validate_task_id(task_id: str) -> str: if not isinstance(task_id, str) or not _TASK_ID_PATTERN.fullmatch(task_id): raise ValueError("Invalid task ID") return task_id- Enforce canonical path containment. Resolve both the base and destination and reject any path outside the configured root:
python def _get_task_path(self, task_id: str) -> Path: task_id = _validate_task_id(task_id) root = self.task_dir.resolve() candidate = (root / task_id).resolve() if candidate.parent != root: raise ValueError("Task path escapes the configured task directory") return candidate-
Apply validation consistently to all public methods accepting
task_id, includingget_status(),get_result(),update_progress(),complete(),fail(),cancel(), callback registration, worker initialization, and convenience functions. -
Reject absolute paths and identifiers containing path separators,
.or..path components, null bytes, or platform-specific separator variants. -
Create the task root with restrictive permissions and ensure task files are not writable by untrusted local users. Where concurrent or adversarial local access is possible, also defend against symbolic-link substitution and time-of-check/time-of-use races by using secure directory handles and no-follow semantics.
-
Add tests covering Unix and Windows traversal forms, absolute paths, nested separators, symbolic links, and ...[truncated 56 chars]
- Generate opaque task identifiers internally. Do not incorporate
