10

According to documentation:

If html is specified, any embedded JavaScript inside the retrieved data is executed before the HTML is returned as a string. Similarly, script will execute the JavaScript that is pulled back from the server, then return nothing.

How to prevent this? I have js that shall modify the content that is obtained through ajax. Executing it before the html is returned makes no sense as it does not have content to work on (at least in my case).

my code:

function do_ajax(url) {
    $.ajax({
        cache: false,
        url : url,
        success: function(response, status, xhr) {
            var ct = xhr.getResponseHeader("content-type") || "";
            if (ct.indexOf('script') > -1) {
                try {
                    eval(response);
                }
                catch(error) {}
            } else {
                var edit_dialog = $('<div class="edit_dialog" style="display:hidden"></div>').appendTo('body');
                edit_dialog.html(response);
                edit_dialog.dialog({ modal:true, close: function(event, ui) { $(this).dialog('destroy').remove(); } });
            }

        },
        error: function (xhr, ajaxOptions, thrownError) {
            alert(xhr.status);
            alert(thrownError);
        }
    });
}

the script received by ajax is executed twice. First by me in the eval(response), then jquery execute it again (as described in the documentation)

YakovL
  • 7,557
  • 12
  • 62
  • 102
Krzysztof Dk
  • 155
  • 1
  • 1
  • 9

4 Answers4

10

Lee's answer already adequately addresses the case of HTML responses - scripts embedded in these are not in fact executed automatically unless you add the HTML to the DOM, contrary to the erroneous documentation you quoted.

That leaves the other case asked about in your question title - preventing script responses from being executed automatically when received. You can do this easily using the dataType setting.

$.ajax('myscript.js', {
    dataType: 'text',
    success: function (response) {
        // Do something with the response
    }
})

Setting dataType to 'text' will cause jQuery to disregard the Content-Type header returned from the server and treat the response like plain text, thus preventing the default behaviour for JavaScript responses (which is to execute them). From the (recently corrected) docs:

The type of pre-processing depends by default upon the Content-Type of the response, but can be set explicitly using the dataType option. If the dataType option is provided, the Content-Type header of the response will be disregarded.

...

If text or html is specified, no pre-processing occurs. The data is simply passed on to the success handler, and made available through the responseText property of the jqXHR object.

Community
  • 1
  • 1
Mark Amery
  • 143,130
  • 81
  • 406
  • 459
3

jQuery.ajax does not evaluate scripts on return when requesting HTML. The passage you quoted in the question was in fact a long-standing error in the documentation, fixed as of April 2014. The new docs have this to say (emphasis mine):

"html": Returns HTML as plain text; included script tags are evaluated when inserted in the DOM.

...

If text or html is specified, no pre-processing occurs. The data is simply passed on to the success handler, and made available through the responseText property of the jqXHR object.

The scripts are evaluated in this case when you call

edit_dialog.html(response);

If you don't want to evaluate the scripts before inserting your response in to the DOM, you should be able to do something like:

edit_dialog.html($($.parseHTML(response)));

parseHTML is the key in that by default it removes script tags. However, be aware that parseHTML is NOT XXS safe and if your source is unknown this is still a security concern.

Community
  • 1
  • 1
Lee
  • 2,610
  • 5
  • 29
  • 36
-1

That's one of the really annoying things about jQuery that it executes javascript on response. Other frameworks like MooTools disable script execution from responses unless you specifically set that you want them executed which is a much better approach.

The only way I could figure to prevent scripts being executed is to add a custom dataFilter Its easy enough but I think it should be default and an ajax option to enable script execution if you want it (I've never had a use for it and other frameworks disable by default for security etc.)

Example

$.ajax('uri',{
    dataFilter: function(data, type)
    {
        type = type || 'text';
        if(type=='html'||type=='text'){
            /*return data.replace(/<script.*>.*?<\/script>/gi, '');*/
            return data.replace(/<script.*?>([\w\W\d\D\s\S\0\n\f\r\t\v\b\B]*?)<\/script>/gi, '');
        }
        return data;
    }
    , success: function(data)
    {
        // whatever
    }
});

** UPDATED ** Needs that crazy regex to cover more script tag instances

NOTE if dataType hasnt been set in options it will be undefined in dataFilter so I just default it to text for the filter - if you remove that line then it will only work if dataType is explicitly set.

rgb
  • 1,246
  • 1
  • 10
  • 14
  • It seems to me that this isn't the right solution in any circumstances. When requesting a JavaScript file, using `dataType: 'text'` is sufficient to prevent its execution. When requesting HTML, scripts in the result won't be executed unless/until you insert the HTML into the DOM, so if you want to automatically scrub out scripts, the best way would be by doing `$response = $(response); $response.find('script').remove();` and then use the scrubbed `$response` instead of the raw HTML from then on. If you do it your way, [Zalgo will get you](http://stackoverflow.com/a/1732454/1709587). – Mark Amery Apr 30 '14 at 20:19
  • Also, with respect to security - note that removing script tags isn't enough to render HTML from an untrusted source safe to insert into the DOM, since it may contain inline event handlers (e.g. `
    `)
    – Mark Amery Apr 30 '14 at 20:20
-1

The documentation states that any embedded Javascript inside the retrieved data will be executed before the HTML is returned as a string. If you want to then alter whatever you have retrieved using your ajax call, you can do so within the succes property:

$.ajax({
  url: "example.html",
  type: "GET",
  dataType: "html",
  succes: function(data){
     // Example: alert(data);
     // Do whatever you want with the returned data using JS or jQuery methods
  }
});
Joey
  • 1,664
  • 3
  • 19
  • 35
  • When I receive only js (dataType:'js'), and in the success function I do eval of the response, it is executed twice. First by my eval, then by the jquery itself as described in the documentation. similar is with html. – Krzysztof Dk Nov 01 '11 at 14:03
  • Why would you need to use an ajax call to receive only JS? Also, `js` is not an allowed datatype. Allowed datatypes are: `xml`, `html`, `json`, `jsonp`, `script` or `text`. – Joey Nov 01 '11 at 14:11
  • i have one function do_ajax(url) that perform ajax calls. It checks what it receives. when it is a js then it is executed, if html it create jqueryui dialog and put the content in the dialog. – Krzysztof Dk Nov 01 '11 at 14:14
  • I agree that js is not proper datatype. Just a typo in the post here. I meant dataType:'script' and I can't change the dataType as I don't know what response I will receive. – Krzysztof Dk Nov 01 '11 at 14:16
  • I don't suggest you use a function like that, as you might lose a lot of data or cause for unwanted behaviour. Instead of calling external javascript methods through ajax, just include them in your code and you can call them whenever you want. Without a work around function like youre describing. – Joey Nov 01 '11 at 14:19
  • I think that changing the script into normal js, not jquery will do this work well. I don't understand why there is no way to stop executing the js from response in jquery. It doesn't matter if you specify the datatype or not. it is always executed. – Krzysztof Dk Nov 01 '11 at 14:27
  • It's no fault of yours at all and I hope you won't take umbrage, but -1 because this answer is misleading; it repeats an entirely false claim once made in the documentation. In fact, embedded JavaScript within HTML responses is *not* executed automatically unless the HTML is later added to the DOM. I removed the incorrect claim here: https://github.com/jquery/api.jquery.com/pull/484 – Mark Amery Apr 30 '14 at 19:44