Re: [SMARTY-DEV] [patch] add $smarty->escape_output feature

[email protected] (Monte Ohrt) Wed, 03 May 2006 21:04:09 -0500
Newsgroups php.smarty.dev
Message-ID <[email protected]>
Off the top of my head, you have to be careful to not escape the values 
when they are not direct output.

example:

$smarty->assign('foo','<bar>');

{$foo} <-- variable output, so escape so it comes out &lt;bar&gt;

{if $foo eq "<bar>"} <-- do not escape $foo here, it is in an IF condition


Andreas Korthaus wrote:
> 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
> ------------------------------------------------------------------------
>
> 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;
> +        }
> +    }
> +
> }
>
> /**
>