T09 · Insecure Skill Coding Practices
- Location
scripts/sync.py:195- Finding
Unvalidated destination paths permit arbitrary file writes outside the Obsidian vault
- Content
View full analysis
Path: dest_dir = VAULT_DIR / relative_path dest_dir.mkdir(parents=True, exist_ok=True) return dest_dir ``` ```python def write_obsidian_file(relative_path: str, filename: str, title: str, content: str, doc_token: str, node_token: str, wiki_name: str, dry_run: bool = False) -> Optional[str]: """Write an Obsidian file, returning its path or None if it exists.""" content = convert_lark_tables(content) existing = find_existing_by_doc_token(doc_token) if existing: print(f" [SKIP] {title} → already exists ({existing.name})") return None dest_dir = ensure_obsidian_folder(relative_path) dest_path = dest_dir / filename counter = 1 while dest_path.exists() and counter < 100: base, ext = filename.rsplit(".", 1) dest_path = dest_dir / f"{base}-{counter}.{ext}" counter += 1 if dry_run: print(f" [DRY-RUN] {title} → {relative_path}/{filename}") return str(dest_path) frontmatter = build_frontmatter(doc_token, node_token, wiki_name) dest_path.write_text(f"{frontmatter}\n\n{content}", encoding="utf-8") print(f" [SYNCED] {title} → {relative_path}/{dest_path.name}") return str(dest_path) ``` ```python todo = json.loads((CACHE_DIR / "plan.json").read_text())["todo"] todo_map: Dict[str, dict] = {t["obj_token"]: t for t in todo} all_synced, skipped, failed = [], 0, [] for node in nodes: title = node.get("title", "Unnamed") obj_token = node.get("obj_token", "") content = node.get("content", "") node_token = node.get("node_token", "") wiki_name = node.get("wiki_name", "") relative_path = node.get("relative_path" ...[truncated 3462 chars]- Remediation
View remediation
