CVE-2021-29454 (PHP, published 2022-01-10T20:15Z)
Description: value: Smarty is a template engine for PHP, facilitating the separation of presentation (HTML/CSS) from application logic. Prior to versions 3.1.42 and 4.0.2, template authors could run arbitrary PHP code by crafting a malicious math string. If a math string was passed through as user provided data to the math function, external users could run arbitrary PHP code by crafting a malicious math string. Users should upgrade to version 3.1.42 or 4.0.2 to receive a patch.
Severity: MEDIUM (CVSS v3 base score: 8.8)
Weakness: CWE-74 \u2014 Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')
The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
Repository: https://github.com/smarty-php/smarty
Files changed:
CHANGELOG.md
libs/plugins/function.math.php
tests/UnitTests/TemplateSource/ValueTests/Math/MathTest.php
Commit message: Merge pull request from GHSA-29gp-2c3m-3j6m

* Temporary fix. Waiting for CVE

* Add CVE

Vulnerable code:
            'tan'   => true

Fixed code:
### Security
- Prevent arbitrary PHP code execution through maliciously crafted expression for the math function. This addresses CVE-2021-29454

            'acos'   => true,
            'acosh'   => true,
            'cosh'   => true,
            'deg2rad'   => true,
            'rad2deg'   => true,
            'asin'   => true,
            'asinh'   => true,
            'sinh'   => true,
            'atan'   => true,
            'atanh'   => true,
            'tan'   => true,
            'tanh'   => true


    // Remove whitespaces
    $equation = preg_replace('/\s+/', '', $equation);

    // Adapted from https://www.php.net/manual/en/function.eval.php#107377
    $number = '(?:\d+(?:[,.]\d+)?|pi|π)'; // What is a number
    $functionsOrVars = '((?:0x[a-fA-F0-9]+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))';
    $operators = '[+\/*\^%-]'; // Allowed math operators
    $regexp = '/^(('.$number.'|'.$functionsOrVars.'|('.$functionsOrVars.'\s*\((?1)+\)|\((?1)+\)))(?:'.$operators.'(?2))?)+$/';

    if (!preg_match($regexp, $equation)) {
        trigger_error("math: illegal characters", E_USER_WARNING);
        return;
    }





	/**
	 * @expectedException PHPUnit_Framework_Error_Warning
	 */
	public function testBackticksIllegal()
	{
		$expected = "22.00";
		$tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="`ls` x * y" x=$x y=$y}');
		$this->assertEquals($expected, $this->smarty->fetch($tpl));
	}

	/**
	 * @expectedException PHPUnit_Framework_Error_Warning
	 */
	public function testDollarSignsIllegal()
	{
		$expected = "22.00";
		$tpl = $this->smarty->createTemplate('eval:{$x = "4"}{$y = "5.5"}{math equation="$" x=$x y=$y}');
		$this->assertEquals($expected, $this->smarty->fetch($tpl));
	}

	/**
	 * @expectedException PHPUnit_Framework_Error_Warning
	 */
	public function testBracketsIllegal()
	{
		$expected = "I";
		$tpl = $this->smarty->createTemplate('eval:{$x = "0"}{$y = "1"}{math equation="((y/x).(x))[x]" x=$x y=$y}');
		$this->assertEquals($expected, $this->smarty->fetch($tpl));
	}