T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/engine/task_store.py:33- Finding
Path Traversal Through Unvalidated Task and Subtask Identifiers
- Content
View full analysis
Vulnerability Details
File Location:
scripts/engine/task_store.py:33-35, 68-74, 199-208, 317-329, 353-366; input entry points inscripts/task_engine.py:788-789, 826-827, 840-841
Vulnerability Type: Path traversal and unrestricted filesystem access
Risk Level: HighVulnerable Code
python def get_task_dir(task_id: str) -> Path: return get_tasks_dir() / task_idpython @contextmanager def task_lock(task_id: str): """Per-task file lock. Uses fcntl on Unix, msvcrt on Windows.""" lock_dir = get_tasks_dir() / ".locks" lock_dir.mkdir(parents=True, exist_ok=True) lock_path = lock_dir / f"{task_id}.lock" fd = open(lock_path, "w")python def read_task(task_id: str) -> Optional[Task]: """Read a task from its task.json.""" task_dir = get_task_dir(task_id) task_path = task_dir / "task.json" if not task_path.exists(): # Check archive archive_path = get_archive_dir() / task_id / "task.json" if archive_path.exists(): data = json.loads(archive_path.read_text(encoding="utf-8")) return Task.from_dict(data) return None data = json.loads(task_path.read_text(encoding="utf-8")) return Task.from_dict(data)python def read_subtask(task_id: str, subtask_id: str) -> Optional[Subtask]: """Read a subtask JSON.""" task_dir = get_task_dir(task_id) path = task_dir / f"{subtask_id}.json" if not path.exists(): return None data = json.loads(path.read_text(encoding="utf-8")) return Subtask.from_dict(data) def save_subtask(subtask: Subtask): """Save subtask JSON atomically.""" task_dir = get_task_dir(subtask.parent_task) atomic_write(task_dir / f"{subtask.id}.json", subtask.to_dict())python def archive_task(task_id: str) -> bool: """Move a terminal-state task to archive/.""" task = read_task(task_id) if task is None: logger.error("Task %s not found", task_id) retu ...[truncated 3587 chars]- Remediation
View remediation
Remediation Suggestions
-
Validate identifiers at every trust boundary.
- Require task IDs to match a strict expression such as
^TASK-[0-9]{3,}$. - Require subtask IDs to match a strict expression such as
^subtask_[0-9]{2,}$. - Reject absolute paths, path separators, empty values,
.and...
- Require task IDs to match a strict expression such as
-
Enforce canonical path containment. Resolve the base and candidate paths before access and verify containment:
python def safe_child(base: Path, component: str) -> Path: if not component or Path(component).is_absolute(): raise ValueError("Invalid path component") base = base.resolve() candidate = (base / component).resolve() if not candidate.is_relative_to(base): raise ValueError("Path escapes the permitted directory") return candidateApply equivalent checks to task directories, archive destinations, lock files, log files, task files, and subtask files.
-
Validate persisted identifiers. Treat IDs loaded from
task.json,subtask_*.json, andindex.jsonas untrusted. Validate them before using them to build paths or save objects. -
Harden archive operations. Confirm both source and destination remain under their expected canonical roots. Reject an existing destination and avoid following unexpected symbolic links.
-
Harden write operations. Create temporary files securely in the verified destination directory, use unique temporary names, and reject symbolic-link targets where supported.
-
Add regression tests. Cover traversal and absolute-path attempts against
status,transition,dispatch,subtask,check,archive, task locking, logging, and index rebuilding. Verify that no file is read, created, modified, or moved outside the temporary task root.
-
