T09 · Insecure Skill Coding Practices
- Location
scripts/notes_workflow.py:48- Finding
Unvalidated Note Paths Permit Writes Outside the Vault
- Content
View full analysis
Path: p = Path(note) if p.suffix == "": p = p.with_suffix(".md") return p def ensure_parent(path: Path): path.parent.mkdir(parents=True, exist_ok=True) def append_plain(path: Path, content: str): ensure_parent(path) with path.open("a", encoding="utf-8") as f: f.write(content) if not content.endswith("\n"): f.write("\n") ``` ```python def do_memo(args): path = VAULT_ROOT / normalize_note(INBOX_NOTE) append_plain(path, args.content + "\n") return "fallback", str(path.relative_to(VAULT_ROOT)), True def do_append(args): path = VAULT_ROOT / normalize_note(args.note) append_plain(path, args.content + "\n") return "fallback", str(path.relative_to(VAULT_ROOT)), True def do_create(args): note = normalize_note(args.note) full = VAULT_ROOT / note if create_via_cli(note, args.content) and not args.force_fallback: return "cli", str(note), True if full.exists() and not args.overwrite: raise SystemExit(f"Note already exists: {full}") ensure_parent(full) full.write_text(args.content + ("" if args.content.endswith("\n") else "\n"), encoding="utf-8") return "fallback", str(full.relative_to(VAULT_ROOT)), True ``` ### Technical Analysis The `--note` argument is converted to a `Path` and joined to `VAULT_ROOT` without validating that the resulting path remains inside the vault. A relative path containing parent-directory components, such as `../../target.md`, can escape the vault. An absolute path is more direct: under `pathlib` semantics, joining a base path with an absolute path discards the base path. The affected write operations are: - `append`, which can append attacker-controlled text ...[truncated 2460 chars]- Remediation
View remediation
Path: candidate = normalize_note(note) if candidate.is_absolute(): raise ValueError("Absolute note paths are not allowed") root = VAULT_ROOT.resolve(strict=True) target = (root / candidate).resolve(strict=False) try: target.relative_to(root) except ValueError as exc: raise ValueError("Note path escapes the vault") from exc return target ``` The containment result must be used before any filesystem mutation. For strong protection against time-of-check/time-of-use and symlink races, use directory-relative file operations and refuse to follow symlinks where supported. ]]>
