T09 · Insecure Skill Coding Practices
- Location
scripts/extract_hcp.py:82- Finding
Spreadsheet Formula Injection Through Untrusted Export Values
- Content
View full analysis
Vulnerability Details
File Location:
scripts/extract_hcp.py:43-65, 82-92, 189-204
Vulnerability Type: Spreadsheet formula injection
Risk Level: MediumVulnerable Code
python def parse_tsv_scalar(value: str) -> object: text = value.strip() if not text: return None if text.upper() == "TRUE": return True if text.upper() == "FALSE": return False if re.fullmatch(r"[+-]?[0-9]+", text): if len(text.lstrip("+-")) > 1 and text.lstrip("+-").startswith("0"): return text try: return int(text) except ValueError: return text if re.fullmatch(r"[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.[0-9]*)(?:[Ee][+-]?[0-9]+)?", text): try: return float(text) except ValueError: return text return textpython if suffix == ".xls": with path.open("r", encoding="utf-16") as handle: reader = csv.reader(handle, delimiter="\t") rows = [[parse_tsv_scalar(cell) for cell in row] for row in reader] wb = Workbook() ws = wb.active ws.title = safe_sheet_title(path.stem) for row_index, row in enumerate(rows, start=1): for col_index, value in enumerate(row, start=1): ws.cell(row=row_index, column=col_index, value=value)python ws.cell(row=row, column=start_col, value=group.title) ... ws.cell(row=row, column=start_col, value=sample_value) ws.cell(row=row, column=start_col + 1, value=mean_value) ws.cell(row=row, column=start_col + 2, value=cv_value)Technical Analysis
The script accepts data from an untrusted UTF-16, tab-delimited
.xlsexport and writes parsed strings directly into a newly generated XLSX workbook.parse_tsv_scalar()performs type conversion but does not neutralize strings beginning with a spreadsheet formula marker, particularly=.OpenPyXL tre ...[truncated 2162 chars]
- Remediation
View remediation
Remediation Suggestions
Introduce a centralized safe-cell conversion function and apply it to every untrusted textual value written to a workbook, including raw imported cells, group titles, headers, sample names, means, and CV values.
Recommended controls:
- Treat formula-leading input as literal text. At minimum, neutralize strings whose first non-whitespace character is
=. - For defense in depth and interoperability with other spreadsheet writers, consider handling
+,-, and@when they are not valid values expected by the relevant field. - Explicitly set untrusted textual cells to the text data type rather than relying solely on visual formatting.
- Preserve legitimate numeric and Boolean values while sanitizing only string values.
- Add regression tests using malicious sample names, group titles, and headers.
- Document that input workbooks and text exports are untrusted data.
Example hardening approach:
python def safe_excel_value(value: object) -> object: if not isinstance(value, str): return value stripped = value.lstrip() if stripped.startswith("="): return "'" + value return valueApply the function at all workbook write boundaries:
python cell = ws.cell( row=row_index, column=col_index, value=safe_excel_value(value), )If preserving the exact displayed value without a visible apostrophe is required, explicitly write the value as a text cell and verify the resulting XLSX cell type in automated tests. Sanitization must occur before every assignment of attacker-controlled text, not only in the summary sheet.
- Treat formula-leading input as literal text. At minimum, neutralize strings whose first non-whitespace character is
