T09 · Insecure Skill Coding Practices
- Location
scripts/generate_review_summary.py:302- Finding
Spreadsheet Formula Injection Through Untrusted Excel Input
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate_review_summary.py, lines 302-359
Vulnerability Type: Spreadsheet formula injection
Risk Level: MediumVulnerable Code
The data-reference worksheet directly writes text obtained from source workbooks:
python for seq, item in enumerate(items, 1): cell(ws, r, 1); cell(ws, r, 2, seq) ws.cell(row=r, column=3, value=item['unit_eng']) ws.cell(row=r, column=4, value=item['code']) ws.cell(row=r, column=5, value=item['name']) ws.cell(row=r, column=6, value=item['desc'][:500] if item['desc'] else '') ws.cell(row=r, column=7, value=item['unit']) for c2, k in [(8, 'send_qty'), (9, 'send_price'), (10, 'send_total'), (11, 'review_qty'), (12, 'review_price'), (13, 'review_total'), (14, 'diff')]: cell(ws, r, c2, item[k], fmt=NUM_FMT) ws.cell(row=r, column=15, value=item['reason'][:500] if item['reason'] else item['analysis']) ws.cell(row=r, column=16, value=item['cat_name']) ws.cell(row=r, column=18, value='电力工程') r += 1The detailed worksheet writes the same untrusted fields without neutralization:
python for idx, item in enumerate(cat_items): ws.cell(row=r, column=1, value=f"{cat_id}.{idx + 1}") ws.cell(row=r, column=2, value=item['unit_eng']) ws.cell(row=r, column=3, value=item['code']) ws.cell(row=r, column=4, value=item['name']) ws.cell(row=r, column=5, value=item['desc'][:500] if item['desc'] else '') ws.cell(row=r, column=6, value=item['unit']) for c2, k in [(7, 'send_qty'), (8, 'send_price'), (9, 'send_total'), (10, 'review_qty'), (11, 'review_price'), (12, 'review_total'), (13, 'diff')]: cell(ws, r, c2, item[k], fmt=NUM_FMT) ws.cell(row=r, column=14, value=item['reason'][:500] if item['reason'] else item['analysis']) ws.cell(row=r, column=15, value=item['cat_name']) r += 1Technical Analysis
Text fields imported from the ...[truncated 2707 chars]
- Remediation
View remediation
Remediation Suggestions
Introduce a centralized function for all text written to spreadsheet cells. At minimum, values beginning with
=must be converted to literal text. For defense in depth and compatibility with different spreadsheet clients and export formats, also neutralize leading+,-, and@characters after accounting for leading whitespace, tabs, carriage returns, and line feeds.python FORMULA_PREFIXES = ('=', '+', '-', '@') def safe_excel_text(value): if value is None: return '' text = str(value) normalized = text.lstrip(' \t\r\n') if normalized.startswith(FORMULA_PREFIXES): return "'" + text return textApply this function to every externally sourced textual value before assigning it to a cell:
python ws.cell(row=r, column=3, value=safe_excel_text(item['unit_eng'])) ws.cell(row=r, column=4, value=safe_excel_text(item['code'])) ws.cell(row=r, column=5, value=safe_excel_text(item['name'])) ws.cell(row=r, column=6, value=safe_excel_text(item['desc'][:500])) ws.cell(row=r, column=15, value=safe_excel_text( item['reason'][:500] if item['reason'] else item['analysis'] ))Apply equivalent protection to all other imported text and to user-controlled command-line values such as project names, company names, and notes. Numeric fields should continue to be validated and written as numeric types rather than passed through the text sanitizer.
Add regression tests that generate workbooks from values beginning with
=,+,-,@, whitespace followed by a formula prefix, tabs, and line breaks. Reload the output withopenpyxland verify that protected cells are stored as literal strings rather than formulas. Where feasible, also test the generated workbook in the spreadsheet clients used by reviewers.
