Bug 1286861: Update CookieService to enforce same site cookies. r=valentin a=jcristau
authorChristoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
Sun, 08 Apr 2018 19:52:05 +0200
changeset 463186 ca8c228d8d97afdbf0577364d6a0acdd75874dc9
parent 463185 319231c9be3b788474869fa2fe2fa1f98fef7b72
child 463187 9024291f55d418c046ebc93a03eac06f4976ccc5
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersvalentin, jcristau
bugs1286861
milestone60.0
Bug 1286861: Update CookieService to enforce same site cookies. r=valentin a=jcristau
netwerk/base/nsNetUtil.cpp
netwerk/base/nsNetUtil.h
netwerk/cookie/CookieServiceChild.cpp
netwerk/cookie/CookieServiceChild.h
netwerk/cookie/CookieServiceParent.cpp
netwerk/cookie/CookieServiceParent.h
netwerk/cookie/PCookieService.ipdl
netwerk/cookie/nsCookieService.cpp
netwerk/cookie/nsCookieService.h
netwerk/test/TestCookie.cpp
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2124,16 +2124,67 @@ NS_HasBeenCrossOrigin(nsIChannel* aChann
   if (aboutBlankInherits && NS_IsAboutBlank(uri)) {
     return false;
   }
 
   return NS_FAILED(loadingPrincipal->CheckMayLoad(uri, aReport, dataInherits));
 }
 
 bool
+NS_IsSafeTopLevelNav(nsIChannel* aChannel)
+{
+  if (!aChannel) {
+    return false;
+  }
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  if (!loadInfo) {
+    return false;
+  }
+  if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) {
+    return false;
+  }
+  RefPtr<HttpBaseChannel> baseChan = do_QueryObject(aChannel);
+  if (!baseChan) {
+    return false;
+  }
+  nsHttpRequestHead *requestHead = baseChan->GetRequestHead();
+  if (!requestHead) {
+    return false;
+  }
+  return requestHead->IsSafeMethod();
+}
+
+bool NS_IsTopLevelForeign(nsIChannel* aChannel)
+{
+  if (!aChannel) {
+    return false;
+  }
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+  if (!loadInfo) {
+    return false;
+  }
+  if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) {
+    return false;
+  }
+
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+    do_GetService(THIRDPARTYUTIL_CONTRACTID);
+  if (!thirdPartyUtil) {
+    return false;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  loadInfo->TriggeringPrincipal()->GetURI(getter_AddRefs(uri));
+
+  bool isForeign = false;
+  thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
+  return isForeign;
+}
+
+bool
 NS_ShouldCheckAppCache(nsIPrincipal *aPrincipal)
 {
     uint32_t privateBrowsingId = 0;
     nsresult rv = aPrincipal->GetPrivateBrowsingId(&privateBrowsingId);
     if (NS_SUCCEEDED(rv) && (privateBrowsingId > 0)) {
         return false;
     }
 
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -679,16 +679,26 @@ bool NS_GetOriginAttributes(nsIChannel *
                             mozilla::OriginAttributes &aAttributes);
 
 /**
  * Returns true if the channel has visited any cross-origin URLs on any
  * URLs that it was redirected through.
  */
 bool NS_HasBeenCrossOrigin(nsIChannel* aChannel, bool aReport = false);
 
+/**
+ * Returns true if the channel is a safe top-level navigation.
+ */
+bool NS_IsSafeTopLevelNav(nsIChannel* aChannel);
+
+/**
+ * Returns true if the channel is a foreign navigation.
+ */
+bool NS_IsTopLevelForeign(nsIChannel* aChannel);
+
 // Constants duplicated from nsIScriptSecurityManager so we avoid having necko
 // know about script security manager.
 #define NECKO_NO_APP_ID 0
 #define NECKO_UNKNOWN_APP_ID UINT32_MAX
 
 // Unique first-party domain for separating the safebrowsing cookie.
 // Note if this value is changed, code in test_cookiejars_safebrowsing.js and
 // nsUrlClassifierHashCompleter.js should also be changed.
--- a/netwerk/cookie/CookieServiceChild.cpp
+++ b/netwerk/cookie/CookieServiceChild.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/SystemGroup.h"
 #include "nsCookie.h"
 #include "nsCookieService.h"
 #include "nsContentUtils.h"
 #include "nsNetCID.h"
+#include "nsNetUtil.h"
 #include "nsIChannel.h"
 #include "nsICookiePermission.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIURI.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsServiceManagerUtils.h"
 
@@ -126,17 +127,20 @@ CookieServiceChild::TrackCookieLoad(nsIC
   }
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   mozilla::OriginAttributes attrs;
   if (loadInfo) {
     attrs = loadInfo->GetOriginAttributes();
   }
   URIParams uriParams;
   SerializeURI(uri, uriParams);
-  SendPrepareCookieList(uriParams, isForeign, attrs);
+  bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
+  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
+  SendPrepareCookieList(uriParams, isForeign, isSafeTopLevelNav,
+                        isTopLevelForeign, attrs);
 }
 
 mozilla::ipc::IPCResult
 CookieServiceChild::RecvRemoveAll(){
   mCookiesMap.Clear();
   return IPC_OK();
 }
 
