From 1bca68beec7eb403c61f026bd62165cf88f56d9a Mon Sep 17 00:00:00 2001 From: Simon Wisselink Date: Mon, 29 Jun 2026 12:34:58 +0200 Subject: [PATCH] Fix TypeError for non-array static_classes in Security policy --- changelog/static-classes-none-typeerror.md | 1 + src/Security.php | 8 +++-- .../UnitTests/SecurityTests/SecurityTest.php | 33 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 changelog/static-classes-none-typeerror.md diff --git a/changelog/static-classes-none-typeerror.md b/changelog/static-classes-none-typeerror.md new file mode 100644 index 00000000..51b5f9bc --- /dev/null +++ b/changelog/static-classes-none-typeerror.md @@ -0,0 +1 @@ +- Fixed a `TypeError` on PHP 8 when `Security::$static_classes` was set to a non-array value (e.g. the string `'none'`) to disable static class access; any non-array value now cleanly denies access. Use `Security::$static_classes = null` to disable access to all static classes. diff --git a/src/Security.php b/src/Security.php index e2d2ab29..325dac65 100644 --- a/src/Security.php +++ b/src/Security.php @@ -52,7 +52,7 @@ class Security { /** * This is an array of trusted static classes. * If empty access to all static classes is allowed. - * If set to 'none' none is allowed. + * To disable access to all static classes set $static_classes = null. * * @var array */ @@ -206,7 +206,11 @@ class Security { * @return boolean true if class is trusted */ public function isTrustedStaticClass($class_name, $compiler) { - if (isset($this->static_classes) + // Only an array enables access: an empty array allows all classes, a + // populated array is an allowlist. Any other value (null, or the + // documented "none") denies all. Using is_array() rather than isset() + // also avoids a PHP 8 TypeError from passing a non-array to in_array(). + if (is_array($this->static_classes) && (empty($this->static_classes) || in_array($class_name, $this->static_classes)) ) { return true; diff --git a/tests/UnitTests/SecurityTests/SecurityTest.php b/tests/UnitTests/SecurityTests/SecurityTest.php index ce00a21f..97d779ae 100644 --- a/tests/UnitTests/SecurityTests/SecurityTest.php +++ b/tests/UnitTests/SecurityTests/SecurityTest.php @@ -243,6 +243,39 @@ class SecurityTest extends PHPUnit_Smarty $this->smarty->fetch('string:{$smarty.template_object::square(5)}'); } + /** + * The default (empty array) allows access to all static classes. Documents + * the backwards-compatible behaviour. + */ + public function testStaticClassAllowedByDefault() + { + $this->smarty->security_policy->static_classes = array(); + $this->assertEquals('25', $this->smarty->fetch('string:{mysecuritystaticclass::square(5)}')); + } + + /** + * Setting static_classes to null disables access to all static classes. + */ + public function testStaticClassDeniedWhenNull() + { + $this->expectException(\Smarty\Exception::class); + $this->expectExceptionMessage("access to static class 'mysecuritystaticclass' not allowed by security setting"); + $this->smarty->security_policy->static_classes = null; + $this->smarty->fetch('string:{mysecuritystaticclass::square(5)}'); + } + + /** + * Regression: a non-array value such as the string 'none' must deny access + * cleanly instead of raising a PHP 8 TypeError from in_array(). + */ + public function testStaticClassDeniedWhenNonArray() + { + $this->expectException(\Smarty\Exception::class); + $this->expectExceptionMessage("access to static class 'mysecuritystaticclass' not allowed by security setting"); + $this->smarty->security_policy->static_classes = 'none'; + $this->smarty->fetch('string:{mysecuritystaticclass::square(5)}'); + } + public function testChangedTrustedDirectory() { $this->smarty->security_policy->secure_dir = array(