From db38e2111aa9efa4425d1c448d7c15248f3eec9a Mon Sep 17 00:00:00 2001 From: Tom Tung Date: Wed, 7 Sep 2016 10:30:21 +0800 Subject: [PATCH] Bug 1187335 - P6 - Support script/css to set integrity metadata to serviceWorker. r=bkelly. r=francois. --- dom/base/nsScriptLoader.cpp | 6 ++ dom/security/SRICheck.cpp | 2 + dom/security/SRIMetadata.h | 9 +++ layout/style/Loader.cpp | 6 ++ .../fetch-request-resources.https.html | 65 ++++++++++++++++--- .../fetch-request-resources-iframe.https.html | 16 +++++ .../fetch-request-resources-worker.js | 3 +- 7 files changed, 98 insertions(+), 9 deletions(-) diff --git a/dom/base/nsScriptLoader.cpp b/dom/base/nsScriptLoader.cpp index 4413c4837ab7..df82f4854f0c 100644 --- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -29,6 +29,7 @@ #include "nsJSPrincipals.h" #include "nsContentPolicyUtils.h" #include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" #include "nsIClassOfService.h" #include "nsITimedChannel.h" #include "nsIScriptElement.h" @@ -1267,6 +1268,11 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType, false); httpChannel->SetReferrerWithPolicy(mDocument->GetDocumentURI(), aRequest->mReferrerPolicy); + + nsCOMPtr internalChannel(do_QueryInterface(httpChannel)); + if (internalChannel) { + internalChannel->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString()); + } } nsCOMPtr loadContext(do_QueryInterface(docshell)); diff --git a/dom/security/SRICheck.cpp b/dom/security/SRICheck.cpp index df61dd94d8d1..6aa062b00928 100644 --- a/dom/security/SRICheck.cpp +++ b/dom/security/SRICheck.cpp @@ -160,6 +160,8 @@ SRICheck::IntegrityMetadata(const nsAString& aMetadataList, } } + outMetadata->mIntegrityString = aMetadataList; + if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) { if (outMetadata->IsValid()) { nsAutoCString alg; diff --git a/dom/security/SRIMetadata.h b/dom/security/SRIMetadata.h index 4a37c646a3a4..4b6bdb47ad19 100644 --- a/dom/security/SRIMetadata.h +++ b/dom/security/SRIMetadata.h @@ -9,12 +9,15 @@ #include "nsTArray.h" #include "nsString.h" +#include "SRICheck.h" namespace mozilla { namespace dom { class SRIMetadata final { + friend class SRICheck; + public: static const uint32_t MAX_ALTERNATE_HASHES = 256; static const int8_t UNKNOWN_ALGORITHM = -1; @@ -61,8 +64,14 @@ public: void GetAlgorithm(nsCString* outAlg) const { *outAlg = mAlgorithm; } void GetHashType(int8_t* outType, uint32_t* outLength) const; + const nsString& GetIntegrityString() const + { + return mIntegrityString; + } + private: nsTArray mHashes; + nsString mIntegrityString; nsCString mAlgorithm; int8_t mAlgorithmType; bool mEmpty; diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 9a58a1855b37..e2baedcd6e0c 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -38,6 +38,7 @@ #include "nsIScriptSecurityManager.h" #include "nsContentPolicyUtils.h" #include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" #include "nsIClassOfService.h" #include "nsIScriptError.h" #include "nsMimeTypes.h" @@ -1686,6 +1687,11 @@ Loader::LoadSheet(SheetLoadData* aLoadData, httpChannel->SetReferrerWithPolicy(referrerURI, aLoadData->mSheet->GetReferrerPolicy()); + nsCOMPtr internalChannel = do_QueryInterface(httpChannel); + if (internalChannel) { + internalChannel->SetIntegrityMetadata(sriMetadata.GetIntegrityString()); + } + // Set the initiator type nsCOMPtr timedChannel(do_QueryInterface(httpChannel)); if (timedChannel) { diff --git a/testing/web-platform/tests/service-workers/service-worker/fetch-request-resources.https.html b/testing/web-platform/tests/service-workers/service-worker/fetch-request-resources.https.html index 0584cafb075d..7a64e9444dc9 100644 --- a/testing/web-platform/tests/service-workers/service-worker/fetch-request-resources.https.html +++ b/testing/web-platform/tests/service-workers/service-worker/fetch-request-resources.https.html @@ -8,56 +8,84 @@ var url_count = 0; var expected_results = {}; -function image_test(frame, url, cross_origin, expexted_mode, +function image_test(frame, url, cross_origin, expected_mode, expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { cross_origin: cross_origin, - mode: expexted_mode, + mode: expected_mode, credentials: expected_credentials, + integrity: '', message: 'Image load (url:' + actual_url + ' cross_origin:' + cross_origin + ')' }; return frame.contentWindow.load_image(actual_url, cross_origin); } -function script_test(frame, url, cross_origin, expexted_mode, +function script_test(frame, url, cross_origin, expected_mode, expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { cross_origin: cross_origin, - mode: expexted_mode, + mode: expected_mode, credentials: expected_credentials, + integrity: '', message: 'Script load (url:' + actual_url + ' cross_origin:' + cross_origin + ')' }; return frame.contentWindow.load_script(actual_url, cross_origin); } -function css_test(frame, url, cross_origin, expexted_mode, +function css_test(frame, url, cross_origin, expected_mode, expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { cross_origin: cross_origin, - mode: expexted_mode, + mode: expected_mode, credentials: expected_credentials, + integrity: '', message: 'CSS load (url:' + actual_url + ' cross_origin:' + cross_origin + ')' }; return frame.contentWindow.load_css(actual_url, cross_origin); } -function font_face_test(frame, url, expexted_mode, expected_credentials) { +function font_face_test(frame, url, expected_mode, expected_credentials) { var actual_url = url + (++url_count); expected_results[actual_url] = { url: actual_url, - mode: expexted_mode, + mode: expected_mode, credentials: expected_credentials, + integrity: '', message: 'FontFace load (url:' + actual_url + ')' }; return frame.contentWindow.load_font(actual_url); } +function script_integrity_test(frame, url, integrity, expected_integrity) { + var actual_url = url + (++url_count); + expected_results[actual_url] = { + url: actual_url, + mode: 'no-cors', + credentials: 'include', + integrity: expected_integrity, + message: 'Script load (url:' + actual_url + ')' + }; + return frame.contentWindow.load_script_with_integrity(actual_url, integrity); +} + +function css_integrity_test(frame, url, integrity, expected_integrity) { + var actual_url = url + (++url_count); + expected_results[actual_url] = { + url: actual_url, + mode: 'no-cors', + credentials: 'include', + integrity: expected_integrity, + message: 'CSS load (url:' + actual_url + ')' + }; + return frame.contentWindow.load_css_with_integrity(actual_url, integrity); +} + async_test(function(t) { var SCOPE = 'resources/fetch-request-resources-iframe.https.html'; var SCRIPT = 'resources/fetch-request-resources-worker.js'; @@ -94,6 +122,10 @@ async_test(function(t) { result.credentials, expected.credentials, 'credentials of ' + expected.message + ' must be ' + expected.credentials + '.'); + assert_equals( + result.integrity, expected.integrity, + 'integrity of ' + expected.message + ' must be ' + + expected.integrity + '.'); --url_count; delete expected_results[result.url]; if (url_count == 0) { @@ -134,6 +166,23 @@ async_test(function(t) { font_face_test(f, LOCAL_URL, 'cors', 'same-origin'); font_face_test(f, REMOTE_URL, 'cors', 'same-origin'); + script_integrity_test(f, LOCAL_URL, ' ', ' '); + script_integrity_test(f, LOCAL_URL, + 'This is not a valid integrity because it has no dashes', + 'This is not a valid integrity because it has no dashes'); + script_integrity_test(f, LOCAL_URL, 'sha256-', 'sha256-'); + script_integrity_test(f, LOCAL_URL, 'sha256-foo?123', 'sha256-foo?123'); + script_integrity_test(f, LOCAL_URL, 'sha256-foo sha384-abc ', 'sha256-foo sha384-abc '); + script_integrity_test(f, LOCAL_URL, 'sha256-foo sha256-abc', 'sha256-foo sha256-abc'); + + css_integrity_test(f, LOCAL_URL, ' ', ' '); + css_integrity_test(f, LOCAL_URL, + 'This is not a valid integrity because it has no dashes', + 'This is not a valid integrity because it has no dashes'); + css_integrity_test(f, LOCAL_URL, 'sha256-', 'sha256-'); + css_integrity_test(f, LOCAL_URL, 'sha256-foo?123', 'sha256-foo?123'); + css_integrity_test(f, LOCAL_URL, 'sha256-foo sha384-abc ', 'sha256-foo sha384-abc '); + css_integrity_test(f, LOCAL_URL, 'sha256-foo sha256-abc', 'sha256-foo sha256-abc'); }) .catch(unreached_rejection(t)); }, 'Verify FetchEvent for resources.'); diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html index cadbff45cae2..93b038dd67c5 100644 --- a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html +++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html @@ -47,5 +47,21 @@ function load_css_image_set(url, type) { div.style[type] = '-webkit-image-set(url(' + url + ') 1x)'; } +function load_script_with_integrity(url, integrity) { + var script = document.createElement('script'); + script.src = url; + script.integrity = integrity; + document.body.appendChild(script); +} + +function load_css_with_integrity(url, integrity) { + var link = document.createElement('link'); + link.rel = 'stylesheet' + link.href = url; + link.type = 'text/css'; + link.integrity = integrity; + document.body.appendChild(link); +} + diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js index 9ec1dc628222..900b63c62fc7 100644 --- a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js +++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js @@ -17,7 +17,8 @@ self.addEventListener('fetch', function(event) { port.postMessage({ url: url, mode: event.request.mode, - credentials: event.request.credentials + credentials: event.request.credentials, + integrity: event.request.integrity }); event.respondWith(Promise.reject()); });