Bug 1187335 - P6 - Support script/css to set integrity metadata to serviceWorker. r=bkelly. r=francois.
authorTom Tung <ttung@mozilla.com>
Wed, 07 Sep 2016 10:30:21 +0800
changeset 313232 126d0c86f8ee4288ded7fc349e79d5e9bde0152e
parent 313231 46b85380cb2d56d7d9fa4e3a6fda16378b14ebc3
child 313233 54e0b3c264e59bfb4a1d943558d56512502e7339
push id30676
push userkwierso@gmail.com
push dateThu, 08 Sep 2016 22:22:24 +0000
treeherdermozilla-central@176aff980979 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly, francois
bugs1187335
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1187335 - P6 - Support script/css to set integrity metadata to serviceWorker. r=bkelly. r=francois.
dom/base/nsScriptLoader.cpp
dom/security/SRICheck.cpp
dom/security/SRIMetadata.h
layout/style/Loader.cpp
testing/web-platform/tests/service-workers/service-worker/fetch-request-resources.https.html
testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html
testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -24,16 +24,17 @@
 #include "nsNetUtil.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsJSPrincipals.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
 #include "nsIClassOfService.h"
 #include "nsITimedChannel.h"
 #include "nsIScriptElement.h"
 #include "nsIDOMHTMLScriptElement.h"
 #include "nsIDocShell.h"
 #include "nsContentUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsAutoPtr.h"
@@ -1262,16 +1263,21 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel) {
     // HTTP content negotation has little value in this context.
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                   NS_LITERAL_CSTRING("*/*"),
                                   false);
     httpChannel->SetReferrerWithPolicy(mDocument->GetDocumentURI(),
                                        aRequest->mReferrerPolicy);
+
+    nsCOMPtr<nsIHttpChannelInternal> internalChannel(do_QueryInterface(httpChannel));
+    if (internalChannel) {
+      internalChannel->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString());
+    }
   }
 
   nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(docshell));
   mozilla::net::PredictorLearn(aRequest->mURI, mDocument->GetDocumentURI(),
       nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadContext);
 
   // Set the initiator type
   nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
--- a/dom/security/SRICheck.cpp
+++ b/dom/security/SRICheck.cpp
@@ -155,16 +155,18 @@ SRICheck::IntegrityMetadata(const nsAStr
       *outMetadata += metadata; // add new hash to strongest metadata
     } else if (*outMetadata < metadata) {
       SRILOG(("SRICheck::IntegrityMetadata, alg '%s' is weaker than '%s'",
               alg1.get(), alg2.get()));
       *outMetadata = metadata; // replace strongest metadata with current
     }
   }
 
