T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/build_ig_recruiting_outreach.py:134
- Finding
- Untrusted CSV Fields Are Exported Without Spreadsheet Formula Neutralization## Vulnerability Details **File Location**: `scripts/build_ig_recruiting_outreach.py`, lines 134-155 **Vulnerability Type**: CSV formula injection **Risk Level**: Medium ### Vulnerable Code ```python def write_messages_csv(path: Path, campaign: str, sequences: List[Dict[str, object]]) -> None: with path.open("w", encoding="utf-8", newline="") as f: writer = csv.DictWriter( f, fieldnames=[ "campaign", "instagram_handle", "target_brokerage", "stage", "message", ], ) writer.writeheader() for seq in sequences: lead = seq["lead"] # type: ignore[index] for item in seq["messages"]: # type: ignore[index] writer.writerow( { "campaign": campaign, "instagram_handle": lead["handle"], "target_brokerage": lead["target_brokerage"], "stage": item["stage"], "message": item["message"], } ) ``` ### Technical Analysis The function writes values derived from command-line arguments and the input lead CSV directly into an output CSV. In particular, `campaign` and `lead["target_brokerage"]` are not neutralized before export. CSV quoting performed by Python's `csv` module protects the file structure but does not prevent spreadsheet formula interpretation. When a cell begins with a formula indicator such as `=`, `+`, `-`, or `@`, spreadsheet software may process it as a formula rather than plain text. Leading tab or carriage-return characters can also be used to bypass incomplete validation. An attacker who can influence a lead record could therefore provide a crafted `target_brokerage` value. The generated `messages_<campaign>.csv` would retai ...[truncated 1453 chars]
- Remediation
- ## Remediation Suggestions Introduce a centralized CSV-cell neutralization function and apply it to every untrusted or externally influenced field before calling `writer.writerow()`. The hardening function should: 1. Treat values beginning with `=`, `+`, `-`, or `@` as potentially dangerous. 2. Also account for leading tabs, carriage returns, line feeds, and whitespace followed by a formula indicator. 3. Prefix dangerous values with an apostrophe or use another neutralization approach documented as safe for the spreadsheet applications supported by the workflow. 4. Preserve the original value separately in JSON if exact, machine-readable source data is required. 5. Apply protection to all exported fields rather than only `target_brokerage`, because future changes may allow additional fields to begin with attacker-controlled content. 6. Add regression tests covering every dangerous prefix, leading whitespace/control characters, quoted payloads, and ordinary benign values. 7. Document that generated CSV files contain untrusted lead data and should be imported with formula execution disabled where possible. Example defensive implementation: ```python FORMULA_PREFIXES = ("=", "+", "-", "@", "\t", "\r", "\n") def safe_csv_cell(value: object) -> str: text = str(value) probe = text.lstrip(" ") if probe.startswith(FORMULA_PREFIXES): return "'" + text return text ``` Every value in the row should then be passed through `safe_csv_cell()` before export. This function should be validated against the behavior of the spreadsheet applications used by campaign operators.
