T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generator.py:137
- Finding
- Unrestricted Output Path Allows Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generator.py:137-151` **Vulnerability Type**: Path traversal and unrestricted file overwrite **Risk Level**: Medium ### Vulnerable Code ```python project_dir = self.output_dir / name project_dir.mkdir(parents=True, exist_ok=True) # Create directories for dir_name in ["src", "tests", "docs"]: (project_dir / dir_name).mkdir(exist_ok=True) # Create files (project_dir / "README.md").write_text(f"# {name}\n\n{description or 'A Python project'}\n") (project_dir / "pyproject.toml").write_text(f'''[project] name = "{name}" version = "0.1.0" description = "{description or 'A Python project'}" ``` Additional files are subsequently written beneath the same unvalidated `project_dir`: ```python (project_dir / "src" / "__init__.py").write_text(f'"""{name} - {description or 'A Python project'}"""\n') (project_dir / "tests" / "__init__.py").write_text("") (project_dir / ".gitignore").write_text("__pycache__/\n*.py[cod]\n.env\n.venv/\n") ``` ### Technical Analysis The project destination is calculated by directly joining the caller-controlled `name` with the caller-controlled `output_dir`: ```python project_dir = self.output_dir / name ``` Neither value is normalized and checked against an approved output root. In `pathlib`, an absolute right-hand operand causes the left-hand path to be discarded. A project name containing parent-directory components such as `../` can also resolve outside the expected output directory. The script then creates the destination and writes multiple files with `Path.write_text()`. That method truncates existing files by default, and the implementation does not check whether a target already exists or require explicit overwrite confirmation. The vulnerability is limited by the operating-system privileges of the process running the generator. It does not independently elevate privileges. ### Attack Path 1. An attacker supplies or influences the `scaffold` command's projec ...[truncated 1386 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve the configured output root and candidate destination before creating anything: ```python output_root = self.output_dir.resolve() candidate = (output_root / name).resolve() ``` 2. Reject absolute project names, parent-directory components, empty names, and path separators: ```python name_path = Path(name) if name_path.is_absolute() or ".." in name_path.parts or len(name_path.parts) != 1: raise ValueError("Project name must be a single relative path component") ``` 3. Enforce containment beneath the approved output root: ```python if candidate != output_root and output_root not in candidate.parents: raise ValueError("Project destination escapes the output directory") ``` 4. Refuse to write into an existing nonempty destination by default. 5. Add an explicit `--force` option if overwrite behavior is required, and clearly list affected files before proceeding. 6. Prefer exclusive file creation, such as mode `x`, for new scaffolds. 7. Add tests covering absolute paths, `../` traversal, nested traversal, symbolic links, and existing-file collisions. ]]>
