Skip to content

Commit f0e6224

Browse files
committed
N°6977 - Sanitize Excel formulas in export in the backoffice
1 parent aede5ea commit f0e6224

24 files changed

+350
-4
lines changed

core/csvbulkexport.class.inc.php

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* @license http://opensource.org/licenses/AGPL-3.0
66
*/
77

8+
use Combodo\iTop\Application\Helper\ExportHelper;
89
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
910
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
1011
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
@@ -13,7 +14,6 @@
1314
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
1415
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
1516
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
16-
use Combodo\iTop\Application\Helper\ExportHelper;
1717
use Combodo\iTop\Application\WebPage\Page;
1818
use Combodo\iTop\Application\WebPage\WebPage;
1919

@@ -55,6 +55,8 @@ public function ReadParameters()
5555
$this->aStatusInfo['charset'] = strtoupper(utils::ReadParam('charset', 'UTF-8', true, 'raw_data'));
5656
$this->aStatusInfo['formatted_text'] = (bool)utils::ReadParam('formatted_text', 0, true);
5757

58+
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
59+
5860
$sDateFormatRadio = utils::ReadParam('csv_date_format_radio', '');
5961
switch ($sDateFormatRadio) {
6062
case 'default':
@@ -223,6 +225,10 @@ public function GetFormPart(WebPage $oP, $sPartId)
223225
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
224226
$oFieldSetDate->AddSubBlock($oRadioCustom);
225227

228+
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
229+
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
230+
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
231+
226232
$oP->add_ready_script(
227233
<<<EOF
228234
$('#form_part_csv_options').on('preview_updated', function() { FormatDatesInPreview('csv', 'csv'); });
@@ -264,7 +270,13 @@ protected function GetValue($oObj, $sAttCode)
264270
default:
265271
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
266272
}
267-
return $sRet;
273+
274+
// If the option to ignore Excel sanitization is set, return the raw value without sanitization
275+
if (array_key_exists('ignore_excel_sanitization', $this->aStatusInfo) && $this->aStatusInfo['ignore_excel_sanitization'] === true) {
276+
return $sRet;
277+
}
278+
279+
return ExportHelper::SanitizeField($sRet, $this->aStatusInfo['text_qualifier'] ?? '');
268280
}
269281

270282
public function GetHeader()
@@ -337,6 +349,12 @@ public function GetNextChunk(&$aStatus)
337349
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput, !$this->aStatusInfo['formatted_text']);
338350
}
339351
}
352+
353+
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
354+
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
355+
$sField = ExportHelper::SanitizeField($sField, $this->aStatusInfo['text_qualifier']);
356+
}
357+
340358
if ($this->aStatusInfo['charset'] != 'UTF-8') {
341359
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
342360
// and thus to convert field by field and not the whole row or file at once (see ticket N°991)

core/excelbulkexport.class.inc.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
* @license http://opensource.org/licenses/AGPL-3.0
66
*/
77

8+
use Combodo\iTop\Application\Helper\ExportHelper;
89
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
910
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
1011
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
1112
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
1213
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
1314
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
14-
use Combodo\iTop\Application\Helper\ExportHelper;
1515
use Combodo\iTop\Application\WebPage\Page;
1616
use Combodo\iTop\Application\WebPage\WebPage;
1717

@@ -63,6 +63,8 @@ public function ReadParameters()
6363
// Export from the command line (or scripted) => default format is SQL, as in previous versions of iTop, unless specified otherwise
6464
$this->aStatusInfo['date_format'] = utils::ReadParam('date_format', (string)AttributeDateTime::GetSQLFormat(), true, 'raw_data');
6565
}
66+
67+
$this->aStatusInfo['ignore_excel_sanitization'] = (bool)utils::ReadParam('ignore_excel_sanitization', 0, true, utils::ENUM_SANITIZATION_FILTER_INTEGER);
6668
}
6769

6870
public function EnumFormParts()
@@ -121,6 +123,10 @@ public function GetFormPart(WebPage $oP, $sPartId)
121123
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
122124
$oFieldSetDate->AddSubBlock($oRadioCustom);
123125