@@ -251,16 +255,18 @@ CookieServiceChild::PrefChanged(nsIPrefB
     mThirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
     NS_ASSERTION(mThirdPartyUtil, "require ThirdPartyUtil service");
   }
 }
 
 void
 CookieServiceChild::GetCookieStringFromCookieHashTable(nsIURI                 *aHostURI,
                                                        bool                   aIsForeign,
+                                                       bool                   aIsSafeTopLevelNav,
+                                                       bool                   aIsTopLevelForeign,
                                                        const OriginAttributes &aOriginAttrs,
                                                        nsCString              &aCookieString)
 {
   nsCOMPtr<nsIEffectiveTLDService> TLDService =
     do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   NS_ASSERTION(TLDService, "Can't get TLDService");
   bool requireHostMatch;
   nsAutoCString baseDomain;
@@ -301,16 +307,31 @@ CookieServiceChild::GetCookieStringFromC
     // check the host, since the base domain lookup is conservative.
     if (!nsCookieService::DomainMatches(cookie, hostFromURI))
       continue;
 
     // if the cookie is secure and the host scheme isn't, we can't send it
     if (cookie->IsSecure() && !isSecure)
       continue;
 
+    int32_t sameSiteAttr = 0;
+    cookie->GetSameSite(&sameSiteAttr);
+    if (aIsForeign || aIsTopLevelForeign) {
+      // it if's a cross origin request and the cookie is same site only (strict)
+      // don't send it
+      if (sameSiteAttr == nsICookie2::SAMESITE_STRICT) {
+        continue;
+      }
+      // if it's a cross origin request, the cookie is same site lax, but it's not
+      // a top-level navigation, don't send it
+      if (sameSiteAttr == nsICookie2::SAMESITE_LAX && !aIsSafeTopLevelNav) {
+        continue;
+      }
+    }
+
     // if the nsIURI path doesn't match the cookie path, don't send it back
     if (!nsCookieService::PathMatches(cookie, pathFromURI))
       continue;
 
     // check if the cookie has expired
     if (cookie->Expiry() <= currentTime) {
       continue;
     }
@@ -328,23 +349,25 @@ CookieServiceChild::GetCookieStringFromC
       }
     }
   }
 }
 
 void
 CookieServiceChild::GetCookieStringSyncIPC(nsIURI                 *aHostURI,
                                            bool                   aIsForeign,
+                                           bool                   aIsSafeTopLevelNav,
+                                           bool                   aIsTopLevelForeign,
                                            const OriginAttributes &aAttrs,
                                            nsAutoCString          &aCookieString)
 {
   URIParams uriParams;
   SerializeURI(aHostURI, uriParams);
 
-  SendGetCookieString(uriParams, aIsForeign, aAttrs, &aCookieString);
+  SendGetCookieString(uriParams, aIsForeign, aIsSafeTopLevelNav, aIsTopLevelForeign, aAttrs, &aCookieString);
 }
 
 uint32_t
 CookieServiceChild::CountCookiesFromHashTable(const nsCString &aBaseDomain,
                                               const OriginAttributes &aOriginAttrs)
 {
   CookiesList *cookiesList = nullptr;
 
@@ -407,16 +430,17 @@ CookieServiceChild::RecordDocumentCookie
   for (uint32_t i = 0; i < cookiesList->Length(); i++) {
     nsCookie *cookie = cookiesList->ElementAt(i);
     if (cookie->Name().Equals(aCookie->Name()) &&
         cookie->Host().Equals(aCookie->Host()) &&
         cookie->Path().Equals(aCookie->Path())) {
       if (cookie->Value().Equals(aCookie->Value()) &&
           cookie->Expiry() == aCookie->Expiry() &&
           cookie->IsSecure() == aCookie->IsSecure() &&
+          cookie->SameSite() == aCookie->SameSite() &&
           cookie->IsSession() == aCookie->IsSession() &&
           cookie->IsHttpOnly() == aCookie->IsHttpOnly()) {
         cookie->SetLastAccessed(aCookie->LastAccessed());
         return;
       }
       cookiesList->RemoveElementAt(i);
       break;
     }
@@ -456,24 +480,30 @@ CookieServiceChild::GetCookieStringInter
       attrs = loadInfo->GetOriginAttributes();
     }
   }
 
   // Asynchronously call the parent.
   bool isForeign = true;
   if (RequireThirdPartyCheck())
     mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
