T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/chan_extractor.py:99
- Finding
- Unsanitized Path Components Permit Output Directory Escape<![CDATA[ ## Vulnerability Details **File Location**: `scripts/chan_extractor.py`, lines 99–108 and 140–160 **Vulnerability Type**: Path traversal and arbitrary file overwrite **Risk Level**: Medium ### Vulnerable Code ```python # Write to file if output_root provided if output_root: timestamp = datetime.now().strftime("%Y-%m-%d_%H") output_dir = os.path.join(output_root, f"{board}_{timestamp}") os.makedirs(output_dir, exist_ok=True) file_path = os.path.join(output_dir, f"{thread_id}.txt") try: with open(file_path, 'w', encoding='utf-8') as f: f.write(full_output) print(f"--- Saved to {file_path} ---", file=sys.stderr) ``` The affected values are taken directly from command-line arguments: ```python board = sys.argv[2] thread_id = sys.argv[3] out_root = None word_limit = None if len(sys.argv) > 4: # Check if arg4 is a digit (word_limit) or a directory if sys.argv[4].isdigit(): word_limit = int(sys.argv[4]) else: out_root = sys.argv[4] if len(sys.argv) > 5 and sys.argv[5].isdigit(): word_limit = int(sys.argv[5]) get_thread(board, thread_id, out_root, word_limit) ``` ### Technical Analysis The caller-controlled `board` and `thread_id` values are incorporated into filesystem paths without validation, normalization, or a containment check. `os.path.join()` does not guarantee that the resulting path remains beneath `output_root`. Traversal components such as `..` may resolve outside the intended directory. An absolute path component may also cause preceding path components to be discarded. The program subsequently creates directories with `os.makedirs()` and opens the destination in write mode, which truncates an existing file. The intended output layout is `<output_root>/<board>_<timestamp>/<thread_id>.txt`, but the implementation does not enforce that boundary. Exploitation is constrained because the file-writing logic runs only after the constructed 4chan re ...[truncated 1768 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Strictly validate board identifiers against the expected format and, preferably, an explicit allowlist of supported 4chan boards: ```python if not re.fullmatch(r"[a-z0-9]+", board): raise ValueError("Invalid board identifier") ``` 2. Require thread IDs to contain digits only: ```python if not re.fullmatch(r"[0-9]+", thread_id): raise ValueError("Invalid thread identifier") ``` 3. Resolve and verify filesystem paths before creating directories or writing files: ```python from pathlib import Path root = Path(output_root).expanduser().resolve() output_dir = (root / f"{board}_{timestamp}").resolve() file_path = (output_dir / f"{thread_id}.txt").resolve() if output_dir != root and root not in output_dir.parents: raise ValueError("Output directory escapes the configured root") if root not in file_path.parents: raise ValueError("Output file escapes the configured root") ``` 4. Consider rejecting symbolic-link components or opening files using directory-relative, no-follow operating-system APIs when the output directory may be writable by untrusted users. 5. If replacing existing files is unnecessary, use exclusive creation mode (`'x'`) rather than write mode (`'w'`) to prevent silent truncation. 6. Run the Skill under a minimally privileged account and restrict the permitted output root to a dedicated data directory. ]]>
