1

I came across an encoding issue when adding inline JavaScript to the footer of a WordPress page via wp_add_inline_script().

The following description is a boiled down version of the problem for demonstration purposes.

So, I have a textarea inside a metabox on the post editor which contains the following JavaScript:

var test3 = '3';
var test4 = "4";

Note the use of single AND double quotes for testing. I'm also enqueueing a JavaScript file containing:

console.log("test.js loaded");

The JavaScript from the textarea is added to the page directly after test.js via wp_add_inline_script().

The issue is that when I inspect the source code the single/double quotes have been encoded:

var test3 = '3';
var test4 = "4";

I couldn't figure out why this was so I did a similar test but storing JavaScript inside a textarea on a plugin settings page.

Here, the textarea contains:

var test1 = '1';
var test2 = "2";

When added to the front end page, this JavaScript outputs perfectly. Here's a screenshot:

enter image description here

It seems as though the data from post meta is encoded while the data from plugin options is not.

Here is the full plugin code:

File: inline-js-test.js

<?php
/*
Plugin Name: Inline JS Test
Version: 0.1
Author: David Gwyer
*/

// ijst_ prefix is derived from [i]nline [js] [t]est

// Enqueue scripts

function ijst_enqueue_scripts() {
    $options = get_option( 'ijst_options' );
    $js = get_post_meta( '5943', '_ijst-js', true );

    wp_enqueue_script( 'ijst-test', plugins_url('test.js', __FILE__), array(), '', true );

    $inline_js1 = $options['textarea'];
    $inline_js2 = $js;
    wp_add_inline_script( 'ijst-test', $inline_js1 );
    wp_add_inline_script( 'ijst-test', $inline_js2 );
}
add_action( 'wp_enqueue_scripts', 'ijst_enqueue_scripts' );

// Plugin options page

function ijst_init() {
    register_setting( 'ijst_plugin_options', 'ijst_options' );
}
add_action( 'admin_init', 'ijst_init' );

function ijst_add_options_page() {
    $page = add_options_page( 'Inline JS Test', 'Inline JS Test', 'manage_options', __FILE__, 'ijst_render_form' );
}
add_action( 'admin_menu', 'ijst_add_options_page' );

function ijst_render_form() {
    ?>
    <div class="wrap">
        <h2 style="font-size: 23px;">Inline JS Test</h2>
        <form method="post" action="options.php">
            <?php
                settings_fields( 'ijst_plugin_options' );
                $options = get_option( 'ijst_options' );
            ?>
            <table>
                <tr>
                    <td><textarea name="ijst_options[textarea]" rows="7" cols="50" type='textarea'><?php echo $options['textarea']; ?></textarea></td>
                </tr>
            </table>
            <p class="submit"><input type="submit" class="button-primary" value="<?php _e( 'Save Changes' ) ?>"></p>
        </form>
    </div>
<?php
}

// Post meta box

function ijst_meta_box_init() {
    add_meta_box( 'inline-js-test', 'Inline JS Test', 'ijst_render_meta_box', 'post', 'normal', 'high' );
    add_action( 'save_post', 'ijst_save_meta_box_data' );
}
add_action( 'admin_init', 'ijst_meta_box_init' );

function ijst_save_meta_box_data( $post_id ) {
    if ( isset( $_POST['ijst-js'] ) ) { update_post_meta( $post_id, '_ijst-js', esc_attr( $_POST['ijst-js'] ) ); }
}

function ijst_render_meta_box( $post, $args ) {
    $js = get_post_meta( $post->ID, '_ijst-js', true );
    ?><table><tr><td><textarea id="ijst-js" name="ijst-js"><?php echo esc_attr( $js ); ?></textarea></td></tr></table><?php
}

File: test.js

console.log("test.js loaded");

The code for the whole plugin can also be found on this Gist: https://gist.github.com/dgwyer/0bb2022be0d733cf3bfc4e094ea815f7

Aside from the main issue I also need to understand the correct procedure for escaping/sanitizing JavaScript before adding it to a web page?

I don't think outputting the raw data is a great idea, but obviously don't want to use anything that would break the code.

0

1) For escaping content of textarea you should use esc_textarea and not esc_attr.

2) You should not escape things when storing them in the DB, escaping should be done only at output time. (I assume that you can find an edge case in which it might be needed, but as a general rule, just don't do it).

3) Asking users to insert JS code is very user hostile. In addition to the obvious security problem that might arise a general type of breakage is almost un avoidable... think someone doing $ = 'dollar';. It doesn't help that if you go that way you have no way to sanitize anything and have to serve it raw.

  • That's true esc_textarea should be used in preference to esc_attr but it doesn't change the behaviour at all. Using either one still results in the encoding issue when retrieving post meta. As for asking users to enter JS code. They don't at all, in this case. All JS is added internally via the plugin, resulting from user interaction with UI controls. As explained in the original post the example plugin is greatly simplified for demonstration purposes. – dgwyer Sep 20 '17 at 13:04
  • Answer updated. Still there is no reason to add the JS even internally. Either it is constant in which case it should be part of your plugin code, or it is a source for trouble via DB corruption or whatever other mishaps DBs may have. – Mark Kaplun Sep 20 '17 at 13:14
  • Dynamically updating a JS configuration object is crucial to the operation of the plugin, so is a valid use-case here. But it's irrelevant to the core issue so was left out for brevity. Any type of code is just text so if it passes through esc_*, and sanitize_* WP functions there should be no further issues as far as I can see. Removing esc_* when saving to the db did the trick. I'm also using sanitize_textarea_field when saving to the db, and when retrieving for output on the front end. – dgwyer Sep 20 '17 at 14:08

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.