1

Is this a esc_attr() and esc_js() bug or I am doing something wrong?

function _action_escape_test() {
    $json = json_encode(array('test' => '<>\'"[]&quot;'));

    foreach (
        array(
            'esc_attr' => esc_attr($json),
            'esc_js' => esc_js($json),
            'htmlspecialchars' => htmlspecialchars($json, ENT_COMPAT, 'UTF-8')
        )
        as $func => $esc_value
    ) {
        ?><fieldset class="esc-test">
            <legend><?php echo $func ?>()</legend>
            <textarea data-test-json="<?php echo $esc_value ?>" style="width: 100%"><?php echo htmlspecialchars($esc_value, ENT_COMPAT, 'UTF-8'); ?></textarea>
        </fieldset><?php
    }

    ?><script type="text/javascript">
        jQuery(function($){
            $('.esc-test').each(function(){
                var $this = $(this);

                try {
                    var json = JSON.parse($this.find('textarea').attr('data-test-json'));

                    $this.css('border', '1px solid green');
                } catch (e) {
                    $this.css('border', '1px solid red');
                }
            });
        });
    </script><?php
}
add_action('admin_notices', '_action_escape_test');

screenshot

3

Normally, when one want to put strings to be used in javascript, esc_js is the right function, not esc_attr.

The problem is that esc_js, according to docs:

Escapes text strings for echoing in JS

(bold mine).

So, using with esc_js you obtain a string that can be safely echoed in js, not parsed: it's not a bug, it's the intended behaviour.

The canonical way to pass data from PHP to js in WordPress is wp_localize_script so you should consider to use it instead. (Note that function internally do not use any esc_* functions, just echo the result of json_encode).

As alternative I can suggest you to use filter_var with sanitize filters: IMHO it is a far better way to do the task esc_* WP functions do. Consider that esc_* function pass through filter hooks, so integrity of your data can be corrupted by external code.

Try:

<?php
$data_safe = filter_var( $json, FILTER_SANITIZE_SPECIAL_CHARS );
$out_safe = filter_var( $json, FILTER_SANITIZE_FULL_SPECIAL_CHARS );
?>
<textarea data-test-json="<?php echo $data_safe; ?>"><?php echo $out_safe; ?></textarea>
  • In that case, we are thinking to migrate from esc_attr() to htmlspecialchars(). We often store in hidden inputs json values: some javascript widget store its value json encoded in a hidden input, on form submit that value is saved in the database, and after page refresh the javascript widget recovers its state from that hidden input json value. We also use wp_localize_script(), but for other cases. Why they created the esc_attr() function if htmlspecialchars() is doing its job very well (I use it in all my non WP projects) ? – moldcraft Sep 30 '14 at 7:17
  • esc_attr do an additional check for valid utf8, call htmlspecialchars then allow filtering results via hooks. That said in my WP projects I use filter_var instead: using right filter it can be equivalent to htmlspecialchars but is more readable and more flexible: you can wrap it in a custom function where you pass as param the filter constant and/or flags to it. You can use one single method to sanitize all types of variable and you can even create app-specific filters. @moldcraft – gmazzap Sep 30 '14 at 9:31
  • It becomes too complicated for this simple task. I see that filters useless, all you need is to put a value in a attribute. We will just use htmlspecialchars() instead of esc_attr(). Thank you for your answers. – moldcraft Sep 30 '14 at 9:46
  • @moldcraft NP. Everyone should be confortable with own code, so if you find htmlspecialchar better, go with it, works. What I'm not agree with you: filter_var and filter_var_input are among the most useful functions in whole PHP: any other way so sanitize / validate generic vars in PHP (expecially user input) just sucks. Example: htmlspecialchars( $_GET['thing'] ) compared to filter_input( INPUT_GET, 'thing', FILTER_SANITIZE_SPECIAL_CHARS ) is far far way worse. Worth a read phparch.com/2010/07/never-use-_get-again – gmazzap Sep 30 '14 at 10:19
  • We save in the database the original user input, and only make it safe for html when displaying. For validation and removing unwanted characters from a string or input, the filter_* functions are good, thank you for the tip. Docs php.net/manual/en/filter.filters.sanitize.php says that FILTER_SANITIZE_FULL_SPECIAL_CHARS is Equivalent to calling htmlspecialchars() with ENT_QUOTES set., so why it is far far way worse? – moldcraft Sep 30 '14 at 10:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.