Bug 1603168 - Reject Cache.match and Cache.matchAll if the response doesn't match the caller context coep r=dom-workers-and-storage-reviewers,perry
authorEden Chuang <echuang@mozilla.com>
Sat, 23 May 2020 01:44:58 +0000
changeset 531755 bbcc193fe0f0389a417ab4e047dc62b4837bf6b1
parent 531754 90998e8e1bdd4046fb5ff04e622a840dfc531c5c
child 531756 c74722b0c0d5cde29b435e228a290eea157377a1
push id37442
push userncsoregi@mozilla.com
push dateSat, 23 May 2020 09:21:24 +0000
treeherdermozilla-central@bbcc193fe0f0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdom-workers-and-storage-reviewers, perry
bugs1603168
milestone78.0a1
first release with
nightly linux32
bbcc193fe0f0 / 78.0a1 / 20200523092124 / files
nightly linux64
bbcc193fe0f0 / 78.0a1 / 20200523092124 / files
nightly mac
bbcc193fe0f0 / 78.0a1 / 20200523092124 / files
nightly win32
bbcc193fe0f0 / 78.0a1 / 20200523092124 / files
nightly win64
bbcc193fe0f0 / 78.0a1 / 20200523092124 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1603168 - Reject Cache.match and Cache.matchAll if the response doesn't match the caller context coep r=dom-workers-and-storage-reviewers,perry Differential Revision: https://phabricator.services.mozilla.com/D70020
dom/cache/CacheOpParent.cpp
dom/cache/CacheOpParent.h
dom/cache/CacheTypes.ipdlh
dom/cache/TypeUtils.cpp
dom/workers/ScriptLoader.cpp
testing/web-platform/meta/html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https.html.ini
testing/web-platform/meta/html/cross-origin-embedder-policy/service-worker-cache-storage.https.html.ini
--- a/dom/cache/CacheOpParent.cpp
+++ b/dom/cache/CacheOpParent.cpp
@@ -149,16 +149,25 @@ void CacheOpParent::OnOpComplete(ErrorRe
 
   // Never send an op-specific result if we have an error.  Instead, send
   // void_t() to ensure that we don't leak actors on the child side.
   if (NS_WARN_IF(aRv.Failed())) {
     Unused << Send__delete__(this, std::move(aRv), void_t());
     return;
   }
 
+  if (aStreamInfo.isSome()) {
+    ProcessCrossOriginResourcePolicyHeader(aRv,
+                                           aStreamInfo->mSavedResponseList);
+    if (NS_WARN_IF(aRv.Failed())) {
+      Unused << Send__delete__(this, std::move(aRv), void_t());
+      return;
+    }
+  }
+
   uint32_t entryCount =
       std::max(1lu, aStreamInfo ? static_cast<unsigned long>(std::max(
                                       aStreamInfo->mSavedResponseList.Length(),
                                       aStreamInfo->mSavedRequestList.Length()))
                                 : 0lu);
 
   // The result must contain the appropriate type at this point.  It may
   // or may not contain the additional result data yet.  For types that
@@ -204,11 +213,109 @@ already_AddRefed<nsIInputStream> CacheOp
   }
 
   // Option 2: A stream was serialized using normal methods or passed
   //           as a PChildToParentStream actor.  Use the standard method for
   //           extracting the resulting stream.
   return DeserializeIPCStream(readStream.stream());
 }
 
