[patch] add $smarty->escape_output feature

Andreas Korthaus <[email protected]> Wed, 03 May 2006 20:12:22 +0200
Newsgroups gmane.comp.php.smarty.devel
Message-ID <[email protected]>
Hi!

I've had a discussion with boots in the smarty forum about a new, 
optional(!) smarty feature for general output escaping, which should not 
break BC. The idea is to provide means for projects, which like to use 
something like a contract, which ensures, that every output from 
user-input in smarty template gets escaped if a special, new Smarty 
member variable ($smarty->escape_output) is set (the used 
escape-function is modifiable). Other template engines provide something 
like that (e.g. flexy, savant), in Smarty this is not possible today 
(forget about default_modifiers).

You can find the discussion here:

http://www.phpinsider.com/smarty-forum/viewtopic.php?t=7806

Attatched you can find a patch against CVS-HEAD, which implements the 
basic functionality. It's not ready yet, because I did not catch every 
point where input get's displayed without escaping.

 From what I understand the smarty code, I think I have catched 
everything beside some of the plugins. But I'm not sure here, so I'd be 
very happy if someone with better knowledge of the smarty compiler could 
check if it's OK.

And I'd like to know what your general opinion about such a feature is.


Best regards
Andreas

-- 
Smarty Development Mailing List (http://smarty.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php
escape_patch.txt (text/plain, 4.4 KB)
Index: libs/Smarty.class.php
===================================================================
RCS file: /repository/smarty/libs/Smarty.class.php,v
retrieving revision 1.524
diff -u -r1.524 Smarty.class.php
--- libs/Smarty.class.php   18 Jan 2006 19:02:52 -0000   1.524
+++ libs/Smarty.class.php   3 May 2006 09:41:09 -0000
@@ -398,6 +398,29 @@
      */
     var $config_class          =   'Config_File';

+    /**
+     * This tells Smarty whether or not to auto-escape output (variables)
+     *
+     * Available values:
+     *    - null: no escaping (default)
+     *  - 'html': htmlspecialchars()
+     *  - 'htmlall': htmlentities()
+     *  - 'url': urlencode()
+     *
+     * @var null|string escape sequence name
+     */
+    var $escape_output = null;
+
+    /**
+     * This tells Smarty what character-set to use for output encoding
+     *
+     * The character set is passed to escaping-functions, if there is a
+     * parameter for that. Default is null.
+     *
+     * @var null|string character-set
+     */
+    var $output_charset = null;
+
/**#@+
  * END Smarty Configuration Section
  * There should be no need to touch anything below this line.
@@ -1481,6 +1504,8 @@
         $smarty_compiler->compile_id        = $this->_compile_id;
         $smarty_compiler->_config            = $this->_config;
         $smarty_compiler->request_use_auto_globals  = $this->request_use_auto_globals;
+        $smarty_compiler->escape_output     = $this->escape_output;
+        $smarty_compiler->output_encoding   = $this->output_encoding;

         if (isset($cache_include_path) && isset($this->_cache_serials[$cache_include_path])) {
             $smarty_compiler->_cache_serial = $this->_cache_serials[$cache_include_path];
Index: libs/Smarty_Compiler.class.php
===================================================================
RCS file: /repository/smarty/libs/Smarty_Compiler.class.php,v
retrieving revision 1.379
diff -u -r1.379 Smarty_Compiler.class.php
--- libs/Smarty_Compiler.class.php   22 Apr 2006 08:16:57 -0000   1.379
+++ libs/Smarty_Compiler.class.php   3 May 2006 09:45:09 -0000
@@ -435,15 +435,20 @@
                     ~xs', $template_tag, $match)) {
             $this->_syntax_error("unrecognized tag: $template_tag", E_USER_ERROR, __FILE__, __LINE__);
         }
-       
+
         $tag_command = $match[1];
         $tag_modifier = isset($match[2]) ? $match[2] : null;
         $tag_args = isset($match[3]) ? $match[3] : null;

         if (preg_match('~^' . $this->_num_const_regexp . '|' . $this->_obj_call_regexp . '|' . $this->_var_regexp . '$~', $tag_command)) {
-            /* tag name is a variable or object */
+
+            /* tag name is a variable or object */
             $_return = $this->_parse_var_props($tag_command . $tag_modifier);
-            return "<?php echo $_return; " . $this->_additional_newline;
+
+            /* variable gets escaped, when $Smarty->escape_output is enabled */
+            $_return_escaped = $this->_escape_var($_return, $tag_modifier);
+
+            return "<?php echo $_return_escaped; " . $this->_additional_newline;
         }

         /* If the tag name is a registered object, we process it. */
@@ -2286,6 +2291,40 @@
                              E_USER_ERROR, __FILE__, __LINE__);
     }

+    /**
+     * add output-escaping around variable
+     * (called after modifiers)
+     *
+     * @param string $var_name
+     * @param string $modifier_string
+     */
+    function _escape_var($var_name, $modifier_string)
+    {
+      /* only escape if $Smarty->escape_output is set and "noescape" modifier is not used */
+        if (!is_null($this->escape_output) && !preg_match('~(^|\|)(no)?escape($|\|)~', $modifier_string)) {
+       
+         if (is_null($this->output_charset)) {
+            $_charset = '';
+         }
+         else {
+            $_charset = ',' . $this->output_charset;
+         }
+            switch ($this->escape_output) {
+                case 'html':
+                    return "htmlspecialchars({$var_name}, ENT_QUOTES{$_charset});";
+                case 'htmlall':
+                    return "htmlentities({$var_name}, ENT_QUOTES{$_charset});";
+                case 'url':
+                    return "urlencode({$var_name});";
+                default:
+                   $this->_trigger_fatal_error("Smarty output escaping error: escapement type '$this->escape_output' is not implemented");
+           }
+        }
+        else {
+           return $var_name;
+        }
+    }
+
}

/**