T09 · Insecure Skill Coding Practices
- Location
scripts/checkpoint.py:32- Finding
Path Traversal Through Unvalidated Checkpoint Names
- Content
View full analysis
Vulnerability Details
File Location:
scripts/checkpoint.py:32-33, 40, 114-120, 135-141, 195-207
Vulnerability Type: Path traversal enabling unauthorized Markdown file access
Risk Level: HighVulnerable Code
python def checkpoint_exists(name: str) -> bool: return (CHECKPOINT_DIR / f"{name}.md").exists()python def save_checkpoint(name: str, task: str = "") -> None: """Save a named checkpoint from current SESSION-STATE.md and recent context.""" CHECKPOINT_DIR.mkdir(parents=True, exist_ok=True) out_file = CHECKPOINT_DIR / f"{name}.md" session_state = get_session_state() now = datetime.now().strftime("%Y-%m-%d %H:%M GMT+8") content = f"""# Checkpoint: {name} **Created:** {now} **Task:** {task or "Unnamed task"} ## Progress <!-- Describe what was completed in this session --> ## Next Action <!-- Exact next step: which file, which command --> ## Blockers <!-- Concrete obstacles: errors, conflicts, pending decisions --> ## Key Decisions <!-- Reasoning that led to current state --> ## Relevant Context <!-- Files, paths, preferences needed to continue --> """ out_file.write_text(content)python def resume_checkpoint(name: str) -> None: """Print checkpoint content for review before resumption.""" cp_file = CHECKPOINT_DIR / f"{name}.md" if not cp_file.exists(): print(f"ERROR: No checkpoint named '{name}'") sys.exit(1) content = cp_file.read_text()python def delete_checkpoint(name: str) -> None: """Delete a named checkpoint.""" if name == "autosave": print("ERROR: Cannot delete autosave. Use a named checkpoint.") sys.exit(1) cp_file = CHECKPOINT_DIR / f"{name}.md" if not cp_file.exists(): print(f"ERROR: No checkpoint named '{name}'") sys.exit(1) cp_file.unlink()python elif cmd == "save": name = sys.argv[2] task = "" if len(sys.argv) > 4 and sys.argv[3] == "--ta ...[truncated 3493 chars]- Remediation
View remediation
Remediation Suggestions
- Enforce the documented checkpoint-name grammar before any filesystem operation. For example:
python import re NAME_PATTERN = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$") FORBIDDEN_NAMES = {"task", "work", "save", "untitled", "backup"} def validate_name(name: str) -> str: if not NAME_PATTERN.fullmatch(name): raise ValueError( "Checkpoint names must contain lowercase letters, digits, and " "single hyphens only." ) if name in FORBIDDEN_NAMES: raise ValueError("Reserved checkpoint name.") return name- Centralize secure path construction and verify containment after resolution:
python def checkpoint_path(name: str) -> Path: name = validate_name(name) base = CHECKPOINT_DIR.resolve() candidate = (base / f"{name}.md").resolve() if candidate.parent != base: raise ValueError("Checkpoint path escapes the checkpoint directory.") return candidate-
Use this helper in
checkpoint_exists,save_checkpoint,resume_checkpoint, anddelete_checkpoint. -
Reject path separators, absolute paths, empty names,
.and..components, control characters, and reserved filenames before performing any read or write. -
Avoid silently overwriting an existing named checkpoint. Require an explicit overwrite option, user confirmation, or atomic exclusive creation where appropriate.
-
Consider symlink defenses if the checkpoint directory may be writable by untrusted local users. Validate that the destination is not a symlink and use operating-system facilities that prevent symlink following where available.
-
Add regression tests covering all affected commands, including:
text ../target ../../target /absolute/target name/with/slash . .. autosave valid-kebab-nameThe tests should assert that invalid names cannot read, create, overwrite, probe, or delete files outside
CHECKPOINT_DIR.
