T09 · Insecure Skill Coding Practices
- Location
scripts/wiseocr.py:179- Finding
Unvalidated Output Name Permits Path Traversal and Arbitrary Markdown File Overwrite
- Content
View full analysis
None: """Save OcrResult to a markdown file.""" markdown = data.get("markdown", "") total_pages = data.get("total_pages", 1) elapsed = data.get("elapsed_seconds", 0) usage = data.get("usage") or {} print(f"[*] Total pages: {total_pages}") print(f"[*] Processing time: {elapsed:.1f}s") if usage: print(f"[*] Usage: prompt_tokens={usage.get('prompt_tokens')}, " f"completion_tokens={usage.get('completion_tokens')}, " f"ocr_pic_size={usage.get('ocr_pic_size')}, " f"total_tokens={usage.get('total_tokens')}") out_path = output_dir / f"{stem}.md" out_path.write_text(markdown, encoding="utf-8") print(f"[+] Markdown saved: {out_path}") ``` The value passed as `stem` is selected without validation: ```python stem = name if name else p.stem _save_result(resp.json(), out_dir, stem) ``` ### Technical Analysis The `--name` argument is treated as a filename stem, but the implementation does not reject absolute paths, path separators, or parent-directory components such as `../`. When `stem` contains an absolute path, `pathlib` discards the preceding `output_dir`. When it contains parent-directory components, path resolution can escape the intended output directory. The subsequent `write_text()` call overwrites an existing file without confirmation. Although the `.md` suffix limits the destination to a Markdown filename, the flaw still permits modification of arbitrary writable Markdown files outside `~/.openclaw/workspace/WiseOCR`. This is particularly relevant in an Agent environment where Markdown files may contain workspace instructions ...[truncated 1520 chars]- Remediation
View remediation
Path: if not stem or not re.fullmatch(r"[A-Za-z0-9 _.-]+", stem): raise ValueError("Invalid output filename") if stem in {".", ".."} or Path(stem).is_absolute(): raise ValueError("Invalid output filename") base = output_dir.resolve() destination = (base / f"{stem}.md").resolve() if destination.parent != base: raise ValueError("Output path escapes the configured directory") if destination.exists(): raise FileExistsError(f"Refusing to overwrite: {destination}") return destination ``` Use the validated path before writing and report validation failures without exposing credentials or document content. ]]>
