CVE-2021-25962 (published 2021-09-29T14:15Z)

“Shuup” application in versions 0.4.2 to 2.10.8 is affected by the “Formula Injection” vulnerability. A customer can inject payloads in the name input field in the billing address while buying a product. When a store administrator accesses the reports page to export the data as an Excel file and opens it, the payload gets executed.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-1236
cwe_name: Improper Neutralization of Formula Elements in a CSV File
description: The product saves user-provided information into a Comma-Separated Value (CSV) file, but it does not neutralize or incorrectly neutralizes special elements that could be interpreted as a command when the file is opened by a spreadsheet product.
extended_description: User-provided data is often saved to traditional databases.  This data can be exported to a CSV file, which allows users to read the data using spreadsheet software such as Excel, Numbers, or Calc.  This software interprets entries beginning with '=' as formulas, which are then executed by the spreadsheet software.  The software's formula language often allows methods to access hyperlinks or the local command line, and frequently allows enough characters to invoke an entire script. Attackers can populate data fields which, when saved to a CSV file, may attempt information exfiltration or other malicious activity when automatically executed by the spreadsheet software.
			

Fix information:
repo_url: https://github.com/shuup/shuup
commit_details: repo_url: https://github.com/shuup/shuup
author: Christian Hess
author_date: 2021-07-07 14:05:24-03:00
msg: Reports: clean malicius content from the HTML and CSV exporters
repository: repo_url: https://github.com/shuup/shuup
repo_name: shuup/shuup
description: E-Commerce Platform
repo_language: Python
owner: shuup
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -14,6 +14,7 @@ List all changes after the last release here (newer on top). Each change on a se
 
 ### Changed
 
+- Reports: clean malicius content from the HTML and CSV exporters
 - Reports: prevent formulas from being exported in excel writer
 - Tests: log errors into a log file
 - Admin: hide email template button based on permission

programming_language: Markdown
filename: writer.py
change_type: ModificationType.MODIFY
diff: @@ -5,6 +5,7 @@
 #
 # This source code is licensed under the OSL-3.0 license found in the
 # LICENSE file in the root directory of this source tree.
+import bleach
 import csv
 import six
 from babel.dates import format_datetime
@@ -154,6 +155,13 @@ def format_data(data, format_iso_dates=False, format_money_values=False):
     return data
 
 
+def remove_unsafe_chars(data):
+    if isinstance(data, str):
+        return "".join([char for char in data if char not in ("=", "+", "-")])
+
+    return data
+
+
 class CSVReportWriter(ReportWriter):
     content_type = "text/csv"
     extension = ".csv"
@@ -170,12 +178,12 @@ class CSVReportWriter(ReportWriter):
         self.data.append([c["title"] for c in report.schema])
         for datum in report_data:
             datum = report.read_datum(datum)
-            self.data.append([format_data(data, format_iso_dates=True) for data in datum])
+            self.data.append([format_data(remove_unsafe_chars(data), format_iso_dates=True) for data in datum])
 
         if has_totals:
             for datum in report.get_totals(report_data):
                 datum = report.read_datum(datum)
-                self.data.append([format_data(data) for data in datum])
+                self.data.append([format_data(remove_unsafe_chars(data)) for data in datum])
 
     def get_rendered_output(self):
         f = StringIO()
@@ -206,13 +214,13 @@ class ExcelReportWriter(ReportWriter):
         self.worksheet.append([c["title"] for c in report.schema])
         for datum in report_data:
             datum = report.read_datum(datum)
-            self.worksheet.append([format_data(data) for data in datum])
+            self.worksheet.append([format_data(remove_unsafe_chars(data)) for data in datum])
             self._convert_row_to_string()
 
         if has_totals:
             for datum in report.get_totals(report_data):
                 datum = report.read_datum(datum)
-                self.worksheet.append([format_data(data) for data in datum])
+                self.worksheet.append([format_data(remove_unsafe_chars(data)) for data in datum])
                 self._convert_row_to_string()
 
     def write_page_heading(self, text):
@@ -264,7 +272,7 @@ class HTMLReportWriter(ReportWriter):
         self.output.append(mark_safe(content))
 
     def _w(self, content):
-        self.output.append(format_data(content, format_money_values=True))
+        self.output.append(bleach.clean(str(format_data(content, format_money_values=True)), strip=True))
 
     def _w_tag(self, tag, content):
         self._w_raw("<%s>" % tag)

programming_language: Python