CVE-2023-36806: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Contao is an open source content management system. Starting in version 4.0.0 and prior to versions 4.9.42, 4.13.28, and 5.1.10, it is possible for untrusted backend users to inject malicious code into headline fields in the back end, which will be executed both in the element preview (back end) and on the website (front end). Installations are only affected if there are untrusted back end users who have the rights to modify headline fields, or other fields using the input unit widget. Contao 4.9.42, 4.13.28, and 5.1.10 have a patch for this issue. As a workaround, disable the login for all untrusted back end users.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 5.4
Published: 2023-07-25T19:15Z
Repository: https://github.com/contao/contao
Language: PHP
Files changed: core-bundle/src/Resources/contao/widgets/InputUnit.php, core-bundle/src/Resources/contao/widgets/TimePeriod.php, core-bundle/src/Resources/contao/widgets/TrblField.php
Commit date: 2023-07-25 09:44:16 +0200
Commit message: Merge pull request from GHSA-4gpr-p634-922x


Diff: diff --git a/core-bundle/src/Resources/contao/widgets/InputUnit.php b/core-bundle/src/Resources/contao/widgets/InputUnit.php
index 72334580ec..5b831d81ba 100644
--- a/core-bundle/src/Resources/contao/widgets/InputUnit.php
+++ b/core-bundle/src/Resources/contao/widgets/InputUnit.php
@@ -30,16 +30,10 @@ class InputUnit extends Widget
 	 * Template
 	 * @var string
 	 */
 	protected $strTemplate = 'be_widget';
 
