Vulnerability: Combodo iTop is a web based IT Service Management tool. In versions prior to 3.0.0-beta6 the export CSV page don't properly escape the user supplied parameters, allowing for javascript injection into rendered csv files. Users are advised to upgrade. There are no known workarounds for this issue.
Vulnerability ID: CVE-2021-41161
CWE: CWE-79
Severity score: 2.9
Project: https://github.com/Combodo/iTop
Published: 2022-04-21
File: pages/csvimport.php
Commit message: N°4361 - XSS in csvimport on develop
Commit author: acognet

Vulnerable version (before fix):
 function ProcessCSVData(WebPage $oPage, $bSimulate = true) 	{ 		$sClassName = utils::ReadParam('class_name', '', false, 'class'); 		// Class access right check for the import 		if (UserRights::IsActionAllowed($sClassName, UR_ACTION_MODIFY) == UR_ALLOWED_NO) 		{ 			throw new CoreException(Dict::S('UI:ActionNotAllowed')); 		}  function ProcessCSVData(WebPage $oPage, $bSimulate = true) 		$sTextQualifier = utils::ReadParam('text_qualifier', '"', false, 'raw_data'); 		$bHeaderLine = (utils::ReadParam('header_line', '0') == 1); 		$iSkippedLines = 0; 		if (utils::ReadParam('box_skiplines', '0') == 1) 		{ 			$iSkippedLines = utils::ReadParam('nb_skipped_lines', '0'); 		} 		$aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data'); function SelectOptions(WebPage $oPage) 				$sCSVData = $oDocument->GetData(); 			} 			break; 			 			default: 			$sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data'); 		} function Welcome(iTopWebPage $oPage) 		$oTabPaste->AddSubBlock($oFormPaste);   		$sCSVData = utils::ReadParam('csvdata', '', false, 'raw_data'); 		$oTextarea = new TextArea('csvdata', $sCSVData, '', 120, 30); 		$oFieldPaste = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:CSVImport:PasteData'), $oTextarea); 		$oFormPaste->AddSubBlock($oFieldPaste);

Fixed version (after fix):
 function ProcessCSVData(WebPage $oPage, $bSimulate = true) 	{ 		$sClassName = utils::ReadParam('class_name', '', false, 'class'); 		// Class access right check for the import 		if (UserRights::IsActionAllowed($sClassName, UR_ACTION_MODIFY) == UR_ALLOWED_NO) { 			throw new CoreException(Dict::S('UI:ActionNotAllowed')); 		}  function ProcessCSVData(WebPage $oPage, $bSimulate = true) 		$sTextQualifier = utils::ReadParam('text_qualifier', '"', false, 'raw_data'); 		$bHeaderLine = (utils::ReadParam('header_line', '0') == 1); 		$iSkippedLines = 0; 		if (utils::ReadParam('box_skiplines', '0') == 1) { 			$iSkippedLines = utils::ReadParam('nb_skipped_lines', '0'); 		} 		$aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data'); function SelectOptions(WebPage $oPage) 				$sCSVData = $oDocument->GetData(); 			} 			break;  			default: 			$sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data'); 		} function Welcome(iTopWebPage $oPage) 		$oTabPaste->AddSubBlock($oFormPaste);   		$sCSVData = utils::ReadParam('csvdata', '', false, utils::ENUM_SANITIZATION_FILTER_STRING); 		$oTextarea = new TextArea('csvdata', $sCSVData, '', 120, 30); 		$oFieldPaste = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:CSVImport:PasteData'), $oTextarea); 		$oFormPaste->AddSubBlock($oFieldPaste);

Patch:
patch: @@ -233,8 +233,7 @@ function ProcessCSVData(WebPage $oPage, $bSimulate = true)
 	{
 		$sClassName = utils::ReadParam('class_name', '', false, 'class');
 		// Class access right check for the import
-		if (UserRights::IsActionAllowed($sClassName, UR_ACTION_MODIFY) == UR_ALLOWED_NO)
-		{
+		if (UserRights::IsActionAllowed($sClassName, UR_ACTION_MODIFY) == UR_ALLOWED_NO) {
 			throw new CoreException(Dict::S('UI:ActionNotAllowed'));
 		}
 
@@ -245,8 +244,7 @@ function ProcessCSVData(WebPage $oPage, $bSimulate = true)
 		$sTextQualifier = utils::ReadParam('text_qualifier', '"', false, 'raw_data');
 		$bHeaderLine = (utils::ReadParam('header_line', '0') == 1);
 		$iSkippedLines = 0;
-		if (utils::ReadParam('box_skiplines', '0') == 1)
-		{
+		if (utils::ReadParam('box_skiplines', '0') == 1) {
 			$iSkippedLines = utils::ReadParam('nb_skipped_lines', '0');
 		}
 		$aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data');
@@ -1150,7 +1148,7 @@ function SelectOptions(WebPage $oPage)
 				$sCSVData = $oDocument->GetData();
 			}
 			break;
-			
+
 			default:
 			$sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data');
 		}
@@ -1515,7 +1513,7 @@ function Welcome(iTopWebPage $oPage)
 		$oTabPaste->AddSubBlock($oFormPaste);
 
 
-		$sCSVData = utils::ReadParam('csvdata', '', false, 'raw_data');
+		$sCSVData = utils::ReadParam('csvdata', '', false, utils::ENUM_SANITIZATION_FILTER_STRING);
 		$oTextarea = new TextArea('csvdata', $sCSVData, '', 120, 30);
 		$oFieldPaste = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:CSVImport:PasteData'), $oTextarea);
 		$oFormPaste->AddSubBlock($oFieldPaste);
path: pages/csvimport.php