From c6bba842af621c5a634bfc4798bb13ae8c43ed00 Mon Sep 17 00:00:00 2001 From: Tatiana Ovchinnikova Date: Thu, 21 Mar 2024 15:43:39 -0500 Subject: [PATCH] Sanitize data for CSV generation CSV generation is not fully sanitized to prevent CSV injection. According to https://owasp.org/www-community/attacks/CSV_Injection, we have to use the following sanitization: - Wrap each cell field in double quotes - Prepend each cell field with a single quote - Escape every double quote using an additional double quote The patch https://review.opendev.org/c/openstack/horizon/+/679161 takes care of the double quotes. This patch adds a single quote to the cell fields beginning with specific characters, so their content will be read by a spreadsheet editor as text, not a formula. Closes-Bug: #2048106 Change-Id: I882fe376613ff1dc13a61f38b59d2a2567dbba7d --- horizon/utils/csvbase.py | 5 ++++- openstack_dashboard/dashboards/identity/projects/tests.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/horizon/utils/csvbase.py b/horizon/utils/csvbase.py index 4009cc44c6..656be1e869 100644 --- a/horizon/utils/csvbase.py +++ b/horizon/utils/csvbase.py @@ -57,7 +57,10 @@ class CsvDataMixin(object): self.writer.writerow([self.encode(col) for col in args]) def encode(self, value): - return str(value) + data = str(value) + if data and data[0] in ('=', '+', '-', '@', chr(9), chr(13)): + return "'" + data + return data class BaseCsvResponse(CsvDataMixin, HttpResponse): diff --git a/openstack_dashboard/dashboards/identity/projects/tests.py b/openstack_dashboard/dashboards/identity/projects/tests.py index 635f981238..7f5dbff9ec 100644 --- a/openstack_dashboard/dashboards/identity/projects/tests.py +++ b/openstack_dashboard/dashboards/identity/projects/tests.py @@ -1226,10 +1226,10 @@ class UsageViewTests(test.BaseAdminViewTests): hdr = ('"Instance Name","VCPUs","RAM (MB)","Disk (GB)",' '"Usage (Hours)","Age (Seconds)","State"') self.assertContains(res, '%s\r\n' % hdr) - usage_1_quoted = ('"=cmd|\' /C calc\'!A0","1","512","0","122.87",' + usage_1_quoted = ('"\'=cmd|\' /C calc\'!A0","1","512","0","122.87",' '"442321","Active"') self.assertContains(res, '%s\r\n' % usage_1_quoted) - usage_2_quoted = ('"=cmd|\' /C calc\'!A0","1","512","0","2.61",' + usage_2_quoted = ('"\'=cmd|\' /C calc\'!A0","1","512","0","2.61",' '"9367","Active"') self.assertContains(res, '%s\r\n' % usage_2_quoted)