From 6f4025b038c763499ff38405ade955e8a7083c0a Mon Sep 17 00:00:00 2001
From: Simon Wisselink <s.wisselink@iwink.nl>
Date: Fri, 18 Nov 2022 13:03:45 +0100
Subject: [PATCH] More concise escape implementation and unit test to cover
 both modifierplugin and modifiercompiler.

---
 libs/plugins/modifier.escape.php              |  5 +--
 libs/plugins/modifiercompiler.escape.php      |  6 +--
 .../PluginModifierEscapeTest.php              | 38 +++++++++++++++++--
 3 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/libs/plugins/modifier.escape.php b/libs/plugins/modifier.escape.php
index 0ffbe0611..11e44682e 100644
--- a/libs/plugins/modifier.escape.php
+++ b/libs/plugins/modifier.escape.php
@@ -37,11 +37,8 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $
         // no break
         case 'htmlall':
             if (Smarty::$_MBSTRING) {
-                $string = htmlspecialchars($string, ENT_QUOTES, $char_set, $double_encode);
-                // htmlentities() won't convert everything, so use mb_convert_encoding
                 $string = mb_convert_encoding($string, 'UTF-8', $char_set);
-                $string = htmlentities($string);
-                return htmlspecialchars_decode($string);
+                return htmlentities($string, ENT_QUOTES, 'UTF-8', $double_encode);
             }
             // no MBString fallback
             return htmlentities($string, ENT_QUOTES, $char_set, $double_encode);
diff --git a/libs/plugins/modifiercompiler.escape.php b/libs/plugins/modifiercompiler.escape.php
index 946de104f..602c3dbfc 100644
--- a/libs/plugins/modifiercompiler.escape.php
+++ b/libs/plugins/modifiercompiler.escape.php
@@ -44,9 +44,9 @@ function smarty_modifiercompiler_escape($params, Smarty_Internal_TemplateCompile
             // no break
             case 'htmlall':
                 if (Smarty::$_MBSTRING) {
-                    return 'htmlspecialchars_decode(mb_convert_encoding(htmlentities(htmlspecialchars((string)' . $params[ 0 ] . ', ENT_QUOTES, ' .
-                        var_export($char_set, true) . ', ' . var_export($double_encode, true) .
-                        '), ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ' . var_export($char_set, true) . '),' . var_export($char_set, true) . '))';
+                    return 'htmlentities(mb_convert_encoding((string)' . $params[ 0 ] . ', \'UTF-8\', ' .
+                        var_export($char_set, true) . '), ENT_QUOTES, \'UTF-8\', ' .
+                        var_export($double_encode, true) . ')';
                 }
                 // no MBString fallback
                 return 'htmlentities((string)' . $params[ 0 ] . ', ENT_QUOTES, ' . var_export($char_set, true) . ', ' .
diff --git a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php
index 07c801d70..309a71ab8 100644
--- a/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php
+++ b/tests/UnitTests/TemplateSource/TagTests/PluginModifier/PluginModifierEscapeTest.php
@@ -20,12 +20,19 @@ public function setUp(): void
         $this->setUpSmarty(__DIR__);
     }
 
-    public function testHtml()
+    public function testHtmlCompiled()
     {
         $tpl = $this->smarty->createTemplate('string:{"I\'m some <html> to ä be \"escaped\" or &copy;"|escape:"html"}');
         $this->assertEquals("I&#039;m some &lt;html&gt; to ä be &quot;escaped&quot; or &amp;copy;", $this->smarty->fetch($tpl));
     }
 
+    public function testHtmlModifier()
+    {
+        $tpl = $this->smarty->createTemplate('string:{"I\'m some <html> to ä be \"escaped\" or &copy;"|escape:$mode}');
+        $this->smarty->assign('mode', 'html');
+        $this->assertEquals("I&#039;m some &lt;html&gt; to ä be &quot;escaped&quot; or &amp;copy;", $this->smarty->fetch($tpl));
+    }
+
     public function testHtmlWithoutMbstring()
     {
         Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb');
@@ -48,13 +55,20 @@ public function testHtmlDoubleWithoutMbstring()
         Smarty::$_MBSTRING = true;
     }
 
-    public function testHtmlall()
+    public function testHtmlallCompiled()
     {
         $tpl = $this->smarty->createTemplate('string:{"I\'m some <html> to ä be \"escaped\" or &copy;"|escape:"htmlall"}');
         $this->assertEquals("I&#039;m some &lt;html&gt; to &auml; be &quot;escaped&quot; or &amp;copy;", $this->smarty->fetch($tpl));
     }
 
-    public function testHtmlallWithoutMbstring()
+    public function testHtmlallModifier()
+    {
+        $tpl = $this->smarty->createTemplate('string:{"I\'m some <html> to ä be \"escaped\" or &copy;"|escape:$mode}');
+        $this->smarty->assign('mode', 'htmlall');
+        $this->assertEquals("I&#039;m some &lt;html&gt; to &auml; be &quot;escaped&quot; or &amp;copy;", $this->smarty->fetch($tpl));
+    }
+
+    public function testHtmlallWithoutMbstringCompiled()
     {
         Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb');
         $tpl = $this->smarty->createTemplate('string:{"I\'m some <html> to ä be \"escaped\" or &copy;"|escape:"htmlall"}');
@@ -62,6 +76,15 @@ public function testHtmlallWithoutMbstring()
         Smarty::$_MBSTRING = true;
     }
 
+    public function testHtmlallWithoutMbstringModifier()
+    {
+        Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb');
+        $tpl = $this->smarty->createTemplate('string:{"I\'m some <html> to ä be \"escaped\" or &copy;"|escape:$mode}');
+        $this->smarty->assign('mode', 'htmlall');
+        $this->assertEquals("I&#039;m some &lt;html&gt; to &auml; be &quot;escaped&quot; or &amp;copy;", $this->smarty->fetch($tpl));
+        Smarty::$_MBSTRING = true;
+    }
+
     public function testHtmlallDouble()
     {
         $tpl = $this->smarty->createTemplate('string:{"I\'m some <html> to ä be \"escaped\" or &copy;"|escape:"htmlall":null:false}');
@@ -76,12 +99,19 @@ public function testHtmlallDoubleWithoutMbstring()
         Smarty::$_MBSTRING = true;
     }
 
-    public function testUrl()
+    public function testUrlCompiled()
     {
         $tpl = $this->smarty->createTemplate('string:{"http://some.encoded.com/url?parts#foo"|escape:"url"}');
         $this->assertEquals("http%3A%2F%2Fsome.encoded.com%2Furl%3Fparts%23foo", $this->smarty->fetch($tpl));
     }
 
+    public function testUrlModifier()
+    {
+        $tpl = $this->smarty->createTemplate('string:{"http://some.encoded.com/url?parts#foo"|escape:$mode}');
+        $this->smarty->assign('mode', 'url');
+        $this->assertEquals("http%3A%2F%2Fsome.encoded.com%2Furl%3Fparts%23foo", $this->smarty->fetch($tpl));
+    }
+
     public function testUrlWithoutMbstring()
     {
         Smarty::$_MBSTRING = false;$this->smarty->setCompileId('mb');
