T09 · Insecure Skill Coding Practices
- Location
scripts/render_chart_svg.py:35- Finding
Unbounded CSV Rendering Can Cause Local Resource Exhaustion
- Content
View full analysis
List[Dict[str, str]]: try: with path.open("r", encoding="utf-8-sig", newline="") as handle: rows = list(csv.DictReader(handle)) ``` The resulting unbounded collection is subsequently aggregated: ```python def aggregate(rows: List[Dict[str, str]], x: str, y: str, group: Optional[str]) -> List[Dict[str, Any]]: acc: Dict[Tuple[str, str], List[float]] = defaultdict(list) for r in rows: yv = to_float(r.get(y)) if yv is None: continue xv = str(r.get(x, "")) gv = str(r.get(group, "")) if group else "" acc[(xv, gv)].append(yv) data = [] for (xv, gv), vals in acc.items(): data.append({"x": xv, "group": gv, "y": sum(vals) / len(vals), "n": len(vals)}) return data ``` ### Technical Analysis The renderer materializes the entire CSV through `list(csv.DictReader(handle))`. It does not enforce limits on input file size, row count, field length, unique categories, group cardinality, or generated SVG size. Memory consumption is amplified during processing because the original rows remain resident while values are copied into aggregation lists. The renderer then builds the complete SVG as an in-memory list of strings before joining and writing it. A CSV with many rows or high-cardinality category and group values can therefore consume substantial memory and CPU and produce an excessively large output file. Unlike `scripts/suggest_display.py`, which provides a bounded `--max-rows` sampling option, the SVG renderer has no equivalent safeguard. ### Attack Path 1. An attacker or untrusted user supplies a very large CSV, or a CSV containing extremely many unique category/group combinations. 2 ...[truncated 1142 chars]- Remediation
View remediation
= max_rows: raise SystemExit(f"Error: CSV exceeds the {max_rows} row limit.") # Update running sum and count for each key. ``` 4. **Cap cardinality and field length** - Limit the number of unique categories and groups. - Reject or truncate abnormally long headers, category labels, titles, and group names. - Fail clearly when a chart would contain more marks than the renderer can safely support. 5. **Bound output generation** - Estimate the number of SVG elements before rendering. - Enforce a maximum output byte size. - Write incrementally where practical instead of retaining the entire SVG document in memory. 6. **Constrain output dimensions** - Keep the existing minimum dimensions and add maximum width and height values. - Reject dimensions that would result in impractically large documents. 7. **Add adversarial tests** - Test oversized row counts, high-cardinality groups, extremely long fields, and large requested dimensions. - Verify that each case terminates with a controlled error rather than exhausting resources. ]]>
