From 19ae410bf56007a5ef24441cdc6414619cfaf664 Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Mon, 10 Jan 2022 10:48:27 +0100 Subject: [PATCH] Merge pull request from GHSA-4h9c-v5vg-5m6m * Prevent evasion of the static_classes security policy. * Updated deprecated exception expectations. --- CHANGELOG.md | 3 ++ lexer/smarty_internal_templateparser.y | 3 ++ .../smarty_internal_templateparser.php | 3 ++ .../UnitTests/SecurityTests/SecurityTest.php | 36 +++++++++++++++---- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee8eed88..31df422e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Security +- Prevent evasion of the `static_classes` security policy. This addresses CVE-2021-21408 + ## [4.0.2] - 2022-01-10 ### Security diff --git a/lexer/smarty_internal_templateparser.y b/lexer/smarty_internal_templateparser.y index 3786466f..62049876 100644 --- a/lexer/smarty_internal_templateparser.y +++ b/lexer/smarty_internal_templateparser.y @@ -747,6 +747,9 @@ value(res) ::= doublequoted_with_quotes(s). { value(res) ::= varindexed(vi) DOUBLECOLON static_class_access(r). { + if ($this->security && $this->security->static_classes !== array()) { + $this->compiler->trigger_template_error('dynamic static class not allowed by security setting'); + } $prefixVar = $this->compiler->getNewPrefixVariable(); if (vi['var'] === '\'smarty\'') { $this->compiler->appendPrefixCode("compiler->compileTag('private_special_variable',array(),vi['smarty_internal_index']).';?>'); diff --git a/libs/sysplugins/smarty_internal_templateparser.php b/libs/sysplugins/smarty_internal_templateparser.php index a065c94f..a2dd0d6f 100644 --- a/libs/sysplugins/smarty_internal_templateparser.php +++ b/libs/sysplugins/smarty_internal_templateparser.php @@ -2397,6 +2397,9 @@ public static $yy_action = array( } // line 749 "../smarty/lexer/smarty_internal_templateparser.y" public function yy_r94(){ + if ($this->security && $this->security->static_classes !== array()) { + $this->compiler->trigger_template_error('dynamic static class not allowed by security setting'); + } $prefixVar = $this->compiler->getNewPrefixVariable(); if ($this->yystack[$this->yyidx + -2]->minor['var'] === '\'smarty\'') { $this->compiler->appendPrefixCode("compiler->compileTag('private_special_variable',array(),$this->yystack[$this->yyidx + -2]->minor['smarty_internal_index']).';?>'); diff --git a/tests/UnitTests/SecurityTests/SecurityTest.php b/tests/UnitTests/SecurityTests/SecurityTest.php index 72bb3cdc..4d1b8a3a 100644 --- a/tests/UnitTests/SecurityTests/SecurityTest.php +++ b/tests/UnitTests/SecurityTests/SecurityTest.php @@ -257,19 +257,41 @@ class SecurityTest extends PHPUnit_Smarty $this->assertEquals('25', $this->smarty->fetch($tpl)); } -/** - * test not trusted PHP function - * @runInSeparateProcess - * @preserveGlobalState disabled - */ - public function testNotTrustedStaticClass() - { + /** + * test not trusted PHP function + * @runInSeparateProcess + * @preserveGlobalState disabled + */ + public function testNotTrustedStaticClass() + { $this->expectException('SmartyException'); $this->expectExceptionMessage('access to static class \'mysecuritystaticclass\' not allowed by security setting'); $this->smarty->security_policy->static_classes = array('null'); $this->smarty->fetch('string:{mysecuritystaticclass::square(5)}'); } + /** + * test not trusted PHP function + */ + public function testNotTrustedStaticClassEval() + { + $this->expectException('SmartyException'); + $this->expectExceptionMessage('dynamic static class not allowed by security setting'); + $this->smarty->security_policy->static_classes = array('null'); + $this->smarty->fetch('string:{$test = "mysecuritystaticclass"}{$test::square(5)}'); + } + + /** + * test not trusted PHP function + */ + public function testNotTrustedStaticClassSmartyVar() + { + $this->expectException('SmartyException'); + $this->expectExceptionMessage('dynamic static class not allowed by security setting'); + $this->smarty->security_policy->static_classes = array('null'); + $this->smarty->fetch('string:{$smarty.template_object::square(5)}'); + } + public function testChangedTrustedDirectory() { $this->smarty->security_policy->secure_dir = array(