T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:161- Finding
Path Traversal Through the Experiment Name Allows Writes Outside the Save Directory
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py, lines 161–166
Vulnerability Type: Path traversal and unrestricted file write
Risk Level: MediumVulnerable Code
python timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") filename_base = f"{self.experiment_name.replace(' ', '_')}_{timestamp}" if self.output_format == "markdown": file_path = self.save_directory / f"{filename_base}.md" self._save_markdown(file_path)Technical Analysis
The filename is derived from the user-controlled
experiment_name, which is supplied through the--experimentcommand-line argument. The only transformation replaces spaces with underscores. Path separators, absolute paths, drive prefixes, and parent-directory components such as..are not rejected.When
pathlib.Pathjoins a base directory with an absolute child path, the base directory is discarded. Relative traversal components can likewise cause the resolved destination to escape the intended save directory. The resulting path is subsequently opened in write mode by the selected save function.Output extensions are appended by the application, and intermediate directories must already exist, but these restrictions do not prevent creation or replacement of matching files outside the configured directory.
Attack Path
- An attacker or untrusted caller invokes the script and controls
--experiment. - The attacker supplies an absolute or traversal-based name, such as
/tmp/unauthorizedor../../tmp/unauthorized. save()incorporates that value intofilename_basewithout filename sanitization or canonical path validation.- The user issues the save or stop command.
- The script resolves the constructed path outside
save_directoryand opens it in write mode. - A timestamped Markdown, JSON, or text file is created or replaced using the privileges of the process.
Impact Assessment
The vulnerability permits ...[truncated 553 chars]
- An attacker or untrusted caller invokes the script and controls
- Remediation
View remediation
Remediation Suggestions
- Treat the experiment name strictly as a filename component rather than a path.
- Reject absolute paths, path separators, drive prefixes, null characters,
.components, and..components. - Normalize the name to a conservative allowlist, such as letters, digits, underscores, and hyphens.
- Resolve both the base directory and destination before writing, then verify that the destination remains beneath the resolved base directory.
- Use exclusive file creation mode to prevent accidental replacement of existing files.
- Generate a random or otherwise collision-resistant suffix instead of relying only on second-resolution timestamps.
- Reject an empty filename after normalization.
Example hardening approach:
python import re import uuid base_dir = self.save_directory.resolve() safe_name = re.sub(r"[^A-Za-z0-9_-]+", "_", self.experiment_name).strip("_") if not safe_name: raise ValueError("The experiment name does not contain a valid filename component") filename = f"{safe_name}_{uuid.uuid4().hex}.md" destination = (base_dir / filename).resolve() if destination.parent != base_dir: raise ValueError("The output path is outside the configured save directory")
