T09 · Insecure Skill Coding Practices
- Location
scripts/generate_interest_calc.py:267- Finding
Spreadsheet Formula Injection Through Untrusted Workbook Inputs
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate_interest_calc.py, lines 267, 373, 407, and 433
Vulnerability Type: XLSX formula injection and insufficient input validation
Risk Level: MediumVulnerable Code
python # Line 267: attacker-controlled title written directly to a cell c = ws.cell(row=row, column=1, value=title)python # Lines 373-375: principal is interpolated into an Excel formula ef = f"={principal}*D{row}*C{row}/360" ec = ws.cell(row=row, column=5) ec.value = efpython # Line 407: attacker-controlled submitter written directly to a cell ws.cell(row=row, column=5, value=submitter).font = data_fontpython # Lines 431-434: --items JSON bypasses argparse's numeric type validation items = [] if args.items: items = json.loads(args.items) elif args.principal and args.start and args.end:Technical Analysis
The generator places the command-line
titleandsubmittervalues directly into XLSX cells without neutralizing formula-prefixed input. Inopenpyxl, a string beginning with=can be stored as an Excel formula rather than inert text.The
principalfield presents an additional injection path. When supplied through--principal,argparseenforcestype=float. However, entries supplied through--itemsare only parsed withjson.loads()and are not validated against a schema. An attacker can therefore provide a string instead of a number. That string is interpolated directly into the formula:python ef = f"={principal}*D{row}*C{row}/360"This permits attacker-controlled Excel formula syntax to be inserted into the generated workbook. There is also no validation that rates are finite numbers, dates form a valid ordered interval, or item objects contain values of the expected types.
The generated workbook is intended for delivery as a legal document, making it plausible that another person will open it in Excel, LibreOffice, or similar software. Depending on the spr ...[truncated 2082 chars]
- Remediation
View remediation
Remediation Suggestions
-
Enforce a schema for every
--itemsentry.- Require
principalandrateto be numeric values. - Reject booleans, strings, arrays, and objects in numeric fields.
- Require
startandendto be valid ISO dates. - Require the start date to be no later than the end date.
- Reject unknown keys where practical.
- Require
-
Require finite, bounded numeric values.
python import math def validate_number(value, field, minimum=None, maximum=None): if isinstance(value, bool) or not isinstance(value, (int, float)): raise ValueError(f"{field} must be numeric") value = float(value) if not math.isfinite(value): raise ValueError(f"{field} must be finite") if minimum is not None and value < minimum: raise ValueError(f"{field} is below the permitted minimum") if maximum is not None and value > maximum: raise ValueError(f"{field} exceeds the permitted maximum") return valueApply this validation to each item's
principaland optionalratebefore callinggenerate_excel().- Neutralize formula-prefixed text before writing it to a cell.
python def safe_excel_text(value): text = str(value) if text.startswith(("=", "+", "-", "@")): return "'" + text return textUse this function for
title,submitter, and any other user-controlled text written to the workbook:python c = ws.cell(row=row, column=1, value=safe_excel_text(title)) ws.cell( row=row, column=5, value=safe_excel_text(submitter), ).font = data_font- Construct formulas only from validated numeric values.
python principal = validate_number( item["principal"], "principal", minimum=0.01, ) ef = f"={principal:.2f}*D{row}*C{row}/360"An even safer design is to place the validated principal in a dedicated numeric cell and make formulas reference that cell instead of interpolating input into formula source text.
- **Add regre ...[truncated 684 chars]
-
