T09 · Insecure Skill Coding Practices
- Location
obsidian_new.py:136- Finding
Path Traversal Enables Filesystem Access Outside the Research Vault
- Content
View full analysis
Vulnerability Details
File Location:
obsidian_new.py:136-155, 207-209;obsidian_search.py:35-47, 139-145
Vulnerability Type: Unrestricted path traversal and insufficient filesystem boundary validation
Risk Level: MediumVulnerable Code
obsidian_new.py:python def get_direction_path(direction): """获取研究方向目录路径""" base = get_research_base_path() # 如果 direction 是编号(如"01"),转换为完整名称 if direction in DIRECTIONS: direction = DIRECTIONS[direction] dir_path = base / direction if not dir_path.exists(): print(f"错误:研究方向目录不存在:{dir_path}") print(f"可用方向:{', '.join(DIRECTIONS.values())}") return None return dir_pathpython # 生成文件名 filename = f"{title}.md" file_path = dir_path / filename # 检查文件是否已存在 if file_path.exists(): print(f"警告:文件已存在:{file_path}") overwrite = input("是否覆盖?(y/n): ") if overwrite.lower() != 'y': print("已取消") return Nonepython # 写入文件 with open(file_path, 'w', encoding='utf-8') as f: f.write(content)obsidian_search.py:python # 确定搜索目录 search_dirs = [base] if direction: direction_dir = base / direction if direction_dir.exists(): search_dirs = [direction_dir] else: print(f"警告:研究方向目录不存在:{direction}") # 遍历所有 markdown 文件 for search_dir in search_dirs: for md_file in search_dir.rglob('*.md'): # 跳过模板文件 if md_file.name == 'template.md': continue try: with open(md_file, 'r', encoding='utf-8') as f: content = f.read()python if direction: direction_dir = base / direction if not direction_dir.exists(): print(f"错误:研究方向目录不存在:{direction}") return [] md_files = list(direction_dir.glob('*.md')) return [{'title': f.stem, 'file': ...[truncated 3466 chars]- Remediation
View remediation
Remediation Suggestions
-
Resolve and validate the vault root before using any user-controlled path:
python base = get_research_base_path().resolve(strict=True) -
Prefer an allowlist for research directions. Accept only keys or exact directory names present in
DIRECTIONSrather than arbitrary path strings. -
Resolve every candidate directory and verify containment:
python candidate = (base / direction).resolve(strict=True) if not candidate.is_relative_to(base): raise ValueError("Direction must remain inside the research vault") -
Reject absolute paths,
..components, path separators, null characters, and platform-reserved filename characters in note titles. A title should be treated as a filename component rather than a path. -
Validate the final note path independently after appending the filename:
python file_path = (candidate / f"{safe_title}.md").resolve() if not file_path.is_relative_to(base): raise ValueError("Note path must remain inside the research vault") -
Use exclusive creation mode (
"x") for new notes. If overwrite support is required, expose an explicit authenticated or deliberate--overwriteoption and repeat the containment check immediately before writing. -
Apply the same resolved-path containment checks before
rglob(),glob(),open(), andrelative_to()in all search and listing paths. -
Consider rejecting symlinked direction directories or resolving symlinks and confirming that their final targets remain inside the vault.
-
Add automated tests covering absolute paths, nested traversal, symlink escapes, mixed path separators, crafted titles, and legitimate allowlisted research directories.
-
