madrobby / zepto

Zepto.js is a minimalist JavaScript library for modern browsers, with a jQuery-compatible API
http://zeptojs.com
Other
15k stars 3.93k forks source link

$('body').append('<script src="http://xxx.js"><\/ script>') src is invalid #1357

Open Vibing opened 5 years ago

Vibing commented 5 years ago
$('body').append('<script src="http://xxx.js"><\/script>')

I tried to execute the above code but it did not send a request

xudeming208 commented 4 years ago

我看了下zepto的源码,翻译过来其执行过程如下,所以不会发送网络请求:

    <div id="demo"></div>

    let str = '<script src="http://xxx.js"><\/script>';
    document.getElementById('demo').innerHTML = str;
    let dom = [].slice.call(document.getElementById('demo').childNodes)[0];
    document.body.insertBefore(dom, null)

但是zepto下面代码是可以执行的:

$('body').append('<script>console.log('test')<\/script>')

其实通过eval执行的,zepto源码如下:

if (parentInDocument) traverseNode(node, function (el) {
    if (el.nodeName != null && el.nodeName.toUpperCase() === 'SCRIPT' &&
        (!el.type || el.type === 'text/javascript') && !el.src) {
        var target = el.ownerDocument ? el.ownerDocument.defaultView : window
        target['eval'].call(target, el.innerHTML)
    }
})

zepto用append css是可以发送网络请求的,如同用原生的innerHTML可以发送网络请求一样,或许是因为浏览器的安全策略,毕竟JS可以执行代码:

$('body').append('<link href="xxx.css">')

而我查看jQuery源码发现,其之所以能发送网络请求,是因为其发送了ajax请求xxx.js,其源码如下:

// Optional AJAX dependency, but won't run scripts if not present
if ( jQuery._evalUrl && !node.noModule ) {
    jQuery._evalUrl( node.src, {
        nonce: node.nonce || node.getAttribute( "nonce" )
    }, doc );
}

jQuery._evalUrl = function( url, options, doc ) {
    return jQuery.ajax( {
        url: url,

        // Make this explicit, since user can override this through ajaxSetup (#11264)
        type: "GET",
        dataType: "script",
        cache: true,
        async: false,
        global: false,

        // Only evaluate the response if it is successful (gh-4126)
        // dataFilter is not invoked for failure responses, so using it instead
        // of the default converter is kludgy but it works.
        converters: {
            "text script": function() {}
        },
        dataFilter: function( response ) {
            jQuery.globalEval( response, options, doc );
        }
    } );
};