+void CacheOpParent::ProcessCrossOriginResourcePolicyHeader(
+    ErrorResult& aRv, const nsTArray<SavedResponse>& aResponses) {
+  // Only checking for match/matchAll.
+  RequestMode mode = RequestMode::No_cors;
+  nsILoadInfo::CrossOriginEmbedderPolicy loadingCOEP =
+      nsILoadInfo::EMBEDDER_POLICY_NULL;
+  Maybe<PrincipalInfo> principalInfo;
+  switch (mOpArgs.type()) {
+    case CacheOpArgs::TCacheMatchArgs: {
+      mode = mOpArgs.get_CacheMatchArgs().request().mode();
+      loadingCOEP =
+          mOpArgs.get_CacheMatchArgs().request().loadingEmbedderPolicy();
+      principalInfo = mOpArgs.get_CacheMatchArgs().request().principalInfo();
+      break;
+    }
+    case CacheOpArgs::TCacheMatchAllArgs: {
+      if (mOpArgs.get_CacheMatchAllArgs().maybeRequest().isSome()) {
+        mode = mOpArgs.get_CacheMatchAllArgs().maybeRequest().ref().mode();
+        loadingCOEP = mOpArgs.get_CacheMatchAllArgs()
+                          .maybeRequest()
+                          .ref()
+                          .loadingEmbedderPolicy();
+        principalInfo = mOpArgs.get_CacheMatchAllArgs()
+                            .maybeRequest()
+                            .ref()
+                            .principalInfo();
+      }
+      break;
+    }
+    default: {
+      return;
+    }
+  }
+
+  // skip checking for CORS mode
+  if (mode == RequestMode::Cors) {
+    return;
+  }
+
+  // skip checking if the request has no principal for same-origin/same-site
+  // checking.
+  if (principalInfo.isNothing() ||
+      principalInfo.ref().type() != PrincipalInfo::TContentPrincipalInfo) {
+    return;
+  }
+  const ContentPrincipalInfo& contentPrincipalInfo =
+      principalInfo.ref().get_ContentPrincipalInfo();
+
+  nsAutoCString corp;
+  for (auto it = aResponses.cbegin(); it != aResponses.cend(); ++it) {
+    corp.Assign(EmptyCString());
+    for (auto headerIt = it->mValue.headers().cbegin();
+         headerIt != it->mValue.headers().cend(); ++headerIt) {
+      if (headerIt->name().Equals(
+              NS_LITERAL_CSTRING("Cross-Origin-Resource-Policy"))) {
+        corp = headerIt->value();
+        break;
+      }
+    }
+
+    // According to https://github.com/w3c/ServiceWorker/issues/1490, the cache
+    // response is expected with CORP header, otherwise, throw the type error.
+    // Note that this is different with the CORP checking for fetch metioned in
+    // https://wicg.github.io/cross-origin-embedder-policy/#corp-check.
+    // For fetch, if the response has no CORP header, "same-origin" checking
+    // will be performed.
+    if (corp.IsEmpty() &&
+        loadingCOEP == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
+      aRv.ThrowTypeError("Response is expected with CORP header.");
+      return;
+    }
+
+    // Skip the case if the response has no principal for same-origin/same-site
+    // checking.
+    if (it->mValue.principalInfo().isNothing() ||
+        it->mValue.principalInfo().ref().type() !=
+            PrincipalInfo::TContentPrincipalInfo) {
+      continue;
+    }
+
+    const ContentPrincipalInfo& responseContentPrincipalInfo =
+        it->mValue.principalInfo().ref().get_ContentPrincipalInfo();
+
+    if (corp.EqualsLiteral("same-origin")) {
+      if (responseContentPrincipalInfo == contentPrincipalInfo) {
+        aRv.ThrowTypeError("Response is expected from same origin.");
+        return;
+      }
+    } else if (corp.EqualsLiteral("same-site")) {
+      if (!responseContentPrincipalInfo.baseDomain().Equals(
+              contentPrincipalInfo.baseDomain())) {
+        aRv.ThrowTypeError("Response is expected from same site.");
+        return;
+      }
+    }
+  }
+}
+
 }  // namespace cache
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/cache/CacheOpParent.h
+++ b/dom/cache/CacheOpParent.h
@@ -50,16 +50,19 @@ class CacheOpParent final : public PCach
   virtual void OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
                             CacheId aOpenedCacheId,
                             const Maybe<StreamInfo>& aStreamInfo) override;
 
   // utility methods
   already_AddRefed<nsIInputStream> DeserializeCacheStream(
       const Maybe<CacheReadStream>& aMaybeStream);
 
+  void ProcessCrossOriginResourcePolicyHeader(
+      ErrorResult& aRv, const nsTArray<SavedResponse>& aResponses);
+
   mozilla::ipc::PBackgroundParent* mIpcManager;
   const CacheId mCacheId;
   const Namespace mNamespace;
   const CacheOpArgs mOpArgs;
   SafeRefPtr<cache::Manager> mManager;
   RefPtr<PrincipalVerifier> mVerifier;
 
   NS_DECL_OWNINGTHREAD
