mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-10 10:57:52 +00:00
Export escape fix (#13110)
* Allow numeric values to be exported unescapted * Updated docs * Explicit regression tests
This commit is contained in:
1 parent
8075ef58d9
commit
6c65d96ece
3 files changed
+65
-2
No files matched your search
@@ -515,11 +515,20 @@ def escape_export_value(value):
|
||||
|
||||
String values which start with a character that a spreadsheet application
|
||||
may interpret as a formula are prefixed with a single quote character.
|
||||
Non-string values are returned unchanged.
|
||||
Non-string values are returned unchanged, as are strictly numeric
|
||||
strings (e.g. "-222", "+1.5", "12e6"), which are not a formula risk.
|
||||
|
||||
Ref: https://owasp.org/www-community/attacks/CSV_Injection
|
||||
"""
|
||||
if isinstance(value, str) and value.startswith(('=', '+', '-', '@', '\t', '\r')):
|
||||
if not isinstance(value, str):
|
||||
return value
|
||||
|
||||
if value.startswith(('+', '-')) and re.fullmatch(
|
||||
r'[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?', value
|
||||
):
|
||||
return value
|
||||
|
||||
if value.startswith(('=', '+', '-', '@', '\t', '\r')):
|
||||
return "'" + value
|
||||
|
||||
return value
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""Unit test for the exporter plugins."""
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from InvenTree.helpers import escape_export_value
|
||||
from InvenTree.unit_test import InvenTreeAPITestCase
|
||||
from plugin.registry import registry
|
||||
|
||||
@@ -152,3 +154,38 @@ class FormulaInjectionExportTest(InvenTreeAPITestCase):
|
||||
names.add(cell.value)
|
||||
|
||||
self.assertEqual(names, set(self.NAMES.values()))
|
||||
|
||||
|
||||
class EscapeExportValueTest(TestCase):
|
||||
"""Unit tests for the escape_export_value helper."""
|
||||
|
||||
def test_numeric_unchanged(self):
|
||||
"""Strictly numeric values are not escaped, even with a +/- prefix."""
|
||||
for value in ['123', '-222', '+5', '123.4', '-123.4', '+.5', '12e6', '-1.5E-3']:
|
||||
self.assertEqual(escape_export_value(value), value)
|
||||
|
||||
def test_non_numeric_escaped(self):
|
||||
"""Non-numeric values with a dangerous prefix are escaped."""
|
||||
for value in [
|
||||
'=2+2',
|
||||
'+1+1',
|
||||
'-1-1',
|
||||
'-12V Regulator',
|
||||
'-',
|
||||
'+',
|
||||
'-1e',
|
||||
'-nan',
|
||||
'-inf',
|
||||
'-1 ',
|
||||
'- 1',
|
||||
'-1,2',
|
||||
'@SUM(1)',
|
||||
'\tabc',
|
||||
'\rabc',
|
||||
]:
|
||||
self.assertEqual(escape_export_value(value), "'" + value)
|
||||
|
||||
def test_other_values(self):
|
||||
"""Safe strings and non-string values are returned unchanged."""
|
||||
for value in ['Safe Part', '', 'a-b', 42, -42, 1.5, None, True]:
|
||||
self.assertEqual(escape_export_value(value), value)
|
||||
Reference in new issue
Block a user