From: Michał Gołębiowski-Owczarek Date: Mon, 21 Jan 2019 17:42:39 +0000 (+0100) Subject: Core: Support passing nonce through jQuery.globalEval X-Git-Tag: 3.4.0~22 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5bdc85b82b84e5459462ddad9002f22d1ce74f21;p=jquery.git Core: Support passing nonce through jQuery.globalEval Fixes gh-4278 Closes gh-4280 Ref gh-3541 Ref gh-4269 --- diff --git a/src/core.js b/src/core.js index 84f9afe13..d9c7e9882 100644 --- a/src/core.js +++ b/src/core.js @@ -238,8 +238,8 @@ jQuery.extend( { }, // Evaluates a script in a global context - globalEval: function( code ) { - DOMEval( code ); + globalEval: function( code, options ) { + DOMEval( code, { nonce: options && options.nonce } ); }, each: function( obj, callback ) { diff --git a/src/core/DOMEval.js b/src/core/DOMEval.js index 8d2d0023b..59f6e0247 100644 --- a/src/core/DOMEval.js +++ b/src/core/DOMEval.js @@ -10,26 +10,29 @@ define( [ noModule: true }; - function DOMEval( code, doc, node ) { + function DOMEval( code, node, doc ) { doc = doc || document; - var i, + var i, val, script = doc.createElement( "script" ); script.text = code; if ( node ) { for ( i in preservedScriptAttributes ) { - if ( node[ i ] ) { - script[ i ] = node[ i ]; - } else if ( node.getAttribute( i ) ) { - // Support: Firefox 64+, Edge 18+ - // Some browsers don't support the "nonce" property on scripts. - // On the other hand, just using `setAttribute` & `getAttribute` - // is not enough as `nonce` is no longer exposed as an attribute - // in the latest standard. - // See https://github.com/whatwg/html/issues/2369 - script.setAttribute( i, node.getAttribute( i ) ); + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); } } } diff --git a/src/manipulation.js b/src/manipulation.js index 042728573..a24a5cc0c 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -202,7 +202,7 @@ function domManip( collection, args, callback, ignored ) { jQuery._evalUrl( node.src ); } } else { - DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); } } } diff --git a/test/data/csp-nonce-globaleval.html b/test/data/csp-nonce-globaleval.html new file mode 100644 index 000000000..aa620c566 --- /dev/null +++ b/test/data/csp-nonce-globaleval.html @@ -0,0 +1,13 @@ + + + + + CSP nonce via jQuery.globalEval Test Page + + + + + +

CSP nonce via jQuery.globalEval Test Page

+ + diff --git a/test/data/csp-nonce-globaleval.js b/test/data/csp-nonce-globaleval.js new file mode 100644 index 000000000..23d549fd6 --- /dev/null +++ b/test/data/csp-nonce-globaleval.js @@ -0,0 +1,5 @@ +/* global startIframeTest */ + +jQuery( function() { + $.globalEval( "startIframeTest()", { nonce: "jquery+hardcoded+nonce" } ); +} ); diff --git a/test/data/mock.php b/test/data/mock.php index 7e6aa1bec..52de8ae9d 100644 --- a/test/data/mock.php +++ b/test/data/mock.php @@ -201,9 +201,10 @@ ok( true, "mock executed");'; 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'] : ''; header( "Content-Security-Policy: script-src 'nonce-jquery+hardcoded+nonce'; report-uri ./mock.php?action=cspLog" ); header( 'Content-type: text/html' ); - echo file_get_contents( __DIR__ . '/csp-nonce.html' ); + echo file_get_contents( __DIR__ . '/csp-nonce' . $test . '.html' ); } protected function cspLog( $req ) { diff --git a/test/middleware-mockserver.js b/test/middleware-mockserver.js index feed28148..12c2e7533 100644 --- a/test/middleware-mockserver.js +++ b/test/middleware-mockserver.js @@ -208,11 +208,13 @@ var mocks = { resp.end( body ); }, cspNonce: function( req, resp ) { + var testParam = req.query.test ? "-" + req.query.test : ""; resp.writeHead( 200, { "Content-Type": "text/html", "Content-Security-Policy": "script-src 'nonce-jquery+hardcoded+nonce'; report-uri /base/test/data/mock.php?action=cspLog" } ); - var body = fs.readFileSync( __dirname + "/data/csp-nonce.html" ).toString(); + var body = fs.readFileSync( + __dirname + "/data/csp-nonce" + testParam + ".html" ).toString(); resp.end( body ); }, cspLog: function( req, resp ) { diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index 7f30ddf25..0acb45f3e 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -2888,3 +2888,26 @@ testIframe( // script-src restrictions completely. QUnit[ /\bedge\/|iphone os [789]|android 4\./i.test( navigator.userAgent ) ? "skip" : "test" ] ); + +testIframe( + "jQuery.globalEval supports nonce", + "mock.php?action=cspNonce&test=globaleval", + function( assert, jQuery, window, document ) { + var done = assert.async(); + + assert.expect( 1 ); + + supportjQuery.get( baseURL + "support/csp.log" ).done( function( data ) { + assert.equal( data, "", "No log request should be sent" ); + supportjQuery.get( baseURL + "mock.php?action=cspClean" ).done( done ); + } ); + }, + + // Support: Edge 18+, iOS 7-9 only, Android 4.0-4.4 only + // Edge doesn't support nonce in non-inline scripts. + // See https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/13246371/ + // Old iOS & Android Browser versions support script-src but not nonce, making this test + // impossible to run. Browsers not supporting CSP at all are not a problem as they'll skip + // script-src restrictions completely. + QUnit[ /\bedge\/|iphone os [789]|android 4\./i.test( navigator.userAgent ) ? "skip" : "test" ] +);