T09 · Insecure Skill Coding Practices
- Location
scripts/order_ocr_cli.py:169- Finding
Spreadsheet Formula Injection in CSV Exports
- Content
View full analysis
Vulnerability Details
File Location:
scripts/order_ocr_cli.py, lines 169–173
Vulnerability Type: CSV/spreadsheet formula injection
Risk Level: MediumVulnerable Code
python def write_csv(rows: list[dict[str, object]], stream: io.TextIOBase) -> None: writer = csv.writer(stream) writer.writerow(["姓名", "原始品項", "對應品項", "數量", "來源", "來源檔案", "對應狀態", "信心"]) for row in rows: writer.writerow([row["person"], row["item"], row["matched_item"], row["qty"], row["source"], row["source_file"], row["match_status"], row["confidence"]])Technical Analysis
The application exports attacker-controlled chat and OCR values directly into CSV cells. The
csv.writerclass correctly escapes CSV delimiters and quotation marks, but it does not protect against spreadsheet formulas.Spreadsheet applications may interpret cells beginning with formula prefixes such as
=,+,-, or@as executable formulas rather than plain text. Leading whitespace or control characters may also be used to conceal these prefixes.An attacker can provide an order item beginning with a formula expression and ending with a quantity marker required by the parser. For example, an input structurally resembling the following can cause the item field to begin with
=:text Customer =1+1 +1The parsed item is subsequently written to the CSV without formula neutralization. More elaborate spreadsheet formulas may attempt to create misleading hyperlinks, initiate external resource requests, or use application-specific functionality.
This issue applies primarily to fields derived from untrusted input, including
person,item,matched_item, andsource_file. Catalog values can also become dangerous if an untrusted catalog is supplied.Attack Path
- An attacker submits crafted chat or OCR content containing an order item whose first significant character is a spreadsheet formula prefix.
- `pa ...[truncated 1248 chars]
- Remediation
View remediation
Remediation Suggestions
Sanitize every string written to CSV when it may originate from chat text, OCR output, file paths, or user-supplied catalogs.
- Detect formula prefixes after removing or accounting for leading whitespace and control characters.
- Prefix dangerous values with a single quote so spreadsheet applications treat them as text.
- Apply protection to every exported textual field rather than only
personanditem. - Preserve the original value separately in JSON if exact, unsanitized data is required for programmatic processing.
- Document that CSV output is spreadsheet-safe text rather than a lossless serialization format.
- Add regression tests for values beginning with
=,+,-,@, tabs, carriage returns, and leading spaces.
Example hardening approach:
python FORMULA_PREFIXES = ("=", "+", "-", "@") def safe_csv_cell(value: object) -> object: if not isinstance(value, str): return value candidate = value.lstrip(" \t\r\n") if candidate.startswith(FORMULA_PREFIXES): return "'" + value return value def write_csv(rows: list[dict[str, object]], stream: io.TextIOBase) -> None: writer = csv.writer(stream) writer.writerow(["姓名", "原始品項", "對應品項", "數量", "來源", "來源檔案", "對應狀態", "信心"]) for row in rows: values = [ row["person"], row["item"], row["matched_item"], row["qty"], row["source"], row["source_file"], row["match_status"], row["confidence"], ] writer.writerow([safe_csv_cell(value) for value in values])