+
+  bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
+  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
+
   nsAutoCString result;
   if (!mIPCSync) {
-    GetCookieStringFromCookieHashTable(aHostURI, !!isForeign, attrs, result);
+    GetCookieStringFromCookieHashTable(aHostURI, !!isForeign, isSafeTopLevelNav,
+                                       isTopLevelForeign, attrs, result);
   } else {
     if (!mIPCOpen) {
       return NS_ERROR_NOT_AVAILABLE;
     }
-    GetCookieStringSyncIPC(aHostURI, !!isForeign, attrs, result);
+    GetCookieStringSyncIPC(aHostURI, !!isForeign, isSafeTopLevelNav,
+                           isTopLevelForeign, attrs, result);
   }
 
   if (!result.IsEmpty())
     *aCookieString = ToNewCString(result);
 
   return NS_OK;
 }
 
--- a/netwerk/cookie/CookieServiceChild.h
+++ b/netwerk/cookie/CookieServiceChild.h
@@ -56,22 +56,26 @@ protected:
                      nsCString &aOriginatingCharset);
 
   nsresult GetCookieStringInternal(nsIURI *aHostURI,
                                    nsIChannel *aChannel,
                                    char **aCookieString);
 
   void GetCookieStringFromCookieHashTable(nsIURI *aHostURI,
                                           bool aIsForeign,
+                                          bool aIsSafeTopLevelNav,
+                                          bool aIsTopLevelForeign,
                                           const OriginAttributes &aAttrs,
                                           nsCString &aCookieString);
 
   void
   GetCookieStringSyncIPC(nsIURI                 *aHostURI,
                          bool                    aIsForeign,
+                         bool                    aIsSafeTopLevelNav,
+                         bool                    aIsTopLevelForeign,
                          const OriginAttributes &aAttrs,
                          nsAutoCString          &aCookieString);
 
   nsresult SetCookieStringInternal(nsIURI *aHostURI,
                                    nsIChannel *aChannel,
                                    const char *aCookieString,
                                    const char *aServerTime,
                                    bool aFromHttp);
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -150,25 +150,27 @@ CookieServiceParent::TrackCookieLoad(nsI
   nsCOMPtr<nsIURI> uri;
   aChannel->GetURI(getter_AddRefs(uri));
 
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   mozilla::OriginAttributes attrs;
   if (loadInfo) {
     attrs = loadInfo->GetOriginAttributes();
   }
+  bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
+  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
 
   // Send matching cookies to Child.
   nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil;
   thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
   bool isForeign = true;
   thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign);
   nsTArray<nsCookie*> foundCookieList;
