T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/parse_buyma_csv.py:57
- Finding
- Untrusted CSV Values Can Be Written as Executable Spreadsheet Formulas<![CDATA[ ## Vulnerability Details **File Location**: `scripts/parse_buyma_csv.py:57-69`, `scripts/build_order_sheet.py:36-40`, `scripts/validate_output.py:13-18` **Vulnerability Type**: Spreadsheet formula injection **Risk Level**: High ### Vulnerable Code `scripts/parse_buyma_csv.py:57-69`: ```python record = { "order_no": order_no, "memo_no": (raw[FIELD_INDEX["memo"]] or "").strip(), "ship_method_raw": (raw[FIELD_INDEX["ship_method"]] or "").strip(), "ship_method": map_ship_method(raw[FIELD_INDEX["ship_method"]]), "product_name_ko": (raw[FIELD_INDEX["product_name"]] or "").strip(), "price": (raw[FIELD_INDEX["price"]] or "").strip(), "option": (raw[FIELD_INDEX["option"]] or "").strip(), "qty": (raw[FIELD_INDEX["qty"]] or "").strip(), "contact_note_raw": (raw[FIELD_INDEX["contact_note"]] or "").strip(), "name_roman": (raw[FIELD_INDEX["name_roman"]] or "").strip(), "region": (raw[FIELD_INDEX["region"]] or "").strip(), } ``` `scripts/build_order_sheet.py:36-40`: ```python for rec in records: for col, key in TARGET_COLS.items(): ws[f"{col}{row}"] = rec.get(key, "") row += 1 wb.save(out_path) ``` `scripts/validate_output.py:13-18`: ```python blanks: List[Dict[str, object]] = [] for row in range(start_row, end_row + 1): missing = [col for col in REQUIRED_COLS if ws[f"{col}{row}"].value in (None, "")] if missing: blanks.append({"row": row, "missing": missing}) ``` ### Technical Analysis Fields obtained from the BUYMA CSV are preserved as strings and subsequently assigned directly to `openpyxl` cells. In particular, `openpyxl` treats strings beginning with `=` as formulas when they are assigned to cells. The affected data includes product names, prices, options, quantities, shipping information, and notes. No validation or neutralization occurs before these values are written. The output validator only checks whether required cells are blank; it does not inspect the cell data type ...[truncated 1790 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Treat every CSV-derived field as untrusted. 2. Reject formula values in fields that should be numeric, such as price and quantity, and parse them into strict numeric types. 3. For textual fields, neutralize values that begin with formula indicators. At minimum, reject or escape leading `=` characters. Consider leading whitespace and control characters before checking. 4. Write untrusted content explicitly as text rather than allowing `openpyxl` to infer a formula: ```python def safe_spreadsheet_text(value: object) -> str: text = "" if value is None else str(value) stripped = text.lstrip() if stripped.startswith(("=", "+", "-", "@")): return "'" + text return text cell = ws[f"{col}{row}"] cell.value = safe_spreadsheet_text(rec.get(key, "")) cell.data_type = "s" ``` 5. Do not apply textual escaping to legitimate numeric fields. Validate and convert those fields to `int`, `Decimal`, or another expected numeric type instead. 6. Extend `validate_output.py` to inspect `cell.data_type` and fail if any externally populated cell is a formula: ```python if cell.data_type == "f": formula_cells.append(cell.coordinate) ``` 7. Add tests using values with leading `=`, whitespace followed by `=`, and other common formula prefixes. 8. Do not send or publish the workbook if formula validation fails. ]]>
