T05 · Unauthorized Access and Privilege Escalation
- Location
scripts/checkpoint.py:30- Finding
Path Traversal Through Unvalidated Checkpoint Identifier
- Content
View full analysis
Vulnerability Details
File Location:
scripts/checkpoint.py, lines 30-63
Vulnerability Type: Path traversal and unauthorized file access
Risk Level: MediumVulnerable Code
python def create(task_id: str, description: str, total: int): """Create a new checkpoint.""" ensure_dir() cp = { "task_id": task_id, "description": description, "total": total, "completed": 0, "succeeded": 0, "failed": 0, "failed_items": [], "created_at": datetime.now(timezone.utc).isoformat(), "last_checkpoint": datetime.now(timezone.utc).isoformat(), "output_file": None, "status": "running" } path = os.path.join(CHECKPOINT_DIR, f"{task_id}.json") with open(path, "w") as f: json.dump(cp, f, indent=2, ensure_ascii=False) def update(task_id: str, succeeded: int = 0, failed: int = 0, failed_items: list = None, output_file: str = None): """Update checkpoint progress.""" path = os.path.join(CHECKPOINT_DIR, f"{task_id}.json") with open(path) as f: cp = json.load(f) # The checkpoint is subsequently written back to the same path. with open(path, "w") as f: json.dump(cp, f, indent=2, ensure_ascii=False) def read(task_id: str): """Read checkpoint state.""" path = os.path.join(CHECKPOINT_DIR, f"{task_id}.json") if not os.path.exists(path): return None with open(path) as f: cp = json.load(f) print(json.dumps(cp, indent=2, ensure_ascii=False)) return cpTechnical Analysis
The
task_idvalue is obtained from command-line input and embedded directly into a filesystem path:python os.path.join(CHECKPOINT_DIR, f"{task_id}.json")No validation rejects absolute paths, directory separators, or
..traversal components. Consequently, the normalized path is not guar ...[truncated 2502 chars]- Remediation
View remediation
Remediation Suggestions
-
Strictly validate checkpoint identifiers. Allow only a limited filename-safe character set:
python import re TASK_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]+$") def validate_task_id(task_id: str) -> None: if not TASK_ID_PATTERN.fullmatch(task_id): raise ValueError("Invalid task ID") -
Centralize secure path construction. Resolve both the base directory and candidate path, then verify containment:
python from pathlib import Path CHECKPOINT_DIR = Path( "~/.openclaw/workspace/checkpoints" ).expanduser().resolve() def checkpoint_path(task_id: str) -> Path: validate_task_id(task_id) candidate = (CHECKPOINT_DIR / f"{task_id}.json").resolve() if candidate.parent != CHECKPOINT_DIR: raise ValueError("Checkpoint path escapes the checkpoint directory") return candidate -
Reject path syntax explicitly. Do not accept absolute paths,
..,/,\, null bytes, or platform-specific path separators as identifiers. -
Mitigate symbolic-link attacks. Refuse to operate on symbolic links and, where supported, open files using flags such as
O_NOFOLLOW. -
Use atomic writes. Write serialized data to a securely created temporary file in the same directory, flush and synchronize it, and then replace the destination atomically with
os.replace. -
Apply restrictive permissions. Create the checkpoint directory and files with permissions that limit access to the Agent's operating-system account.
-
Validate loaded checkpoint schemas. Before updating a file, verify that it is a valid checkpoint object with correctly typed and bounded fields.
-
