{"title":"CSV Formula Injection via Untrusted Exported Fields","language":"Python","severity":"High","cwe":"CWE-1236","source_lines":[6],"flow_lines":[6,7,10,11],"sink_lines":[11],"vulnerable_code":"import csv\nfrom flask import request, send_file\n\n@app.route('/export_iot_telemetry', methods=['POST'])\ndef export_device_metrics():\n    device_id = request.json.get('device_id')\n    sensor_readings = db.query(f\"SELECT timestamp, sensor_name, value, unit FROM telemetry WHERE device={device_id}\")\n    output_path = f'/tmp/device_{device_id}_report.csv'\n    with open(output_path, 'w', newline='') as csvfile:\n        writer = csv.writer(csvfile)\n        writer.writerow(['Timestamp', 'Sensor', 'Reading', 'Unit'])\n        for row in sensor_readings:\n            writer.writerow([row[0], row[1], row[2], row[3]])\n    return send_file(output_path, as_attachment=True, download_name=f'telemetry_{device_id}.csv')","explanation":"The application retrieves sensor telemetry data from the database without sanitizing values that may contain CSV formula injection payloads (formulas starting with =, +, -, @). When these untrusted values are written directly to CSV via writer.writerow(), Excel or other spreadsheet applications will execute the formulas, potentially leading to remote code execution or data exfiltration.","remediation":"The fix introduces a sanitize_csv_value() function that prefixes cell values starting with dangerous characters (=, +, -, @, tab, carriage return, newline) with a single quote, preventing spreadsheet applications from interpreting them as formulas. Additionally, the SQL query was changed from an f-string to a parameterized query to also address the SQL injection vulnerability present in the original code.","secure_code":"import csv\nimport re\nfrom flask import request, send_file\n\n\ndef sanitize_csv_value(value):\n    \"\"\"Sanitize a value to prevent CSV formula injection.\n    \n    Prefixes dangerous characters with a single quote to prevent\n    spreadsheet applications from interpreting them as formulas.\n    \"\"\"\n    if value is None:\n        return ''\n    str_value = str(value)\n    # Characters that can trigger formula execution in spreadsheet applications\n    dangerous_prefixes = ('=', '+', '-', '@', '\\t', '\\r', '\\n')\n    if str_value.startswith(dangerous_prefixes):\n        return \"'\" + str_value\n    return str_value\n\n\n@app.route('/export_iot_telemetry', methods=['POST'])\ndef export_device_metrics():\n    device_id = request.json.get('device_id')\n    # Use parameterized query to prevent SQL injection\n    sensor_readings = db.query(\n        \"SELECT timestamp, sensor_name, value, unit FROM telemetry WHERE device = :device_id\",\n        {'device_id': device_id}\n    )\n    output_path = f'/tmp/device_{device_id}_report.csv'\n    with open(output_path, 'w', newline='') as csvfile:\n        writer = csv.writer(csvfile)\n        writer.writerow(['Timestamp', 'Sensor', 'Reading', 'Unit'])\n        for row in sensor_readings:\n            writer.writerow([\n                sanitize_csv_value(row[0]),\n                sanitize_csv_value(row[1]),\n                sanitize_csv_value(row[2]),\n                sanitize_csv_value(row[3])\n            ])\n    return send_file(output_path, as_attachment=True, download_name=f'telemetry_{device_id}.csv')"}