T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/batch_renamer.py:13
- Finding
- Path Traversal and Arbitrary File Overwrite Through Crafted Rename Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_renamer.py`, lines 13–24 and 51–53 **Vulnerability Type**: Insufficient destination-path validation **Risk Level**: High ### Vulnerable Code ```python if a.replace: old, new_s = a.replace.split("|", 1) if "|" in a.replace else (a.replace, "") new = new.replace(old, new_s) if a.prefix: new = a.prefix + new if a.suffix: r, e = os.path.splitext(new) new = r + a.suffix + e if new != name: plan.append((f, os.path.join(os.path.dirname(f), new))) ``` The generated paths are subsequently used without validation: ```python if a.apply and plan: for x, y in plan: os.rename(x, y) ``` ### Technical Analysis The `--prefix`, `--suffix`, and `--replace` arguments influence the destination path directly. The generated value is passed to `os.path.join()` without rejecting: - Absolute paths - `..` parent-directory components - Forward or backward path separators - Destinations outside the directory supplied through `--dir` - Existing destination files If `new` is absolute, `os.path.join(os.path.dirname(f), new)` discards the intended source directory. A relative value containing `../` can similarly escape that directory after filesystem path resolution. The script then passes the untrusted destination directly to `os.rename`. On operating systems where `os.rename` replaces an existing destination, this can overwrite a file without confirmation. On other systems, the operation can fail after earlier files have already been renamed. ### Attack Path 1. The attacker supplies or convinces an operator or agent to use a crafted prefix, suffix, or replacement rule. 2. The crafted name contains an absolute path or parent-directory traversal components. 3. For example, a prefix such as `../escaped/` causes a source file named `report.txt` to receive a destination resembling: `selected-directory/../escaped/report.txt`. 4. The operator runs the command with `--apply`. 5. Th ...[truncated 1077 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Treat every generated destination as an untrusted filesystem path. 1. Require the generated name to be a filename rather than a path: - Reject `/` and `\`. - Reject `.` and `..`. - Reject absolute paths using `os.path.isabs`. - Consider rejecting platform-specific drive and UNC path syntax. 2. Canonicalize and enforce directory containment: ```python source_dir = os.path.realpath(os.path.dirname(f)) destination = os.path.realpath(os.path.join(source_dir, new)) if os.path.dirname(destination) != source_dir: raise ValueError("Destination must remain inside the source directory") ``` 3. Refuse to overwrite existing paths: ```python if os.path.lexists(destination): raise FileExistsError("Destination already exists: %s" % destination) ``` 4. Perform validation for the entire plan before changing any file. 5. Detect duplicate destination paths using normalized, case-aware filesystem semantics. 6. Display validation failures in both human-readable and JSON output and return a nonzero exit code. 7. Where appropriate, use a non-overwriting rename primitive or explicitly create destination reservations to reduce race-condition exposure between validation and execution. ]]>
