T09 · Insecure Skill Coding Practices
- Location
scripts/organize_files.py:37- Finding
Unvalidated custom category names allow destination path traversal
- Content
View full analysis
str: ext = ext.lower().lstrip(".") for category, extensions in rules.items(): if ext in extensions: return category return "Others" def safe_move(src: Path, dst_dir: Path, dry_run=False) -> Path: """移动文件,自动处理命名冲突""" dst_dir.mkdir(parents=True, exist_ok=True) dst = dst_dir / src.name if dst.exists() and dst != src: stem, suffix = src.stem, src.suffix i = 1 while dst.exists(): dst = dst_dir / f"{stem}_{i}{suffix}" i += 1 if not dry_run: shutil.move(str(src), str(dst)) return dst ``` ```python for f in files: category = get_file_category(f.suffix, rules) dst_dir = target / category if f.parent == dst_dir: stats["skipped"] += 1 continue dst = safe_move(f, dst_dir, dry_run=dry_run) ``` ```python if args.rules: with open(args.rules, encoding="utf-8") as f: rules = json.load(f) ``` ### Technical Analysis Keys from a user-supplied JSON rules file become category names without validation. The program subsequently appends each category directly to the target path. A category containing parent-directory components, such as `../../outside`, causes the resulting path to escape the intended target directory. Under `pathlib`, an absolute category such as `/tmp/drop` also replaces the preceding target path entirely. The program does not reject absolute paths, directory separators, or `..` components, and it does not verify that the resolved destination remains beneath the resolved target. The resulting destination is passed to `mkdir()` and `shutil.move()`, allowing matching source files to be moved to attacker-selected loc ...[truncated 1294 chars]- Remediation
View remediation
Path: category_path = Path(category) if ( category_path.is_absolute() or category in {".", ".."} or len(category_path.parts) != 1 or "/" in category or "\\" in category ): raise ValueError(f"Invalid category name: {category!r}") resolved_target = target.resolve() destination = (resolved_target / category).resolve() if not destination.is_relative_to(resolved_target): raise ValueError("Category destination escapes the target directory") return destination ``` 3. Replace `dst_dir = target / category` with the validated destination helper. 4. Validate the complete JSON schema before organization begins. Require an object whose keys are valid category names and whose values are lists of normalized extension strings. 5. Abort before moving any files if any rule is invalid, and provide a clear error identifying the rejected category. 6. Add tests for `../outside`, `../../outside`, absolute Unix paths, Windows drive paths, backslash traversal, and valid category names. ]]>