-  mCookieService->GetCookiesForURI(uri, isForeign, false,
-                                   attrs, foundCookieList);
+  mCookieService->GetCookiesForURI(uri, isForeign, isSafeTopLevelNav, isTopLevelForeign,
+                                   false, attrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, uri);
   Unused << SendTrackCookiesLoad(matchingCookiesList, attrs);
 }
 
 void
 CookieServiceParent::SerialializeCookieList(const nsTArray<nsCookie*> &aFoundCookieList,
                                             nsTArray<CookieStruct>    &aCookiesList,
@@ -188,53 +190,57 @@ CookieServiceParent::SerialializeCookieL
     cookieStruct->isSecure() = cookie->IsSecure();
     cookieStruct->sameSite() = cookie->SameSite();
   }
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvPrepareCookieList(const URIParams        &aHost,
                                            const bool             &aIsForeign,
+                                           const bool             &aIsSafeTopLevelNav,
+                                           const bool             &aIsTopLevelForeign,
                                            const OriginAttributes &aAttrs)
 {
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
 
   // Send matching cookies to Child.
   nsTArray<nsCookie*> foundCookieList;
-  mCookieService->GetCookiesForURI(hostURI, aIsForeign, false,
-                                   aAttrs, foundCookieList);
+  mCookieService->GetCookiesForURI(hostURI, aIsForeign, aIsSafeTopLevelNav, aIsTopLevelForeign,
+                                   false, aAttrs, foundCookieList);
   nsTArray<CookieStruct> matchingCookiesList;
   SerialializeCookieList(foundCookieList, matchingCookiesList, hostURI);
   Unused << SendTrackCookiesLoad(matchingCookiesList, aAttrs);
   return IPC_OK();
 }
 
 void
 CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // Nothing needed here. Called right before destructor since this is a
   // non-refcounted class.
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvGetCookieString(const URIParams& aHost,
                                          const bool& aIsForeign,
+                                         const bool& aIsSafeTopLevelNav,
+                                         const bool& aIsToplevelForeign,
                                          const OriginAttributes& aAttrs,
                                          nsCString* aResult)
 {
   if (!mCookieService)
     return IPC_OK();
 
   // Deserialize URI. Having a host URI is mandatory and should always be
   // provided by the child; thus we consider failure fatal.
   nsCOMPtr<nsIURI> hostURI = DeserializeURI(aHost);
   if (!hostURI)
     return IPC_FAIL_NO_REASON(this);
-
-  mCookieService->GetCookieStringInternal(hostURI, aIsForeign, false, aAttrs, *aResult);
+  mCookieService->GetCookieStringInternal(hostURI, aIsForeign, aIsSafeTopLevelNav, aIsToplevelForeign,
+                                          false, aAttrs, *aResult);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CookieServiceParent::RecvSetCookieString(const URIParams& aHost,
                                          const bool& aIsForeign,
                                          const nsCString& aCookieString,
                                          const nsCString& aServerTime,
--- a/netwerk/cookie/CookieServiceParent.h
+++ b/netwerk/cookie/CookieServiceParent.h
@@ -32,28 +32,32 @@ public:
 
   void AddCookie(nsICookie *aCookie);
 
 protected:
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual mozilla::ipc::IPCResult RecvGetCookieString(const URIParams& aHost,
                                                       const bool& aIsForeign,
+                                                      const bool& aIsSafeTopLevelNav,
+                                                      const bool& aIsTopLevelForeign,
                                                       const OriginAttributes& aAttrs,
                                                       nsCString* aResult) override;
 
   virtual mozilla::ipc::IPCResult RecvSetCookieString(const URIParams& aHost,
                                                       const bool& aIsForeign,
                                                       const nsCString& aCookieString,
                                                       const nsCString& aServerTime,
                                                       const OriginAttributes& aAttrs,
                                                       const bool& aFromHttp) override;
   virtual
   mozilla::ipc::IPCResult RecvPrepareCookieList(const URIParams &aHost,
                                                 const bool &aIsForeign,
+                                                const bool &aIsSafeTopLevelNav,
+                                                const bool &aIsTopLevelForeign,
                                                 const OriginAttributes &aAttrs) override;
 
   void
   SerialializeCookieList(const nsTArray<nsCookie*> &aFoundCookieList,
                          nsTArray<CookieStruct> &aCookiesList,
                          nsIURI *aHostURI);
 
   RefPtr<nsCookieService> mCookieService;
--- a/netwerk/cookie/PCookieService.ipdl
+++ b/netwerk/cookie/PCookieService.ipdl
@@ -57,16 +57,18 @@ parent:
    * @see nsICookieService.getCookieString
    * @see nsICookieService.getCookieStringFromHttp
    * @see mozIThirdPartyUtil.isThirdPartyChannel
    *
    * @return the resulting cookie string.
    */
   nested(inside_cpow) sync GetCookieString(URIParams host,
                                            bool isForeign,
+                                           bool isSafeTopLevelNav,
+                                           bool isTopLevelForeign,
                                            OriginAttributes attrs)
        returns (nsCString result);
 
   /*
    * Set a cookie string.
    *
    * @param host
    *        Same as the 'aURI' argument to nsICookieService.setCookieString.
@@ -98,16 +100,18 @@ parent:
                                             bool isForeign,
                                             nsCString cookieString,
                                             nsCString serverTime,
                                             OriginAttributes attrs,
                                             bool aFromHttp);
 
   async PrepareCookieList(URIParams host,
                           bool isForeign,
+                          bool isSafeTopLevelNav,
+                          bool isTopLevelForeign,
                           OriginAttributes attrs);
 
   async __delete__();
 
 child:
   async TrackCookiesLoad(CookieStruct[] cookiesList,
                          OriginAttributes attrs);
 
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -2035,18 +2035,21 @@ nsCookieService::GetCookieStringCommon(n
   mThirdPartyUtil->IsThirdPartyChannel(aChannel, aHostURI, &isForeign);
 
   // Get originAttributes.
   OriginAttributes attrs;
   if (aChannel) {
     NS_GetOriginAttributes(aChannel, attrs);
   }
 
+  bool isSafeTopLevelNav = NS_IsSafeTopLevelNav(aChannel);
+  bool isTopLevelForeign = NS_IsTopLevelForeign(aChannel);
   nsAutoCString result;
-  GetCookieStringInternal(aHostURI, isForeign, aHttpBound, attrs, result);
+  GetCookieStringInternal(aHostURI, isForeign, isSafeTopLevelNav, isTopLevelForeign,
+                          aHttpBound, attrs, result);
   *aCookie = result.IsEmpty() ? nullptr : ToNewCString(result);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCookieService::SetCookieString(nsIURI     *aHostURI,
                                  nsIPrompt  *aPrompt,
                                  const char *aCookieHeader,
@@ -3103,16 +3106,18 @@ nsCookieService::PathMatches(nsCookie* a
   // either the paths match exactly, or the cookie path is a prefix of
   // the given path.
   return true;
 }
 
 void
 nsCookieService::GetCookiesForURI(nsIURI *aHostURI,
                                   bool aIsForeign,
+                                  bool aIsSafeTopLevelNav,
+                                  bool aIsTopLevelForeign,
                                   bool aHttpBound,
                                   const OriginAttributes& aOriginAttrs,
                                   nsTArray<nsCookie*>& aCookieList)
 {
   NS_ASSERTION(aHostURI, "null host!");
 
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
@@ -3192,16 +3197,31 @@ nsCookieService::GetCookiesForURI(nsIURI
     // check the host, since the base domain lookup is conservative.
     if (!DomainMatches(cookie, hostFromURI))
       continue;
 
     // if the cookie is secure and the host scheme isn't, we can't send it
     if (cookie->IsSecure() && !isSecure)
       continue;
 
+    int32_t sameSiteAttr = 0;
+    cookie->GetSameSite(&sameSiteAttr);
+    if (aIsForeign || aIsTopLevelForeign) {
+      // it if's a cross origin request and the cookie is same site only (strict)
+      // don't send it
+      if (sameSiteAttr == nsICookie2::SAMESITE_STRICT) {
+        continue;
+      }
+      // if it's a cross origin request, the cookie is same site lax, but it's not
+      // a top-level navigation, don't send it
+      if (sameSiteAttr == nsICookie2::SAMESITE_LAX && !aIsSafeTopLevelNav) {
+        continue;
+      }
+    }
+
     // if the cookie is httpOnly and it's not going directly to the HTTP
     // connection, don't send it
     if (cookie->IsHttpOnly() && !aHttpBound)
       continue;
 
     // if the nsIURI path doesn't match the cookie path, don't send it back
     if (!PathMatches(cookie, pathFromURI))
       continue;
@@ -3259,22 +3279,25 @@ nsCookieService::GetCookiesForURI(nsIURI
   // this is required per RFC2109.  if cookies match in length,
   // then sort by creation time (see bug 236772).
   aCookieList.Sort(CompareCookiesForSending());
 }
 
 void
 nsCookieService::GetCookieStringInternal(nsIURI *aHostURI,
                                          bool aIsForeign,
+                                         bool aIsSafeTopLevelNav,
+                                         bool aIsTopLevelForeign,
                                          bool aHttpBound,
                                          const OriginAttributes& aOriginAttrs,
                                          nsCString &aCookieString)
 {
   AutoTArray<nsCookie*, 8> foundCookieList;
-  GetCookiesForURI(aHostURI, aIsForeign, aHttpBound, aOriginAttrs, foundCookieList);
+  GetCookiesForURI(aHostURI, aIsForeign, aIsSafeTopLevelNav, aIsTopLevelForeign,
+                   aHttpBound, aOriginAttrs, foundCookieList);
 
   nsCookie* cookie;
   for (uint32_t i = 0; i < foundCookieList.Length(); ++i) {
     cookie = foundCookieList.ElementAt(i);
 
     // check if we have anything to write
     if (!cookie->Name().IsEmpty() || !cookie->Value().IsEmpty()) {
       // if we've already added a cookie to the return list, append a "; " so
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -263,17 +263,17 @@ class nsCookieService final : public nsI
   static nsCString GetPathFromURI(nsIURI *aHostURI);
   static nsresult GetBaseDomain(nsIEffectiveTLDService *aTLDService, nsIURI *aHostURI, nsCString &aBaseDomain, bool &aRequireHostMatch);
   static nsresult GetBaseDomainFromHost(nsIEffectiveTLDService *aTLDService, const nsACString &aHost, nsCString &aBaseDomain);
   static bool DomainMatches(nsCookie* aCookie, const nsACString& aHost);
   static bool PathMatches(nsCookie* aCookie, const nsACString& aPath);
   static bool CanSetCookie(nsIURI *aHostURI, const nsCookieKey& aKey, nsCookieAttributes &aCookieAttributes, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, nsIChannel* aChannel, bool aLeaveSercureAlone, bool &aSetCookie, mozIThirdPartyUtil* aThirdPartyUtil);
   static CookieStatus CheckPrefs(nsICookiePermission *aPermissionServices, uint8_t aCookieBehavior, bool aThirdPartySession, bool aThirdPartyNonsecureSession, nsIURI *aHostURI, bool aIsForeign, const char *aCookieHeader, const int aNumOfCookies, const OriginAttributes& aOriginAttrs);
   static int64_t ParseServerTime(const nsCString &aServerTime);
-  void GetCookiesForURI(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsTArray<nsCookie*>& aCookieList);
+  void GetCookiesForURI(nsIURI *aHostURI, bool aIsForeign, bool aIsSafeTopLevelNav, bool aIsTopLevelForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsTArray<nsCookie*>& aCookieList);
 
   protected:
     virtual ~nsCookieService();
 
     void                          PrefChanged(nsIPrefBranch *aPrefBranch);
     void                          InitDBStates();
     OpenDBResult                  TryInitDB(bool aDeleteExistingDB);
     void                          InitDBConn();
@@ -289,17 +289,17 @@ class nsCookieService final : public nsI
     void                          HandleDBClosed(DBState* aDBState);
     void                          HandleCorruptDB(DBState* aDBState);
     void                          RebuildCorruptDB(DBState* aDBState);
     OpenDBResult                  Read();
     mozilla::UniquePtr<ConstCookie> GetCookieFromRow(mozIStorageStatement *aRow, const OriginAttributes &aOriginAttributes);
     void                          EnsureReadComplete(bool aInitDBConn);
     nsresult                      NormalizeHost(nsCString &aHost);
     nsresult                      GetCookieStringCommon(nsIURI *aHostURI, nsIChannel *aChannel, bool aHttpBound, char** aCookie);
-    void                          GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsCString &aCookie);
+    void                          GetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, bool aIsSafeTopLevelNav, bool aIsTopLevelForeign, bool aHttpBound, const OriginAttributes& aOriginAttrs, nsCString &aCookie);
     nsresult                      SetCookieStringCommon(nsIURI *aHostURI, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, bool aFromHttp);
     void                          SetCookieStringInternal(nsIURI *aHostURI, bool aIsForeign, nsDependentCString &aCookieHeader, const nsCString &aServerTime, bool aFromHttp, bool aFromChild, const OriginAttributes &aOriginAttrs, nsIChannel* aChannel);
     bool                          SetCookieInternal(nsIURI *aHostURI, const nsCookieKey& aKey, bool aRequireHostMatch, CookieStatus aStatus, nsDependentCString &aCookieHeader, int64_t aServerTime, bool aFromHttp, bool aFromChild, nsIChannel* aChannel);
     void                          AddInternal(const nsCookieKey& aKey, nsCookie *aCookie, int64_t aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, bool aFromHttp, bool aFromChild = false);
     void                          RemoveCookieFromList(const nsListIter &aIter, mozIStorageBindingParamsArray *aParamsArray = nullptr);
     void                          AddCookieToList(const nsCookieKey& aKey, nsCookie *aCookie, DBState *aDBState, mozIStorageBindingParamsArray *aParamsArray, bool aWriteToDB = true);
     void                          UpdateCookieInList(nsCookie *aCookie, int64_t aLastAccessed, mozIStorageBindingParamsArray *aParamsArray);
     static bool                   GetTokenValue(nsACString::const_char_iterator &aIter, nsACString::const_char_iterator &aEndIter, nsDependentCSubstring &aTokenString, nsDependentCSubstring &aTokenValue, bool &aEqualsFound);
--- a/netwerk/test/TestCookie.cpp
+++ b/netwerk/test/TestCookie.cpp
@@ -813,13 +813,28 @@ TEST(TestCookie,TestCookieMain)
         EXPECT_TRUE(sameSiteAttr == nsICookie2::SAMESITE_STRICT);
       } else if (name.EqualsLiteral("lax")) {
         EXPECT_TRUE(sameSiteAttr == nsICookie2::SAMESITE_LAX);
       }
     }
 
     EXPECT_TRUE(i == 6);
 
+    // *** SameSite attribute
+    // Clear the cookies
+    EXPECT_TRUE(NS_SUCCEEDED(cookieMgr->RemoveAll()));
+
+    // please note that the flag aForeign is always set to true using this test setup because no nsIChannel is
+    // passed to SetCookieString(). therefore we can only test that no cookies are sent for cross origin requests
+    // using same-site cookies.
+    SetACookie(cookieService, "http://www.samesite.com", nullptr, "test=sameSiteStrictVal; samesite=strict", nullptr);
+    GetACookie(cookieService, "http://www.notsamesite.com", nullptr, cookie);
+    EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
+    SetACookie(cookieService, "http://www.samesite.test", nullptr, "test=sameSiteLaxVal; samesite=lax", nullptr);
+    GetACookie(cookieService, "http://www.notsamesite.com", nullptr, cookie);
+    EXPECT_TRUE(CheckResult(cookie.get(), MUST_BE_NULL));
+
     // XXX the following are placeholders: add these tests please!
     // *** "noncompliant cookie" tests
     // *** IP address tests
     // *** speed tests
 }