+  outMetadata->mIntegrityString = aMetadataList;
+
   if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) {
     if (outMetadata->IsValid()) {
       nsAutoCString alg;
       outMetadata->GetAlgorithm(&alg);
       SRILOG(("SRICheck::IntegrityMetadata, using a '%s' hash", alg.get()));
     } else if (outMetadata->IsEmpty()) {
       SRILOG(("SRICheck::IntegrityMetadata, no metadata"));
     } else {
--- a/dom/security/SRIMetadata.h
+++ b/dom/security/SRIMetadata.h
@@ -4,22 +4,25 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_SRIMetadata_h
 #define mozilla_dom_SRIMetadata_h
 
 #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;
 
   /**
    * Create an empty metadata object.
    */
   SRIMetadata() : mAlgorithmType(UNKNOWN_ALGORITHM), mEmpty(true) {}
@@ -56,18 +59,24 @@ public:
   bool IsAlgorithmSupported() const { return mAlgorithmType != UNKNOWN_ALGORITHM; }
   bool IsValid() const { return !IsMalformed() && IsAlgorithmSupported(); }
 
   uint32_t HashCount() const { return mHashes.Length(); }
   void GetHash(uint32_t aIndex, nsCString* outHash) const;
   void GetAlgorithm(nsCString* outAlg) const { *outAlg = mAlgorithm; }
   void GetHashType(int8_t* outType, uint32_t* outLength) const;
 
+  const nsString& GetIntegrityString() const
+  {
+    return mIntegrityString;
+  }
+
 private:
   nsTArray<nsCString> mHashes;
+  nsString mIntegrityString;
   nsCString mAlgorithm;
   int8_t mAlgorithmType;
   bool mEmpty;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -33,16 +33,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsIProtocolHandler.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
 #include "nsIClassOfService.h"
 #include "nsIScriptError.h"
 #include "nsMimeTypes.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsICSSLoaderObserver.h"
 #include "nsCSSParser.h"
 #include "mozilla/CSSStyleSheet.h"
 #include "mozilla/css/ImportRule.h"
@@ -1681,16 +1682,21 @@ Loader::LoadSheet(SheetLoadData* aLoadDa
     httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                   NS_LITERAL_CSTRING("text/css,*/*;q=0.1"),
                                   false);
     nsCOMPtr<nsIURI> referrerURI = aLoadData->GetReferrerURI();
     if (referrerURI)
       httpChannel->SetReferrerWithPolicy(referrerURI,
                                          aLoadData->mSheet->GetReferrerPolicy());
 
+    nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
+    if (internalChannel) {
+      internalChannel->SetIntegrityMetadata(sriMetadata.GetIntegrityString());
+    }
+
     // Set the initiator type
     nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
     if (timedChannel) {
       if (aLoadData->mParentData) {
         timedChannel->SetInitiatorType(NS_LITERAL_STRING("css"));
       } else {
         timedChannel->SetInitiatorType(NS_LITERAL_STRING("link"));
       }
--- 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
@@ -3,64 +3,92 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/get-host-info.sub.js"></script>
 <script src="resources/test-helpers.sub.js?pipe=sub"></script>
 <script>
 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: 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: expexted_mode,
-      credentials: expected_credentials,
-      message: 'FontFace load (url:' + actual_url + ')'
+      mode: 'no-cors',
+      credentials: 'include',
+      integrity: expected_integrity,
+      message: 'Script load (url:' + actual_url + ')'
     };
-  return frame.contentWindow.load_font(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';
     var host_info = get_host_info();
     var LOCAL_URL =
       host_info['HTTPS_ORIGIN'] + base_path() + 'resources/dummy?test';
@@ -89,16 +117,20 @@ async_test(function(t) {
                 assert_equals(
                     result.mode, expected.mode,
                     'mode of ' + expected.message +  ' must be ' +
                     expected.mode + '.');
                 assert_equals(
                     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) {
                   frame.remove();
                   service_worker_unregister_and_done(t, SCOPE);
                 }
               });
               worker.postMessage(
@@ -129,12 +161,29 @@ async_test(function(t) {
         css_test(f, LOCAL_URL, 'anonymous', 'cors', 'same-origin');
         css_test(f, LOCAL_URL, 'use-credentials', 'cors', 'include');
         css_test(f, REMOTE_URL, 'anonymous', 'cors', 'same-origin');
         css_test(f, REMOTE_URL, 'use-credentials', 'cors', 'include');
 
         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.');
 </script>
--- 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
@@ -42,10 +42,26 @@ function load_css_image(url, type) {
 }
 
 function load_css_image_set(url, type) {
   var div = document.createElement('div');
   document.body.appendChild(div);
   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);
+}
+
 </script>
 </body>
--- 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
@@ -12,12 +12,13 @@ self.onmessage = function(e) {
 self.addEventListener('fetch', function(event) {
     var url = event.request.url;
     if (url.indexOf('dummy?test') == -1) {
       return;
     }
     port.postMessage({
         url: url,
         mode: event.request.mode,
-        credentials: event.request.credentials
+        credentials: event.request.credentials,
+        integrity: event.request.integrity
       });
     event.respondWith(Promise.reject());
   });