T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:315- Finding
CSV Formula Injection Through Untrusted Report Data
- Content
View full analysis
Vulnerability Details
File Location:
scripts/main.py:315-324andscripts/extract_pollutant_info.py:75-84
Vulnerability Type: CSV formula injection
Risk Level: MediumVulnerable Code
scripts/main.py:315-324:python for idx, row in df.iterrows(): if idx == 0: # Skip the header continue record = {} # Extract fields according to the table structure for col in df.columns: val = row.get(col) if val and not pd.isna(val): record[str(col)] = str(val).strip()scripts/extract_pollutant_info.py:75-84:python # Collect all possible field names all_fieldnames = set() for record in data: all_fieldnames.update(record.keys()) # Convert to an ordered list fieldnames = list(all_fieldnames) with open(filename, 'w', newline='', encoding='utf-8-sig') as f: writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction='ignore') writer.writeheader() writer.writerows(data)Technical Analysis
The application processes user-supplied PDF and DOCX tables and copies cell values into CSV records without neutralizing spreadsheet formula prefixes. A value beginning with
=,+,-, or@is written unchanged.CSV itself does not execute code, but common spreadsheet applications may interpret such values as formulas when a generated file is opened. Depending on the spreadsheet application and its security settings, malicious formulas may create deceptive content, initiate external requests, expose spreadsheet-accessible information, or invoke dangerous legacy functionality.
CSV quoting performed by Python's
csvmodule does not prevent this issue because spreadsheet applications still evaluate a quoted cell's decoded content as a formula.Attack Path
- An attacker creates an environmental report containing a table cell whose value starts with a spreadsheet formula character, such as `=HYPER ...[truncated 1112 chars]
- Remediation
View remediation
Remediation Suggestions
Implement a centralized CSV-cell sanitizer and apply it to every field and header before serialization. At minimum, neutralize values whose first non-whitespace character is
=,+,-, or@. Prefixing such values with a single quote is commonly used for spreadsheet-oriented CSV output, although the exact strategy should be verified against supported spreadsheet applications.Example:
python DANGEROUS_CSV_PREFIXES = ("=", "+", "-", "@") def sanitize_csv_cell(value): if value is None: return value text = str(value) if text.lstrip().startswith(DANGEROUS_CSV_PREFIXES): return "'" + text return text def sanitize_record(record): return { sanitize_csv_cell(key): sanitize_csv_cell(value) for key, value in record.items() }Sanitize records before calling
writer.writerows():python safe_data = [sanitize_record(record) for record in data] writer.writerows(safe_data)Apply equivalent protection to every CSV exporter in:
scripts/extract_pollutant_info.pyscripts/extract_emission_standards.pyscripts/extract_source_calculation.py
Add regression tests covering leading formula characters, leading whitespace before formula characters, tabs, carriage returns, multiline cells, and ordinary negative numeric values. Where preservation of numeric types is important, define an explicit schema rather than broadly treating all values beginning with
-as malicious.
