T09 · Insecure Skill Coding Practices
- Location
scripts/export_coding.py:56- Finding
Spreadsheet Formula Injection in XLSX Coding Export
- Content
View full analysis
Vulnerability Details
File Location:
scripts/export_coding.py, lines 56-72
Vulnerability Type: Untrusted data written to formula-capable spreadsheet cells
Risk Level: MediumComplete Code Snippet
python values = [ code.get("theme", ""), code.get("code", ""), code.get("sub_code", ""), code.get("quote", ""), code.get("source", ""), code.get("line", ""), code.get("note", ""), ] for col, val in enumerate(values, 1): cell = ws.cell(row=row_idx, column=col, value=val) cell.alignment = Alignment(wrap_text=True, vertical="top") cell.border = border if row_fill: cell.fill = row_fill ws.row_dimensions[row_idx].height = 40Technical Analysis
All values from the input coding JSON are assigned directly to
openpyxlcells without validation or formula neutralization. This includes quotations and other fields that may originate from untrusted interview documents.When
openpyxlreceives a string beginning with=, it can store the value as a spreadsheet formula rather than literal text. Other spreadsheet applications may also interpret values beginning with+,-, or@as formulas. Consequently, attacker-controlled qualitative material can become executable spreadsheet content in the generated XLSX file.A malicious input could use a formula that requests an external resource, constructs a deceptive hyperlink, or references other workbook cells. The exact behavior depends on the spreadsheet application and its security configuration.
Attack Path
- An attacker places a formula-prefixed value in an interview document or other analyzed source.
- The malicious text is copied into a coding JSON field such as
quote,code,source, ornote. - The user runs
export_coding.pyagainst that JSON file. - The script passes the value directly to
ws.cell(..., value=val). openpyxlstores a value beginning wit ...[truncated 994 chars]
- Remediation
View remediation
Remediation Suggestions
- Treat every JSON-derived spreadsheet field as untrusted text.
- Before assigning a string to a cell, detect values whose first non-whitespace character is
=,+,-, or@. - Prefix dangerous values with an apostrophe or apply another spreadsheet-compatible text-neutralization mechanism.
- Explicitly preserve exported values as text rather than formulas.
- Validate that the top-level JSON value is a list and that each entry is an object containing values of expected types.
- Add regression tests using payloads such as formula-prefixed quotations, leading whitespace followed by a formula marker, and formula content in every exported column.
- Document that generated workbooks may contain participant-supplied data and should not contain active formulas unless explicitly required.
Example hardening logic:
python def spreadsheet_safe_text(value): if value is None: return "" value = str(value) if value.lstrip().startswith(("=", "+", "-", "@")): return "'" + value return value for col, val in enumerate(values, 1): cell = ws.cell( row=row_idx, column=col, value=spreadsheet_safe_text(val), )