--- a/dom/cache/CacheTypes.ipdlh
+++ b/dom/cache/CacheTypes.ipdlh
@@ -15,16 +15,17 @@ using OpenMode from "mozilla/dom/cache/I
 using ReferrerPolicy from "mozilla/dom/FetchIPCTypes.h";
 using RequestCredentials from "mozilla/dom/FetchIPCTypes.h";
 using RequestMode from "mozilla/dom/FetchIPCTypes.h";
 using RequestCache from "mozilla/dom/FetchIPCTypes.h";
 using RequestRedirect from "mozilla/dom/FetchIPCTypes.h";
 using ResponseType from "mozilla/dom/FetchIPCTypes.h";
 using mozilla::void_t from "ipc/IPCMessageUtils.h";
 using struct nsID from "nsID.h";
+using nsILoadInfo::CrossOriginEmbedderPolicy from "nsILoadInfo.h";
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 struct CacheQueryParams
 {
   bool ignoreSearch;
@@ -58,16 +59,18 @@ struct CacheRequest
   ReferrerPolicy referrerPolicy;
   RequestMode mode;
   RequestCredentials credentials;
   CacheReadStream? body;
   uint32_t contentPolicyType;
   RequestCache requestCache;
   RequestRedirect requestRedirect;
   nsString integrity;
+  CrossOriginEmbedderPolicy loadingEmbedderPolicy;
+  PrincipalInfo? principalInfo;
 };
 
 struct CacheResponse
 {
   ResponseType type;
   nsCString[] urlList;
   uint32_t status;
   nsCString statusText;
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -139,16 +139,22 @@ void TypeUtils::ToCacheRequest(
   aOut.headersGuard() = headers->Guard();
   aOut.mode() = aIn.Mode();
   aOut.credentials() = aIn.GetCredentialsMode();
   aOut.contentPolicyType() = aIn.ContentPolicyType();
   aOut.requestCache() = aIn.GetCacheMode();
   aOut.requestRedirect() = aIn.GetRedirectMode();
 
   aOut.integrity() = aIn.GetIntegrity();
+  aOut.loadingEmbedderPolicy() = aIn.GetEmbedderPolicy();
+  const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
+      aIn.GetPrincipalInfo();
+  if (principalInfo) {
+    aOut.principalInfo() = Some(*(principalInfo.get()));
+  }
 
   if (aBodyAction == IgnoreBody) {
     aOut.body() = Nothing();
     return;
   }
 
   // BodyUsed flag is checked and set previously in ToInternalRequest()
 
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -70,16 +70,17 @@
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SRILogHelper.h"
 #include "mozilla/dom/ServiceWorkerBinding.h"
 #include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/Result.h"
 #include "mozilla/ResultExtensions.h"
+#include "mozilla/StaticPrefs_browser.h"
 #include "mozilla/StaticPrefs_dom.h"
 #include "mozilla/StaticPrefs_security.h"
 #include "mozilla/UniquePtr.h"
 #include "Principal.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
@@ -577,16 +578,20 @@ class ScriptResponseHeaderProcessor fina
 
   ScriptResponseHeaderProcessor(WorkerPrivate* aWorkerPrivate,
                                 bool aIsMainScript)
       : mWorkerPrivate(aWorkerPrivate), mIsMainScript(aIsMainScript) {
     AssertIsOnMainThread();
   }
 
   NS_IMETHOD OnStartRequest(nsIRequest* aRequest) override {
+    if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
+      return NS_OK;
+    }
+
     nsresult rv = ProcessCrossOriginEmbedderPolicyHeader(aRequest);
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aRequest->Cancel(rv);
     }
 
     return rv;
   }
deleted file mode 100644
--- a/testing/web-platform/meta/html/cross-origin-embedder-policy/dedicated-worker-cache-storage.https.html.ini
+++ /dev/null
@@ -1,7 +0,0 @@
-[dedicated-worker-cache-storage.https.html]
-  [coep-require-corp coep-require-corp corp-undefined]
-    expected: FAIL
-
-  [coep-none coep-require-corp corp-undefined]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/cross-origin-embedder-policy/service-worker-cache-storage.https.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[service-worker-cache-storage.https.html]
-  [A ServiceWorker with coep-require-corp use CacheStorage to get a corp-undefined response.]
-    expected: FAIL
-