From 628f8cdf03e8373a659afe09720cd3d22ed584d9 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 21 Feb 2023 10:06:25 +0800 Subject: [PATCH] Update AMS smarty to 3.1.47 and merge with our changes --- web/private_php/ams/smarty/CHANGELOG.md | 59 ++++++++++++++++- web/private_php/ams/smarty/README.md | 2 +- .../ams/smarty/libs/Smarty.class.php | 2 +- .../plugins/function.html_select_date.php | 65 ++++++++++--------- .../smarty/libs/plugins/function.mailto.php | 38 +++++++---- .../ams/smarty/libs/plugins/function.math.php | 32 ++++++++- .../smarty/libs/plugins/modifier.escape.php | 7 +- .../libs/plugins/modifiercompiler.escape.php | 3 +- .../plugins/modifiercompiler.unescape.php | 22 +++++-- .../libs/plugins/shared.mb_str_replace.php | 34 ++++++++++ .../smarty_internal_compile_block.php | 4 +- .../smarty_internal_compile_function.php | 8 +-- .../smarty_internal_compile_include.php | 4 +- .../smarty_internal_config_file_compiler.php | 10 +-- .../smarty_internal_runtime_codeframe.php | 9 ++- .../smarty_internal_templatebase.php | 10 ++- .../smarty_internal_templatecompilerbase.php | 4 ++ .../smarty_internal_templateparser.php | 4 ++ 18 files changed, 244 insertions(+), 73 deletions(-) diff --git a/web/private_php/ams/smarty/CHANGELOG.md b/web/private_php/ams/smarty/CHANGELOG.md index e3bb93a4f3..77bb8203b8 100644 --- a/web/private_php/ams/smarty/CHANGELOG.md +++ b/web/private_php/ams/smarty/CHANGELOG.md @@ -6,11 +6,66 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- Output buffer is now cleaned for internal PHP errors as well, not just for Exceptions [#514](https://github.com/smarty-php/smarty/issues/514) + +## [3.1.47] - 2022-09-14 + +### Security +- Applied appropriate javascript and html escaping in mailto plugin to counter injection attacks [#454](https://github.com/smarty-php/smarty/issues/454) + +### Fixed +- Fixed use of `rand()` without a parameter in math function [#794](https://github.com/smarty-php/smarty/issues/794) +- Fixed unselected year/month/day not working in html_select_date [#395](https://github.com/smarty-php/smarty/issues/395) +- Updated requirement contraint for 'php' in composer.json to correctly reflect that Smarty3 does not support PHP8. Please upgrade to Smarty4 to use PHP8. + +## [3.1.46] - 2022-08-01 + +### Fixed +- Fixed problems with smarty_mb_str_replace [#549](https://github.com/smarty-php/smarty/issues/549) +- Fixed second parameter of unescape modifier not working [#777](https://github.com/smarty-php/smarty/issues/777) + +## [3.1.45] - 2022-05-17 + +### Security +- Prevent PHP injection through malicious block name or include file name. This addresses CVE-2022-29221 + +### Fixed +- Math equation `max(x, y)` didn't work anymore [#721](https://github.com/smarty-php/smarty/issues/721) + +## [3.1.44] - 2022-01-18 + +### Fixed +- Fixed illegal characters bug in math function security check [#702](https://github.com/smarty-php/smarty/issues/702) + +## [3.1.43] - 2022-01-10 + +### Security +- Prevent evasion of the `static_classes` security policy. This addresses CVE-2021-21408 + +## [3.1.42] - 2022-01-10 + +### Security +- Prevent arbitrary PHP code execution through maliciously crafted expression for the math function. This addresses CVE-2021-29454 + +## [3.1.41] - 2022-01-09 + +### Security +- Rewrote the mailto function to not use `eval` when encoding with javascript + +## [3.1.40] - 2021-10-13 + +### Changed +- modifier escape now triggers a E_USER_NOTICE when an unsupported escape type is used https://github.com/smarty-php/smarty/pull/649 + +### Security +- More advanced javascript escaping to handle https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements thanks to m-haritonov + ## [3.1.39] - 2021-02-17 ### Security -- Prevent access to `$smarty.template_object` in sandbox mode -- Fixed code injection vulnerability by using illegal function names in `{function name='blah'}{/function}` +- Prevent access to `$smarty.template_object` in sandbox mode. This addresses CVE-2021-26119. +- Fixed code injection vulnerability by using illegal function names in `{function name='blah'}{/function}`. This addresses CVE-2021-26120. ## [3.1.38] - 2021-01-08 diff --git a/web/private_php/ams/smarty/README.md b/web/private_php/ams/smarty/README.md index ee7edb1abf..b57969c48c 100644 --- a/web/private_php/ams/smarty/README.md +++ b/web/private_php/ams/smarty/README.md @@ -18,7 +18,7 @@ Smarty can be run with PHP 5.2 to PHP 7.4. > Read the NEW_FEATURES and INHERITANCE_RELEASE_NOTES file for recent extensions to Smarty 3.1 functionality -Smarty versions 3.1.11 or later are now on github and can be installed with Composer. +Smarty versions 3.1.11 or later are now on GitHub and can be installed with Composer. The "smarty/smarty" package will start at libs/.... subfolder. diff --git a/web/private_php/ams/smarty/libs/Smarty.class.php b/web/private_php/ams/smarty/libs/Smarty.class.php index 375bab1331..cc3f77d2c7 100644 --- a/web/private_php/ams/smarty/libs/Smarty.class.php +++ b/web/private_php/ams/smarty/libs/Smarty.class.php @@ -111,7 +111,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * smarty version */ - const SMARTY_VERSION = '3.1.39'; + const SMARTY_VERSION = '3.1.47'; /** * define variable scopes */ diff --git a/web/private_php/ams/smarty/libs/plugins/function.html_select_date.php b/web/private_php/ams/smarty/libs/plugins/function.html_select_date.php index 86403e3dc0..0791f1a310 100644 --- a/web/private_php/ams/smarty/libs/plugins/function.html_select_date.php +++ b/web/private_php/ams/smarty/libs/plugins/function.html_select_date.php @@ -101,6 +101,7 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem $field_separator = "\n"; $option_separator = "\n"; $time = null; + // $all_empty = null; // $day_empty = null; // $month_empty = null; @@ -113,17 +114,7 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem foreach ($params as $_key => $_value) { switch ($_key) { case 'time': - if (!is_array($_value) && $_value !== null) { - $template->_checkPlugins( - array( - array( - 'function' => 'smarty_make_timestamp', - 'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php' - ) - ) - ); - $time = smarty_make_timestamp($_value); - } + $$_key = $_value; // we'll handle conversion below break; case 'month_names': if (is_array($_value) && count($_value) === 12) { @@ -178,43 +169,59 @@ function smarty_function_html_select_date($params, Smarty_Internal_Template $tem } // Note: date() is faster than strftime() // Note: explode(date()) is faster than date() date() date() - if (isset($params[ 'time' ]) && is_array($params[ 'time' ])) { - if (isset($params[ 'time' ][ $prefix . 'Year' ])) { + + if (isset($time) && is_array($time)) { + if (isset($time[$prefix . 'Year'])) { // $_REQUEST[$field_array] given foreach (array( - 'Y' => 'Year', - 'm' => 'Month', - 'd' => 'Day' - ) as $_elementKey => $_elementName) { + 'Y' => 'Year', + 'm' => 'Month', + 'd' => 'Day' + ) as $_elementKey => $_elementName) { $_variableName = '_' . strtolower($_elementName); $$_variableName = - isset($params[ 'time' ][ $prefix . $_elementName ]) ? $params[ 'time' ][ $prefix . $_elementName ] : + isset($time[$prefix . $_elementName]) ? $time[$prefix . $_elementName] : date($_elementKey); } - } elseif (isset($params[ 'time' ][ $field_array ][ $prefix . 'Year' ])) { + } elseif (isset($time[$field_array][$prefix . 'Year'])) { // $_REQUEST given foreach (array( - 'Y' => 'Year', - 'm' => 'Month', - 'd' => 'Day' - ) as $_elementKey => $_elementName) { + 'Y' => 'Year', + 'm' => 'Month', + 'd' => 'Day' + ) as $_elementKey => $_elementName) { $_variableName = '_' . strtolower($_elementName); - $$_variableName = isset($params[ 'time' ][ $field_array ][ $prefix . $_elementName ]) ? - $params[ 'time' ][ $field_array ][ $prefix . $_elementName ] : date($_elementKey); + $$_variableName = isset($time[$field_array][$prefix . $_elementName]) ? + $time[$field_array][$prefix . $_elementName] : date($_elementKey); } } else { // no date found, use NOW - list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d')); + list($_year, $_month, $_day) = explode('-', date('Y-m-d')); } + } elseif (isset($time) && preg_match("/(\d*)-(\d*)-(\d*)/", $time, $matches)) { + $_year = $_month = $_day = null; + if ($matches[1] > '') $_year = (int) $matches[1]; + if ($matches[2] > '') $_month = (int) $matches[2]; + if ($matches[3] > '') $_day = (int) $matches[3]; } elseif ($time === null) { if (array_key_exists('time', $params)) { - $_year = $_month = $_day = $time = null; + $_year = $_month = $_day = null; } else { - list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d')); + list($_year, $_month, $_day) = explode('-', date('Y-m-d')); } } else { - list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d', $time)); + $template->_checkPlugins( + array( + array( + 'function' => 'smarty_make_timestamp', + 'file' => SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php' + ) + ) + ); + $time = smarty_make_timestamp($time); + list($_year, $_month, $_day) = explode('-', date('Y-m-d', $time)); } + // make syntax "+N" or "-N" work with $start_year and $end_year // Note preg_match('!^(\+|\-)\s*(\d+)$!', $end_year, $match) is slower than trim+substr foreach (array( diff --git a/web/private_php/ams/smarty/libs/plugins/function.mailto.php b/web/private_php/ams/smarty/libs/plugins/function.mailto.php index 27351df826..5119a15345 100644 --- a/web/private_php/ams/smarty/libs/plugins/function.mailto.php +++ b/web/private_php/ams/smarty/libs/plugins/function.mailto.php @@ -48,8 +48,13 @@ */ function smarty_function_mailto($params) { - static $_allowed_encoding = - array('javascript' => true, 'javascript_charcode' => true, 'hex' => true, 'none' => true); + static $_allowed_encoding = array( + 'javascript' => true, + 'javascript_charcode' => true, + 'hex' => true, + 'none' => true + ); + $extra = ''; if (empty($params[ 'address' ])) { trigger_error("mailto: missing 'address' parameter", E_USER_WARNING); @@ -57,11 +62,11 @@ function smarty_function_mailto($params) } else { $address = $params[ 'address' ]; } + $text = $address; + // netscape and mozilla do not decode %40 (@) in BCC field (bug?) // so, don't encode it. - $search = array('%40', '%2C'); - $replace = array('@', ','); $mail_parms = array(); foreach ($params as $var => $value) { switch ($var) { @@ -69,7 +74,7 @@ function smarty_function_mailto($params) case 'bcc': case 'followupto': if (!empty($value)) { - $mail_parms[] = $var . '=' . str_replace($search, $replace, rawurlencode($value)); + $mail_parms[] = $var . '=' . str_replace(array('%40', '%2C'), array('@', ','), rawurlencode($value)); } break; case 'subject': @@ -83,6 +88,7 @@ function smarty_function_mailto($params) default: } } + if ($mail_parms) { $address .= '?' . join('&', $mail_parms); } @@ -94,22 +100,26 @@ function smarty_function_mailto($params) ); return; } - // FIXME: (rodneyrehm) document.write() excues me what? 1998 has passed! + + $flags = ENT_QUOTES; + if (defined('ENT_SUBSTITUTE') && defined('ENT_HTML401')) { + $flags |= ENT_SUBSTITUTE | ENT_HTML401; + } + + $string = '' . htmlspecialchars($text, $flags, Smarty::$_CHARSET) . ''; + if ($encode === 'javascript') { - $string = 'document.write(\'' . $text . '\');'; $js_encode = ''; for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { $js_encode .= '%' . bin2hex($string[ $x ]); } - return ''; + return ''; } elseif ($encode === 'javascript_charcode') { - $string = '' . $text . ''; - for ($x = 0, $y = strlen($string); $x < $y; $x++) { + for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { $ord[] = ord($string[ $x ]); } - $_ret = "\n"; - return $_ret; + return ''; } elseif ($encode === 'hex') { preg_match('!^(.*)(\?.*)$!', $address, $match); if (!empty($match[ 2 ])) { @@ -132,6 +142,6 @@ function smarty_function_mailto($params) return '' . $text_encode . ''; } else { // no encoding - return '' . $text . ''; + return $string; } } diff --git a/web/private_php/ams/smarty/libs/plugins/function.math.php b/web/private_php/ams/smarty/libs/plugins/function.math.php index 7348d96494..e2f8e04c83 100644 --- a/web/private_php/ams/smarty/libs/plugins/function.math.php +++ b/web/private_php/ams/smarty/libs/plugins/function.math.php @@ -28,7 +28,12 @@ function smarty_function_math($params, $template) 'int' => true, 'abs' => true, 'ceil' => true, + 'acos' => true, + 'acosh' => true, 'cos' => true, + 'cosh' => true, + 'deg2rad' => true, + 'rad2deg' => true, 'exp' => true, 'floor' => true, 'log' => true, @@ -39,27 +44,51 @@ function smarty_function_math($params, $template) 'pow' => true, 'rand' => true, 'round' => true, + 'asin' => true, + 'asinh' => true, 'sin' => true, + 'sinh' => true, 'sqrt' => true, 'srand' => true, - 'tan' => true + 'atan' => true, + 'atanh' => true, + 'tan' => true, + 'tanh' => true ); + // be sure equation parameter is present if (empty($params[ 'equation' ])) { trigger_error("math: missing equation parameter", E_USER_WARNING); return; } $equation = $params[ 'equation' ]; + + // 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.'(?1))?)+$/'; + + if (!preg_match($regexp, $equation)) { + trigger_error("math: illegal characters", E_USER_WARNING); + return; + } + // make sure parenthesis are balanced if (substr_count($equation, '(') !== substr_count($equation, ')')) { trigger_error("math: unbalanced parenthesis", E_USER_WARNING); return; } + // disallow backticks if (strpos($equation, '`') !== false) { trigger_error("math: backtick character not allowed in equation", E_USER_WARNING); return; } + // also disallow dollar signs if (strpos($equation, '$') !== false) { trigger_error("math: dollar signs not allowed in equation", E_USER_WARNING); @@ -96,6 +125,7 @@ function smarty_function_math($params, $template) } $smarty_math_result = null; eval("\$smarty_math_result = " . $equation . ";"); + if (empty($params[ 'format' ])) { if (empty($params[ 'assign' ])) { return $smarty_math_result; diff --git a/web/private_php/ams/smarty/libs/plugins/modifier.escape.php b/web/private_php/ams/smarty/libs/plugins/modifier.escape.php index 150901c7c7..43353cfc6d 100644 --- a/web/private_php/ams/smarty/libs/plugins/modifier.escape.php +++ b/web/private_php/ams/smarty/libs/plugins/modifier.escape.php @@ -184,7 +184,11 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ '"' => '\\"', "\r" => '\\r', "\n" => '\\n', - ' '<\/' + ' '<\/', + // see https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements + '