T09 · Insecure Skill Coding Practices
- Location
scripts/filter.py:309- Finding
Destructive input truncation through output-path aliasing in filter.py
- Content
View full analysis
Vulnerability Details
File Location:
scripts/filter.py:309-322
Vulnerability Type: Input/output path aliasing and unsafe non-atomic file replacement
Risk Level: HighVulnerable Code
python with open(out_path, "w", encoding="utf-8", newline="") as fout: if fmt == "jsonl": writer = fout def emit(row): fout.write(json.dumps(row, ensure_ascii=False) + "\n") else: delim = "\t" if fmt == "tsv" else "," csvw = csv.DictWriter(fout, fieldnames=out_cols, delimiter=delim, extrasaction="ignore") csvw.writeheader() def emit(row): csvw.writerow(row) with open_table(in_path) as (_kind, _hdr, reader):Technical Analysis
The output file is opened with mode
"w"before the processing pass opens the input. Opening a file in this mode immediately truncates it. The script does not verify thatout_pathandin_pathrefer to different filesystem objects.A textual path comparison alone would also be insufficient because the same file can be referenced through relative-path normalization, symbolic links, or hard links. Although
safe_path()restricts path characters, it does not enforce input/output separation and therefore does not prevent this condition.Attack Path
- The caller supplies an existing CSV file as both input and output:
bash python3 scripts/filter.py data.csv data.csv --where "id > 0" - The script initially verifies that
data.csvis a file and reads its header. - It opens
out_pathwith"w", immediately truncatingdata.csv. - It then reopens
in_pathfor the processing pass. - The input now contains no original records, so the source data is permanently replaced by an empty or header-only result.
The same result can be induced using a symbolic-link or hard-link output that aliases the input.
Impact Assessment
...[truncated 395 chars]
- The caller supplies an existing CSV file as both input and output:
- Remediation
View remediation
Remediation Suggestions
- Resolve input and output paths before processing and reject direct equality.
- If the output already exists, use
os.path.samefile(in_path, out_path)to detect symbolic-link and hard-link aliases. - Write output to a securely created temporary file in the destination directory.
- Flush and close the temporary file successfully before replacing the destination with
os.replace(). - On failure, delete the temporary file and preserve the original input.
- Add regression tests covering identical paths, normalized relative paths, symbolic links, hard links, and failures during row processing.
