Skip to content

Commit 3b8e079

Browse files
steffunkyMolkobain
andauthored
N°6977 - Sanitize Excel formulas in exports (#818)
* N°6977 - Sanitize Excel formulas in export in the backoffice --------- Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
1 parent fc967c0 commit 3b8e079

25 files changed

+388
-21
lines changed

core/csvbulkexport.class.inc.php

Lines changed: 20 additions & 1 deletion
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,6 +270,13 @@ protected function GetValue($oObj, $sAttCode)
264270
default:
265271
$sRet = trim($oObj->GetAsCSV($sAttCode), '"');
266272
}
273+
274+
// If the option to ignore Excel sanitization is not set or explicitly set to false, apply sanitization
275+
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
276+
return ExportHelper::SanitizeField($sRet, $this->aStatusInfo['text_qualifier'] ?? '');
277+
}
278+
279+
// The option to ignore Excel sanitization is explicitly set to true: return the raw value without sanitization
267280
return $sRet;
268281
}
269282

@@ -337,6 +350,12 @@ public function GetNextChunk(&$aStatus)
337350
$sField = $oObj->GetAsCSV($sAttCode, $this->aStatusInfo['separator'], $this->aStatusInfo['text_qualifier'], $this->bLocalizeOutput, !$this->aStatusInfo['formatted_text']);
338351
}
339352
}
353+
354+
// If the option to ignore Excel sanitization is not set or absent, sanitize the field
355+
if (!(array_key_exists('ignore_excel_sanitization', $this->aStatusInfo)) || $this->aStatusInfo['ignore_excel_sanitization'] === false) {
356+
$sField = ExportHelper::SanitizeField($sField, $this->aStatusInfo['text_qualifier']);
357+
}
358+
340359
if ($this->aStatusInfo['charset'] != 'UTF-8') {
341360
// Note: due to bugs in the glibc library it's safer to call iconv on the smallest possible string
342361
// 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+
}

datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/popup-export-excel.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</div>
1212

1313
<div id="export-feedback">
14-
<p id="export-excel-warning" class="alert alert-warning" role="alert">{{ 'UI:Bulk:Export:MaliciousInjection:Alert:Message'|dict_format(sWikiUrl)|raw }}</p>
14+
<p id="export-excel-warning" class="alert alert-warning" role="alert">{{ 'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message'|dict_format(sWikiUrl)|raw }}</p>
1515
<p class="export-message" style="text-align:center;">{{ 'ExcelExport:PreparingExport'|dict_s }}</p>
1616
<div class="progress">
1717
<div class="progress-bar" role="progressbar" style="width: 0%"

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@
1313
Dict::Add('CS CZ', 'Czech', 'Čeština', [
1414
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
1515
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
16-
'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>~~',
16+
'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" target="_blank">Learn more in our documentation.</a>~~',
17+
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
18+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
19+
'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.~~',
20+
'Core:BulkExport:Security' => 'Security~~',
1721
]);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@
1313
Dict::Add('DA DA', 'Danish', 'Dansk', [
1414
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context~~',
1515
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning~~',
16-
'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>~~',
16+
'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" target="_blank">Learn more in our documentation.</a>~~',
17+
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
18+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
19+
'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.~~',
20+
'Core:BulkExport:Security' => 'Security~~',
1721
]);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,9 @@
1313
Dict::Add('DE DE', 'German', 'Deutsch', [
1414
'UI:Bulk:modify:IncompatibleAttribute' => 'Dieses Attribut kann in einer Massenänderung nicht bearbeitet werden.',
1515
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel-Sicherheitswarnung',
16-
'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>',
16+
'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" target="_blank">Erfahren Sie mehr in unserer Dokumentation.</a>',
17+
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>~~',
18+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values~~',
19+
'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.~~',
20+
'Core:BulkExport:Security' => 'Security~~',
1721
]);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,9 @@
2323
// Bulk modify
2424
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context',
2525
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning',
26-
'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>',
26+
'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" target="_blank">Learn more in our documentation.</a>',
27+
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</a>',
28+
'UI:Bulk:Export:MaliciousInjection:Input:Label' => 'Sanitize potentially dangerous values',
29+
'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.',
30+
'Core:BulkExport:Security' => 'Security',
2731
]);

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,9 @@
1010
// Bulk modify
1111
'UI:Bulk:modify:IncompatibleAttribute' => 'This attribute can\'t be edited in bulk context',
1212
'UI:Bulk:Export:MaliciousInjection:Alert:Title' => 'Excel security warning',
13-
'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>',
13+
'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" target="_blank">Learn more in our documentation.</a>',
14+
'UI:Bulk:Export:MaliciousInjection:Sanitization:Alert:Message' => 'Some values have been sanitized to prevent potential security issues in Microsoft Excel. <a href="%1$s" target="_blank">Learn more in our documentation.</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',
1418
]);

0 commit comments

Comments
 (0)