126+
$oFieldSetSecurity = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:Security'));
127+
$oMulticolumn->AddColumn(ColumnUIBlockFactory::MakeForBlock($oFieldSetSecurity));
128+
$oFieldSetSecurity->AddSubBlock(ExportHelper::GetInputForSanitizeExcelExport());
129+
124130
$oP->add_ready_script(
125131
<<<EOF
126132
$('#form_part_xlsx_options').on('preview_updated', function() { FormatDatesInPreview('excel', 'xlsx'); });
@@ -216,6 +222,12 @@ protected function GetValue($oObj, $sAttCode)
216222
}
217223
}
218224
}
225+
226+
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
227+
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
228+
return ExportHelper::SanitizeField($sRet, '');
229+
}
230+
219231
return $sRet;
220232
}
221233

css/backoffice/application/bulk/_all.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
*/
55

66
@import "bulk-modify";
7+
@import "bulk-export";
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/*
2+
* @copyright Copyright (C) 2010-2026 Combodo SAS
3+
* @license http://opensource.org/licenses/AGPL-3.0
4+
*/
5+
6+
#form_part_csv_options:has(#ibo-sanitize-excel-export--input:checked), #form_part_xlsx_options:has(#ibo-sanitize-excel-export--input:checked){
7+
#ibo-sanitize-excel-export--alert {
8+
display: none;
9+
}
10+
}

dictionaries/ui/application/bulk/cs.dictionary.itop.bulk.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@
1414
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
1515
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
1616
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
17+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
18+
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
19+
'Core:BulkExport:Security' => 'Security~~',
1720
]);

dictionaries/ui/application/bulk/da.dictionary.itop.bulk.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@
1414
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
1515
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
1616
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>~~',
17+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
18+
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
19+
'Core:BulkExport:Security' => 'Security~~',
1720
]);

dictionaries/ui/application/bulk/de.dictionary.itop.bulk.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@
1414
'UI:Bulk:modify:IncompatibleAttribute' => 'Dieses Attribut kann in einer Massenänderung nicht bearbeitet werden.',
1515
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel-Sicherheitswarnung',
1616
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Das Öffnen einer Datei mit nicht vertrauenswürdigen Daten in Microsoft Excel kann zu einer Formel-Injektion führen. Stellen Sie sicher, dass Ihre Excel-Einstellungen so konfiguriert sind, dass Dateien sicher verarbeitet werden. <a href="%1$s">Erfahren Sie mehr in unserer Dokumentation.</a>',
17+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
18+
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
19+
'Core:BulkExport:Security' => 'Security~~',
1720
]);

dictionaries/ui/application/bulk/en.dictionary.itop.bulk.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@
2424
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context',
2525
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning',
2626
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>',
27+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values',
28+
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.',
29+
'Core:BulkExport:Security' => 'Security',
2730
]);

dictionaries/ui/application/bulk/en_gb.dictionary.itop.bulk.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@
1111
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context',
1212
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning',
1313
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Opening a file with untrusted data in Microsoft Excel may lead to formula injection. Ensure that your Excel settings are configured to handle files safely. <a href="%1$s">Learn more in our documentation.</a>',
14+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values',
15+
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.',
16+
'Core:BulkExport:Security' => 'Security',
1417
]);

dictionaries/ui/application/bulk/es_cr.dictionary.itop.bulk.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@
1212
'UI:Bulk:modify:IncompatibleAttribute' => 'Este atributo no se puede editar en contexto masivo',
1313
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Advertencia de seguridad de Excel',
1414
'UI:Bulk:Export:MaliciousInjection:Alert:Message' => 'Abrir un archivo con datos que no son de confianza en Microsoft Excel puede provocar la inyección de fórmulas. Asegúrese de que la configuración de Excel esté configurada para manejar archivos de forma segura. <a href="%1$s">Obtenga más información en nuestra documentación.</a>',
15+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
16+
'UI:Bulk:Export:MaliciousInjection:Input:Tooltip' => 'When enabled, potentially dangerous values will be sanitized during export. This will prevent Microsoft Excel from interpreting them as formulas. Note that this may alter the original data by prefixing it with a single quote (\') to ensure it is treated as text.~~',
17+
'Core:BulkExport:Security' => 'Security~~',
1518
]);

0 commit comments

Comments
 (0)