T09 · Insecure Skill Coding Practices
- Location
scripts/split_by_material.py:265- Finding
Path Traversal Through Unvalidated Output Slug
- Content
View full analysis
Vulnerability Details
File Location:
scripts/split_by_material.py, lines 38-44 and 265-266
Vulnerability Type: Path traversal and arbitrary file overwrite
Risk Level: MediumVulnerable Code
python def parse_parts(specs): out = [] for spec in specs: if '=' not in spec: raise SystemExit(f'Bad --part spec {spec!r}; expected MATERIAL=slug') mat, slug = spec.split('=', 1) out.append((mat, slug)) return outpython stl = os.path.join(args.outdir, slug + '.stl') export_binary_stl(obj, stl)Technical Analysis
The
slugcomponent of each--part MATERIAL=slugargument is used as part of an output path without validation. The code does not reject absolute paths, parent-directory components such as.., or path separators.os.path.join()does not guarantee that the resulting path remains beneathargs.outdir. Parent-directory components can escape the intended output directory, and an absoluteslugcan cause the base directory to be discarded entirely.The resulting path is passed to
export_binary_stl(), which opens the path in binary write mode. Consequently, the script can create or overwrite an accessible file outside the selected output directory.Attack Path
-
An attacker or untrusted automation influences a
--partargument passed to the Blender script. -
The supplied output slug contains traversal components or an absolute path, for example:
text --part PART_01_HAT=../../outside/result -
The parser accepts the value without checking its path semantics.
-
os.path.join(args.outdir, slug + '.stl')resolves to a location outside the intended output directory. -
export_binary_stl()writes generated STL data to that location. -
If the destination already exists and is writable, it is overwritten.
Exploitation requires the attacker to influence script arguments and is lim ...[truncated 592 chars]
-
- Remediation
View remediation
Remediation Suggestions
-
Restrict output slugs to a conservative filename pattern, such as:
python import re if not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9._-]*', slug): raise SystemExit(f'Invalid output slug: {slug!r}') -
Explicitly reject empty values, absolute paths,
..components, and both POSIX and Windows path separators. -
Resolve and verify the final destination before writing:
python base = os.path.realpath(args.outdir) candidate = os.path.realpath(os.path.join(base, slug + '.stl')) if os.path.commonpath([base, candidate]) != base: raise SystemExit('Output path escapes the configured output directory') -
Consider rejecting existing destination files by default or requiring an explicit overwrite option.
-
Add tests for
../name, absolute paths, Windows-style separators, empty slugs, nested paths, symbolic-link escapes, and valid simple slugs.
-
