Adding bookmarklet to wordpress post

I’m trying to add a javascript bookmarklet link to a post on my WordPress site. However it isn’t coming out in the post preview. When I check the link that WordPress adds to the post it has converted it to javascript:void(0). This simple example reproduces the problem.

<a href="javascript:alert('Alert!');">Search Scholar</a>

There are a few other people who’ve had the same problem here, here, here, and here but no-one seems to have found a solution beyond just giving their bookmarklet code for people to copy and paste and create their own bookmarklet.

Read More

The cause of this problem is that Chrome’s XSS protection is stripping out the javascript from the link when submitting it via wp-admin. One “solution” is to add the line header("X-XSS-Protection: 0"); to wp-blog-header.php in the root folder. This is insecure as it switches off the XSS protection on your WordPress site but it does allow the bookmarklet code to be rendered when the page loads.

Are there any real solutions to this problem that don’t involve switching off XSS protection? Is there a perhaps a plugin I can install to my WordPress to allow me to add javascript: links inside my posts?

Related posts

Leave a Reply

2 comments

  1. EDIT 2 After even more research, this is actually caused by the browser’s XSS detection as mentioned by the OP (as opposed to any WordPress-specific functionality). The problem only arises when you click the Preview button in WordPress, and only on that initial page load. Apparently WordPress sends along some of the HTML in the request headers, and that triggers the XSS functionality in the browser. If you load the preview, and then you refresh the page, the XSS issue goes away, and the javascript: link is displayed as it was saved. When viewing the actual site, after publishing the page, this XSS issue is never encountered present.

    EDIT After some deeper research (working with @gnarf), it turns out the actual issue comes down to the way that WordPress handles javascript: links in its preview functionality. It would seem that WordPress has some custom Javascript that runs and converts all javascript: links to javascript:void(0) links (stripping out any custom code), but only if you’re previewing the page. After publishing the page, the javascript: links are rendered out properly.


    Original Post (describes how to stop WordPress from stripping out javascript: links when saving a post as a non-admin user, which is what I assumed the original problem might have been)

    It looks like WordPress strips out the HTML in the content_save_pre filter. Specifically, it calls the wp_kses_bad_protocol method in wp-includeskses.php:

    /**
     * Sanitize string from bad protocols.
     *
     * This function removes all non-allowed protocols from the beginning of
     * $string. It ignores whitespace and the case of the letters, and it does
     * understand HTML entities. It does its work in a while loop, so it won't be
     * fooled by a string like "javascript:javascript:alert(57)".
     *
     * @since 1.0.0
     *
     * @param string $string Content to filter bad protocols from
     * @param array $allowed_protocols Allowed protocols to keep
     * @return string Filtered content
     */
    function wp_kses_bad_protocol($string, $allowed_protocols) {
        $string = wp_kses_no_null($string);
        $iterations = 0;
    
        do {
            $original_string = $string;
            $string = wp_kses_bad_protocol_once($string, $allowed_protocols);
        } while ( $original_string != $string && ++$iterations < 6 );
    
        if ( $original_string != $string )
            return '';
    
        return $string;
    }
    

    The $allowed_protocols parameter is retrieved via the wp_allowed_protocols() method, which applies the kses_allowed_protocols filter to the list of protocols.

    With this information, you should be able to tie into the kses_allowed_protocols filter to add javascript as a valid one (note that this, of course, would open up security issues):

    add_filter( 'kses_allowed_protocols', function ($protocols) {
       $protocols[] = 'javascript';
       return $protocols;
    });
    

    One way to enhance the security of this approach would be to add a check for specific users or specific roles (by default, it looks like this filter actually isn’t run on administrative accounts, so you can use javascript: links to your heart’s content as an admin) prior to allowing the javascript protocol.

  2. The initial problem is of course due to this:

    JavaScript cannot be added to post content without a special WordPress
    Plugin that removes the filters that prevent unwanted code within the
    post content area for the protection of the user.

    To avoid installing a plugin you can use the recommended method:

    The safe and recommended method of adding JavaScript to a WordPress
    generated page and WordPress Theme or Plugin is by using
    wp_enqueue_script(). This function includes the script if it hasn’t
    already been included, and safely handles dependencies.

    Here are all the details for wp_enqueue_script():
    http://codex.wordpress.org/Function_Reference/wp_enqueue_script

    You then need to create a custom JavaScript file which you include and register with the functions you need for your posts.

    You can in that file make a function which for example takes your bookmarklet link as an argument and do a document.write (or attaches a node to as child to a known element) to the current location.

    Then include the script and call the function in the post as:

    <script type="text/javascript" src="/scripts/myscript.js"></script>
    <script type="text/javascript">
    <!--
    bookmarklet(myLink);
    //--></script>
    

    you only need to link the script ones in the same post – if you use this script in every post it’s probably better to link it in the header (header.php template file, between the meta tags and the style sheet link).

    <script type="text/javascript" src="/scripts/myscript.js"></script>
    

    or alternatively if the above doesn’t work:

    <script type="text/javascript" src="<?php bloginfo('template_url'); ?>/pathto/myscript.js"></script>
    

    Also note that if the src attribute is stripped away you need to go to Users and Personal Options in dashboard to turn off the rich editor.

    Source and more information on how to use JavaScript in WordPress globally and in posts:
    http://codex.wordpress.org/Using_Javascript#Javascript_in_Posts