mirror of
https://github.com/jquery/jquery.git
synced 2024-11-23 02:54:22 +00:00
Ajax: Avoid CSP errors in the script transport for async requests
Until now, the AJAX script transport only used a script tag to load scripts for cross-domain requests or ones with `scriptAttrs` set. This commit makes it also used for all async requests to avoid CSP errors arising from usage of inline scripts. This also makes `jQuery.getScript` not trigger CSP errors as it uses the AJAX script transport under the hood. For sync requests such a change is impossible and that's what `jQuery._evalUrl` uses. Fixing that is tracked in gh-1895. The commit also makes other type of requests using the script tag version of the script transport set its type to "GET", namely async scripts & ones with `scriptAttrs` set in addition to the existing cross-domain ones. Fixes gh-3969 Closes gh-4763
This commit is contained in:
parent
82b87f6f0e
commit
07a8e4a177
@ -32,7 +32,10 @@ jQuery.ajaxPrefilter( "script", function( s ) {
|
|||||||
if ( s.cache === undefined ) {
|
if ( s.cache === undefined ) {
|
||||||
s.cache = false;
|
s.cache = false;
|
||||||
}
|
}
|
||||||
if ( s.crossDomain ) {
|
|
||||||
|
// These types of requests are handled via a script tag
|
||||||
|
// so force their methods to GET.
|
||||||
|
if ( s.crossDomain || s.async || s.scriptAttrs ) {
|
||||||
s.type = "GET";
|
s.type = "GET";
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
@ -40,8 +43,9 @@ jQuery.ajaxPrefilter( "script", function( s ) {
|
|||||||
// Bind script tag hack transport
|
// Bind script tag hack transport
|
||||||
jQuery.ajaxTransport( "script", function( s ) {
|
jQuery.ajaxTransport( "script", function( s ) {
|
||||||
|
|
||||||
// This transport only deals with cross domain or forced-by-attrs requests
|
// This transport only deals with async, cross domain or forced-by-attrs requests.
|
||||||
if ( s.crossDomain || s.scriptAttrs ) {
|
// Sync requests remain handled differently to preserve strict script ordering.
|
||||||
|
if ( s.crossDomain || s.async || s.scriptAttrs ) {
|
||||||
var script, callback;
|
var script, callback;
|
||||||
return {
|
return {
|
||||||
send: function( _, complete ) {
|
send: function( _, complete ) {
|
||||||
|
1
test/data/csp-ajax-script-downloaded.js
Normal file
1
test/data/csp-ajax-script-downloaded.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
window.downloadedScriptCalled = true;
|
13
test/data/csp-ajax-script.html
Normal file
13
test/data/csp-ajax-script.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
<title>jQuery.ajax() - script, CSP script-src compat (gh-3969)</title>
|
||||||
|
<script src="../jquery.js"></script>
|
||||||
|
<script src="iframeTest.js"></script>
|
||||||
|
<script src="csp-ajax-script.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>CSP Test Page</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
test/data/csp-ajax-script.js
Normal file
25
test/data/csp-ajax-script.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* global startIframeTest */
|
||||||
|
|
||||||
|
var timeoutId, type;
|
||||||
|
|
||||||
|
function finalize() {
|
||||||
|
startIframeTest( type, window.downloadedScriptCalled );
|
||||||
|
}
|
||||||
|
|
||||||
|
timeoutId = setTimeout( function() {
|
||||||
|
finalize();
|
||||||
|
}, 1000 );
|
||||||
|
|
||||||
|
jQuery
|
||||||
|
.ajax( {
|
||||||
|
url: "csp-ajax-script-downloaded.js",
|
||||||
|
dataType: "script",
|
||||||
|
method: "POST",
|
||||||
|
beforeSend: function( _jqXhr, settings ) {
|
||||||
|
type = settings.type;
|
||||||
|
}
|
||||||
|
} )
|
||||||
|
.then( function() {
|
||||||
|
clearTimeout( timeoutId );
|
||||||
|
finalize();
|
||||||
|
} );
|
@ -195,22 +195,24 @@ QUnit.assert.ok( true, "mock executed");';
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function cspFrame( $req ) {
|
protected function cspFrame( $req ) {
|
||||||
// This is CSP only for browsers with "Content-Security-Policy" header support
|
|
||||||
// i.e. no old WebKit or old Firefox
|
|
||||||
header( "Content-Security-Policy: default-src 'self'; report-uri ./mock.php?action=cspLog" );
|
header( "Content-Security-Policy: default-src 'self'; report-uri ./mock.php?action=cspLog" );
|
||||||
header( 'Content-type: text/html' );
|
header( 'Content-type: text/html' );
|
||||||
echo file_get_contents( __DIR__ . '/csp.include.html' );
|
echo file_get_contents( __DIR__ . '/csp.include.html' );
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function cspNonce( $req ) {
|
protected function cspNonce( $req ) {
|
||||||
// This is CSP only for browsers with "Content-Security-Policy" header support
|
|
||||||
// i.e. no old WebKit or old Firefox
|
|
||||||
$test = $req->query['test'] ? '-' . $req->query['test'] : '';
|
$test = $req->query['test'] ? '-' . $req->query['test'] : '';
|
||||||
header( "Content-Security-Policy: script-src 'nonce-jquery+hardcoded+nonce'; report-uri ./mock.php?action=cspLog" );
|
header( "Content-Security-Policy: script-src 'nonce-jquery+hardcoded+nonce'; report-uri ./mock.php?action=cspLog" );
|
||||||
header( 'Content-type: text/html' );
|
header( 'Content-type: text/html' );
|
||||||
echo file_get_contents( __DIR__ . '/csp-nonce' . $test . '.html' );
|
echo file_get_contents( __DIR__ . '/csp-nonce' . $test . '.html' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function cspAjaxScript( $req ) {
|
||||||
|
header( "Content-Security-Policy: script-src 'self'; report-uri /base/test/data/mock.php?action=cspLog" );
|
||||||
|
header( 'Content-type: text/html' );
|
||||||
|
echo file_get_contents( __DIR__ . '/csp-ajax-script.html' );
|
||||||
|
}
|
||||||
|
|
||||||
protected function cspLog( $req ) {
|
protected function cspLog( $req ) {
|
||||||
file_put_contents( $this->cspFile, 'error' );
|
file_put_contents( $this->cspFile, 'error' );
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,15 @@ var mocks = {
|
|||||||
__dirname + "/data/csp-nonce" + testParam + ".html" ).toString();
|
__dirname + "/data/csp-nonce" + testParam + ".html" ).toString();
|
||||||
resp.end( body );
|
resp.end( body );
|
||||||
},
|
},
|
||||||
|
cspAjaxScript: function( req, resp ) {
|
||||||
|
resp.writeHead( 200, {
|
||||||
|
"Content-Type": "text/html",
|
||||||
|
"Content-Security-Policy": "script-src 'self'; report-uri /base/test/data/mock.php?action=cspLog"
|
||||||
|
} );
|
||||||
|
var body = fs.readFileSync(
|
||||||
|
__dirname + "/data/csp-ajax-script.html" ).toString();
|
||||||
|
resp.end( body );
|
||||||
|
},
|
||||||
cspLog: function( req, resp ) {
|
cspLog: function( req, resp ) {
|
||||||
cspLog = "error";
|
cspLog = "error";
|
||||||
resp.writeHead( 200 );
|
resp.writeHead( 200 );
|
||||||
|
@ -89,17 +89,21 @@ QUnit.module( "ajax", {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ajaxTest( "jQuery.ajax() - custom attributes for script tag", 4,
|
ajaxTest( "jQuery.ajax() - custom attributes for script tag", 5,
|
||||||
function( assert ) {
|
function( assert ) {
|
||||||
return {
|
return {
|
||||||
create: function( options ) {
|
create: function( options ) {
|
||||||
var xhr;
|
var xhr;
|
||||||
|
options.method = "POST";
|
||||||
options.dataType = "script";
|
options.dataType = "script";
|
||||||
options.scriptAttrs = { id: "jquery-ajax-test", async: "async" };
|
options.scriptAttrs = { id: "jquery-ajax-test", async: "async" };
|
||||||
xhr = jQuery.ajax( url( "mock.php?action=script" ), options );
|
xhr = jQuery.ajax( url( "mock.php?action=script" ), options );
|
||||||
assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" );
|
assert.equal( jQuery( "#jquery-ajax-test" ).attr( "async" ), "async", "attr value" );
|
||||||
return xhr;
|
return xhr;
|
||||||
},
|
},
|
||||||
|
beforeSend: function( _jqXhr, settings ) {
|
||||||
|
assert.strictEqual( settings.type, "GET", "Type changed to GET" );
|
||||||
|
},
|
||||||
success: function() {
|
success: function() {
|
||||||
assert.ok( true, "success" );
|
assert.ok( true, "success" );
|
||||||
},
|
},
|
||||||
@ -1356,6 +1360,17 @@ QUnit.module( "ajax", {
|
|||||||
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
testIframe(
|
||||||
|
"jQuery.ajax() - script, CSP script-src compat (gh-3969)",
|
||||||
|
"mock.php?action=cspAjaxScript",
|
||||||
|
function( assert, jQuery, window, document, type, downloadedScriptCalled ) {
|
||||||
|
assert.expect( 2 );
|
||||||
|
|
||||||
|
assert.strictEqual( type, "GET", "Type changed to GET" );
|
||||||
|
assert.strictEqual( downloadedScriptCalled, true, "External script called" );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
ajaxTest( "jQuery.ajax() - script, Remote", 2, function( assert ) {
|
ajaxTest( "jQuery.ajax() - script, Remote", 2, function( assert ) {
|
||||||
return {
|
return {
|
||||||
setup: function() {
|
setup: function() {
|
||||||
@ -1369,12 +1384,15 @@ QUnit.module( "ajax", {
|
|||||||
};
|
};
|
||||||
} );
|
} );
|
||||||
|
|
||||||
ajaxTest( "jQuery.ajax() - script, Remote with POST", 3, function( assert ) {
|
ajaxTest( "jQuery.ajax() - script, Remote with POST", 4, function( assert ) {
|
||||||
return {
|
return {
|
||||||
setup: function() {
|
setup: function() {
|
||||||
Globals.register( "testBar" );
|
Globals.register( "testBar" );
|
||||||
},
|
},
|
||||||
url: url( "mock.php?action=testbar" ),
|
url: url( "mock.php?action=testbar" ),
|
||||||
|
beforeSend: function( _jqXhr, settings ) {
|
||||||
|
assert.strictEqual( settings.type, "GET", "Type changed to GET" );
|
||||||
|
},
|
||||||
type: "POST",
|
type: "POST",
|
||||||
dataType: "script",
|
dataType: "script",
|
||||||
success: function( data, status ) {
|
success: function( data, status ) {
|
||||||
|
Loading…
Reference in New Issue
Block a user