From 4250b628783d7bfa92ec6c5550c6e4b22fab6034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Go=C5=82=C4=99biowski-Owczarek?= Date: Mon, 1 Nov 2021 18:10:23 +0100 Subject: [PATCH] Attributes: Don't stringify attributes in the setter Stringifying attributes in the setter was needed for IE <=9 but it breaks trusted types enforcement when setting a script `src` attribute. Note that this doesn't mean script execution works. Since jQuery disables all scripts by changing their type and then executes them by creating fresh script tags with proper `src` & possibly other attributes, this unwraps any trusted `src` wrappers, making the script not execute under strict CSP settings. We might try to fix it in the future in a separate change. Fixes gh-4948 Closes gh-4949 --- src/attributes/attr.js | 2 +- test/data/mock.php | 6 +++ test/data/trusted-types-attributes.html | 61 +++++++++++++++++++++++++ test/data/trusted-types-attributes.js | 1 + test/middleware-mockserver.js | 8 ++++ test/unit/attributes.js | 20 ++++++++ 6 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/data/trusted-types-attributes.html create mode 100644 test/data/trusted-types-attributes.js diff --git a/src/attributes/attr.js b/src/attributes/attr.js index 2773a383c..d6d497735 100644 --- a/src/attributes/attr.js +++ b/src/attributes/attr.js @@ -50,7 +50,7 @@ jQuery.extend( { return ret; } - elem.setAttribute( name, value + "" ); + elem.setAttribute( name, value ); return value; } diff --git a/test/data/mock.php b/test/data/mock.php index 268ad06ef..c72bea9f3 100644 --- a/test/data/mock.php +++ b/test/data/mock.php @@ -247,6 +247,12 @@ QUnit.assert.ok( true, "mock executed");'; echo file_get_contents( __DIR__ . '/trusted-html.html' ); } + protected function trustedTypesAttributes( $req ) { + header( "Content-Security-Policy: require-trusted-types-for 'script'; report-uri ./mock.php?action=cspLog" ); + header( 'Content-type: text/html' ); + echo file_get_contents( __DIR__ . '/trusted-types-attributes.html' ); + } + protected function errorWithScript( $req ) { header( 'HTTP/1.0 404 Not Found' ); if ( isset( $req->query['withScriptContentType'] ) ) { diff --git a/test/data/trusted-types-attributes.html b/test/data/trusted-types-attributes.html new file mode 100644 index 000000000..1767226cc --- /dev/null +++ b/test/data/trusted-types-attributes.html @@ -0,0 +1,61 @@ + + + + + Trusted HTML attribute tests + + +
+ + + + + diff --git a/test/data/trusted-types-attributes.js b/test/data/trusted-types-attributes.js new file mode 100644 index 000000000..907edf8c8 --- /dev/null +++ b/test/data/trusted-types-attributes.js @@ -0,0 +1 @@ +window.testMessage = "script run"; diff --git a/test/middleware-mockserver.js b/test/middleware-mockserver.js index 0bd44f95b..ea6a5d2b0 100644 --- a/test/middleware-mockserver.js +++ b/test/middleware-mockserver.js @@ -264,6 +264,14 @@ var mocks = { var body = fs.readFileSync( __dirname + "/data/trusted-html.html" ).toString(); resp.end( body ); }, + trustedTypesAttributes: function( req, resp ) { + resp.writeHead( 200, { + "Content-Type": "text/html", + "Content-Security-Policy": "require-trusted-types-for 'script'; report-uri /base/test/data/mock.php?action=cspLog" + } ); + var body = fs.readFileSync( __dirname + "/data/trusted-types-attributes.html" ).toString(); + resp.end( body ); + }, errorWithScript: function( req, resp ) { if ( req.query.withScriptContentType ) { resp.writeHead( 404, { "Content-Type": "application/javascript" } ); diff --git a/test/unit/attributes.js b/test/unit/attributes.js index d83375172..2658495ae 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -1764,3 +1764,23 @@ QUnit.test( "non-lowercase boolean attribute getters should not crash", function } } ); } ); + + +// Test trustedTypes support in browsers where they're supported (currently Chrome 83+). +// Browsers with no TrustedScriptURL support still run tests on object wrappers with +// a proper `toString` function. +testIframe( + "Basic TrustedScriptURL support (gh-4948)", + "mock.php?action=trustedTypesAttributes", + function( assert, jQuery, window, document, test ) { + var done = assert.async(); + + assert.expect( 1 ); + + test.forEach( function( result ) { + assert.deepEqual( result.actual, result.expected, result.message ); + } ); + + supportjQuery.get( baseURL + "mock.php?action=cspClean" ).then( done ); + } +);