T09 · Insecure Skill Coding Practices
- Location
scripts/init.py:28- Finding
Path Traversal Through an Unvalidated Report Name
- Content
View full analysis
Vulnerability Details
File Location:
scripts/init.py:28-37
Vulnerability Type: Path traversal and unrestricted directory creation
Risk Level: MediumVulnerable Code
python p.add_argument('--dir', '-d', default='reports', help='Output root directory') args = p.parse_args() # Create directories report_dir = Path(args.dir) / args.name if report_dir.exists(): print(f'Error: directory already exists: {report_dir}', file=sys.stderr) sys.exit(1) report_dir.mkdir(parents=True) (report_dir / 'appendix').mkdir()The displayed English comments and help text are translations of the original strings; the executable path-handling logic is unchanged.
Technical Analysis
The report name supplied through
args.nameis appended directly to the output root and treated as a filesystem path. The implementation does not reject:- Absolute paths
- Parent-directory components such as
.. - Path separators
- Other values that resolve outside the configured output root
In
pathlib, joining a base path with an absolute path discards the base path. Relative names containing parent-directory components can similarly escape the intendedreports/directory after path resolution.The script then calls
mkdir(parents=True)before validating that the resulting path remains below the output root. Consequently, an attacker-controlled report name can cause directory trees, including anappendixsubdirectory, to be created at unintended locations accessible to the executing account.This behavior also conflicts with
references/edge-cases.md:9, which states that report names containing special characters are converted into safe filenames. No such filtering is implemented.Attack Path
- An attacker supplies or influences the report name processed by an Agent.
- The Agent invokes
scripts/init.pyusing that untrusted name. - The attacker uses an absolute path or traversal components such as
../../target. - The expression `Path(arg ...[truncated 1008 chars]
- Remediation
View remediation
Remediation Suggestions
-
Require the report name to be a single filename component:
- Reject absolute paths.
- Reject
.and... - Reject
/,\, and platform-specific path separators. - Apply a conservative allowlist for permitted characters.
-
Resolve and verify containment before creating directories:
python output_root = Path(args.dir).expanduser().resolve() name = args.name.strip() if ( not name or Path(name).is_absolute() or name in {'.', '..'} or Path(name).name != name ): p.error('Report name must be a single safe path component') safe_name = re.sub(r'[^A-Za-z0-9._ -]', '_', name).strip() if not safe_name or safe_name in {'.', '..'}: p.error('Report name is invalid after sanitization') report_dir = (output_root / safe_name).resolve() try: report_dir.relative_to(output_root) except ValueError: p.error('Resolved report directory escapes the output root') report_dir.mkdir(parents=True, exist_ok=False) (report_dir / 'appendix').mkdir()-
Use the sanitized name consistently for directory names, report filenames, headings, and generated links.
-
Create the complete scaffold in a temporary directory under the validated output root and rename it atomically after all files have been generated. This avoids leaving partial directories when later operations fail.
-
Add regression tests covering absolute paths,
../traversal, nested separators, empty names, platform-specific separators, and valid Unicode report names. -
Update
references/edge-cases.mdso its documented sanitization behavior exactly matches the implementation.
-
