T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/chart-generator.py:111
- Finding
- Path Traversal Through Dataset-Controlled Chart Filenames<![CDATA[ ## Vulnerability Details **File Location**: `scripts/chart-generator.py:111`, `scripts/chart-generator.py:145`, `scripts/chart-generator.py:179`, and `scripts/chart-generator.py:221` **Vulnerability Type**: Path traversal and uncontrolled file write **Risk Level**: Medium ### Vulnerable Code ```python # scripts/chart-generator.py:111 path = Path(out) / f"{col}_histogram.png" ``` ```python # scripts/chart-generator.py:145 path = Path(out) / f"{col}_bar.png" ``` ```python # scripts/chart-generator.py:179 path = Path(out) / f"{col}_pie.png" ``` ```python # scripts/chart-generator.py:221 path = Path(out) / f"scatter_{x_col}_vs_{y_col}.png" ``` ### Technical Analysis Column names from an input CSV, TSV, or spreadsheet are incorporated directly into chart filenames. The code does not sanitize path separators, reject absolute paths, generate opaque filenames, or verify that the resolved destination remains inside the configured output directory. A malicious column name containing path traversal components such as `../` can cause `matplotlib` to save a generated PNG outside the intended chart directory. Depending on the constructed name and operating system path semantics, absolute or nested path components may also redirect the output. The write remains constrained by the chart-specific suffix, such as `_histogram.png`, and the generated content is a PNG image. Nevertheless, an attacker can overwrite a matching file or create files in unintended directories writable by the process. ### Attack Path 1. An attacker supplies a CSV or spreadsheet with a numeric or categorical column name containing traversal components, such as `../../shared/target`. 2. A user or Agent runs `chart-generator.py` or `auto-pipeline.py` against that dataset. 3. The malicious column is selected for histogram, bar, pie, or scatter generation. 4. The filename expression preserves the traversal components. 5. `fig.savefig()` resolves the resulting path outside the intended char ...[truncated 721 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Convert every dataset-derived column name into a safe filename component: ```python import re import hashlib def safe_filename_component(value): text = str(value) sanitized = re.sub(r"[^A-Za-z0-9._-]+", "_", text).strip("._") digest = hashlib.sha256(text.encode("utf-8")).hexdigest()[:10] return f"{sanitized[:50] or 'column'}_{digest}" ``` 2. Resolve and validate every destination before writing: ```python def confined_output_path(output_dir, filename): base = Path(output_dir).resolve() destination = (base / filename).resolve() if destination.parent != base: raise ValueError("Chart destination escapes the output directory") return destination ``` 3. Use the sanitized component at every affected sink: ```python component = safe_filename_component(col) path = confined_output_path(out, f"{component}_histogram.png") ``` 4. Apply equivalent protection to histogram, bar, pie, and scatter filenames. 5. Reject column-derived names containing path separators or control characters as defense in depth. 6. Add regression tests using column names containing `../`, `..\`, absolute paths, Unicode separators, quotes, and very long strings. 7. Consider using generated identifiers for filenames and retaining the original column name only as escaped chart metadata. ]]>