-	/**
-	 * Units
-	 * @var array
-	 */
-	protected $arrUnits = array();
-
 	/**
 	 * Add specific attributes
 	 *
 	 * @param string $strKey
 	 * @param mixed  $varValue
@@ -70,31 +64,37 @@ class InputUnit extends Widget
 			case 'placeholder':
 				$this->arrAttributes['placeholder'] = $varValue;
 				break;
 
 			case 'options':
-				$this->arrUnits = StringUtil::deserialize($varValue);
+				$this->arrOptions = StringUtil::deserialize($varValue);
 				break;
 
 			default:
 				parent::__set($strKey, $varValue);
 				break;
 		}
 	}
 
 	/**
-	 * Do not validate unit fields
-	 *
 	 * @param mixed $varInput
 	 *
 	 * @return mixed
 	 */
 	protected function validator($varInput)
 	{
 		foreach ($varInput as $k=>$v)
 		{
-			if ($k != 'unit')
+			if ($k == 'unit')
+			{
+				if (!$this->isValidOption($v))
+				{
+					$varInput[$k] = '';
+					$this->addError($GLOBALS['TL_LANG']['ERR']['invalid']);
+				}
+			}
+			else
 			{
 				$varInput[$k] = parent::validator($v);
 			}
 		}
 
@@ -130,11 +130,11 @@ class InputUnit extends Widget
 	 */
 	public function generate()
 	{
 		$arrUnits = array();
 
-		foreach ($this->arrUnits as $arrUnit)
+		foreach ($this->arrOptions as $arrUnit)
 		{
 			$arrUnits[] = sprintf(
 				'<option value="%s"%s>%s</option>',
 				StringUtil::specialchars($arrUnit['value']),
 				$this->isSelected($arrUnit),
diff --git a/core-bundle/src/Resources/contao/widgets/TimePeriod.php b/core-bundle/src/Resources/contao/widgets/TimePeriod.php
index 713c9e607c..098e091a20 100644
--- a/core-bundle/src/Resources/contao/widgets/TimePeriod.php
+++ b/core-bundle/src/Resources/contao/widgets/TimePeriod.php
@@ -29,16 +29,10 @@ class TimePeriod extends Widget
 	 * Template
 	 * @var string
 	 */
 	protected $strTemplate = 'be_widget';
 
-	/**
-	 * Units
-	 * @var array
-	 */
-	protected $arrUnits = array();
-
 	/**
 	 * Add specific attributes
 	 *
 	 * @param string $strKey
 	 * @param mixed  $varValue
@@ -65,31 +59,37 @@ class TimePeriod extends Widget
 				}
 				parent::__set($strKey, $varValue);
 				break;
 
 			case 'options':
-				$this->arrUnits = StringUtil::deserialize($varValue);
+				$this->arrOptions = StringUtil::deserialize($varValue);
 				break;
 
 			default:
 				parent::__set($strKey, $varValue);
 				break;
 		}
 	}
 
 	/**
-	 * Do not validate unit fields
-	 *
 	 * @param mixed $varInput
 	 *
 	 * @return mixed
 	 */
 	protected function validator($varInput)
 	{
 		foreach ($varInput as $k=>$v)
 		{
-			if ($k != 'unit')
+			if ($k == 'unit')
+			{
+				if (!$this->isValidOption($v))
+				{
+					$varInput[$k] = '';
+					$this->addError($GLOBALS['TL_LANG']['ERR']['invalid']);
+				}
+			}
+			else
 			{
 				$varInput[$k] = parent::validator($v);
 			}
 		}
 
@@ -126,16 +126,16 @@ class TimePeriod extends Widget
 	public function generate()
 	{
 		$arrUnits = array();
 
 		// Add an empty option if there are none (see #5067)
-		if (empty($this->arrUnits))
+		if (empty($this->arrOptions))
 		{
-			$this->arrUnits = array(array('value'=>'', 'label'=>'-'));
+			$this->arrOptions = array(array('value'=>'', 'label'=>'-'));
 		}
 
-		foreach ($this->arrUnits as $arrUnit)
+		foreach ($this->arrOptions as $arrUnit)
 		{
 			$arrUnits[] = sprintf(
 				'<option value="%s"%s>%s</option>',
 				StringUtil::specialchars($arrUnit['value'] ?? ''),
 				$this->isSelected($arrUnit),
diff --git a/core-bundle/src/Resources/contao/widgets/TrblField.php b/core-bundle/src/Resources/contao/widgets/TrblField.php
index 0342467229..cadeba39b5 100644
--- a/core-bundle/src/Resources/contao/widgets/TrblField.php
+++ b/core-bundle/src/Resources/contao/widgets/TrblField.php
@@ -28,16 +28,10 @@ class TrblField extends Widget
 	 * Template
 	 * @var string
 	 */
 	protected $strTemplate = 'be_widget';
 
-	/**
-	 * Units
-	 * @var array
-	 */
-	protected $arrUnits = array();
-
 	/**
 	 * Add specific attributes
 	 *
 	 * @param string $strKey
 	 * @param mixed  $varValue
@@ -52,31 +46,37 @@ class TrblField extends Widget
 					$this->arrAttributes['maxlength'] = $varValue;
 				}
 				break;
 
 			case 'options':
-				$this->arrUnits = StringUtil::deserialize($varValue);
+				$this->arrOptions = StringUtil::deserialize($varValue);
 				break;
 
 			default:
 				parent::__set($strKey, $varValue);
 				break;
 		}
 	}
 
 	/**
-	 * Do not validate unit fields
-	 *
 	 * @param mixed $varInput
 	 *
 	 * @return mixed
 	 */
 	protected function validator($varInput)
 	{
 		foreach ($varInput as $k=>$v)
 		{
-			if ($k != 'unit')
+			if ($k == 'unit')
+			{
+				if (!$this->isValidOption($v))
+				{
+					$varInput[$k] = '';
+					$this->addError($GLOBALS['TL_LANG']['ERR']['invalid']);
+				}
+			}
+			else
 			{
 				$varInput[$k] = parent::validator($v);
 			}
 		}
 
@@ -112,11 +112,11 @@ class TrblField extends Widget
 	 */
 	public function generate()
 	{
 		$arrUnits = array();
 
-		foreach ($this->arrUnits as $arrUnit)
+		foreach ($this->arrOptions as $arrUnit)
 		{
 			$arrUnits[] = sprintf(
 				'<option value="%s"%s>%s</option>',
 				StringUtil::specialchars($arrUnit['value']),
 				$this->isSelected($arrUnit),

Vulnerable code: 	/**
	 * Units
	 * @var array
	 */
	protected $arrUnits = array();

				$this->arrUnits = StringUtil::deserialize($varValue);
	 * Do not validate unit fields
	 *
			if ($k != 'unit')
		foreach ($this->arrUnits as $arrUnit)
	/**
	 * Units
	 * @var array
	 */
	protected $arrUnits = array();

				$this->arrUnits = StringUtil::deserialize($varValue);
	 * Do not validate unit fields
	 *
			if ($k != 'unit')
		if (empty($this->arrUnits))
			$this->arrUnits = array(array('value'=>'', 'label'=>'-'));
		foreach ($this->arrUnits as $arrUnit)
	/**
	 * Units
	 * @var array
	 */
	protected $arrUnits = array();

				$this->arrUnits = StringUtil::deserialize($varValue);
	 * Do not validate unit fields
	 *
			if ($k != 'unit')
		foreach ($this->arrUnits as $arrUnit)
Fixed code: 				$this->arrOptions = StringUtil::deserialize($varValue);
			if ($k == 'unit')
			{
				if (!$this->isValidOption($v))
				{
					$varInput[$k] = '';
					$this->addError($GLOBALS['TL_LANG']['ERR']['invalid']);
				}
			}
			else
		foreach ($this->arrOptions as $arrUnit)
				$this->arrOptions = StringUtil::deserialize($varValue);
			if ($k == 'unit')
			{
				if (!$this->isValidOption($v))
				{
					$varInput[$k] = '';
					$this->addError($GLOBALS['TL_LANG']['ERR']['invalid']);
				}
			}
			else
		if (empty($this->arrOptions))
			$this->arrOptions = array(array('value'=>'', 'label'=>'-'));
		foreach ($this->arrOptions as $arrUnit)
				$this->arrOptions = StringUtil::deserialize($varValue);
			if ($k == 'unit')
			{
				if (!$this->isValidOption($v))
				{
					$varInput[$k] = '';
					$this->addError($GLOBALS['TL_LANG']['ERR']['invalid']);
				}
			}
			else
		foreach ($this->arrOptions as $arrUnit)