Bug 1591932 - Enable Sniffing on No Mime+ XCTO nosniff r=ckerschb default tip
authorSebastian Streich <sstreich@mozilla.com>
Wed, 13 Nov 2019 12:12:34 +0000
changeset 501734 35436d4e7917bf9d9b96a6173201ca001a8ff7bc
parent 501733 7a55cb6e7aecdd0d3fb786fefe8cc165558b78e0
push id36799
push userbtara@mozilla.com
push dateWed, 13 Nov 2019 21:50:41 +0000
treeherdermozilla-central@35436d4e7917 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb
bugs1591932
milestone72.0a1
first release with
nightly linux32
35436d4e7917 / 72.0a1 / 20191113215041 / files
nightly linux64
35436d4e7917 / 72.0a1 / 20191113215041 / files
nightly mac
35436d4e7917 / 72.0a1 / 20191113215041 / files
nightly win32
35436d4e7917 / 72.0a1 / 20191113215041 / files
nightly win64
35436d4e7917 / 72.0a1 / 20191113215041 / 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 1591932 - Enable Sniffing on No Mime+ XCTO nosniff r=ckerschb Differential Revision: https://phabricator.services.mozilla.com/D50816
dom/security/test/general/window_nosniff_navigation.html
image/imgLoader.cpp
netwerk/base/nsNetUtil.cpp
netwerk/streamconv/converters/nsUnknownDecoder.cpp
toolkit/components/mediasniffer/nsMediaSniffer.cpp
--- a/dom/security/test/general/window_nosniff_navigation.html
+++ b/dom/security/test/general/window_nosniff_navigation.html
@@ -49,45 +49,61 @@
  * Navigations.
  * If Firefox cant Display the Page, it will prompt a download 
  * and the URL of the Page will be about:blank.
  * So we will try to open different content send with
  * no-mime, mismatched-mime and garbage-mime types.
  * 
  */
 
+
+/*
+  What the Sniffer Should sniff, given a querystring for file_nosniff_navigation.sjs
+*/
+const EXPECTED_MIMES={
+  "xml": "text/xml",
+  "html": "text/html",
+  "img": "image/png",
+  "css": "text/plain",
+  "js": "text/plain",
+  "json": "text/plain", 
+  "": "text/plain"
+}
+
 SimpleTest.waitForExplicitFinish();
 
 window.addEventListener("load", ()=>{
   let noMimeFrames = Array.from(document.querySelectorAll(".no-mime"));
 
   noMimeFrames.forEach( frame => {
-    // In case of no Provided Content Type, not rendering or assuming text/plain is valid
-    let result = frame.contentWindow.document.URL == "about:blank" || frame.contentWindow.document.contentType == "text/plain";
-    let sniffTarget = (new URL(frame.src)).search;
-    window.opener.ok(result, `${sniffTarget} without MIME - was not Sniffed`);
+    // In case of no Provided Content Type + XTCO set, we still should do sniffing
+    let sniffedMimeType = frame.contentWindow.document.contentType;
+    let contentTypeQuery = (new URL(frame.src)).search.substr(1);
+    let expectedMime = EXPECTED_MIMES[contentTypeQuery];
+    let result = expectedMime == sniffedMimeType;
+    window.opener.ok(result, `${contentTypeQuery} without MIME send -> was Sniffed: ${frame.contentWindow.document.contentType}`);
   });
 
   let mismatchedMimes = Array.from(document.querySelectorAll(".mismatch-mime"));
   mismatchedMimes.forEach(frame => {
     // In case the Server mismatches the Mime Type (sends content X as image/png)
     // assert that we do not sniff and correct this.
     let result = frame.contentWindow.document.contentType == "image/png";
-    let sniffTarget = (new URL(frame.src)).search;
+    let sniffTarget = (new URL(frame.src)).search.substr(1);
     window.opener.ok(result, `${sniffTarget} send as image/png - was not Sniffed`);
   });
 
   let badMimeFrames = Array.from(document.querySelectorAll(".garbage-mime"));
 
   badMimeFrames.forEach( frame => {
     // In the case we got a bogous mime, assert that we dont sniff. 
     // We must not default here to text/plain
     // as the Server at least provided a mime type. 
     let result = frame.contentWindow.document.URL == "about:blank";
-    let sniffTarget = (new URL(frame.src)).search;
+    let sniffTarget = (new URL(frame.src)).search.substr(1);;
     window.opener.ok(result, `${sniffTarget} send as garbage/garbage - was not Sniffed`);
   });
   
   window.opener.SimpleTest.finish();
   this.close();
 });
 </script>
 </body>
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -2565,23 +2565,16 @@ bool imgLoader::SupportImageWithMimeType
   DecoderType type = DecoderFactory::GetDecoderType(mimeType.get());
   return type != DecoderType::UNKNOWN;
 }
 
 NS_IMETHODIMP
 imgLoader::GetMIMETypeFromContent(nsIRequest* aRequest,
                                   const uint8_t* aContents, uint32_t aLength,
                                   nsACString& aContentType) {
-  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
-  if (channel) {
-    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-    if (loadInfo->GetSkipContentSniffing()) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-  }
   return GetMimeTypeFromContent((const char*)aContents, aLength, aContentType);
 }
 
 /* static */
 nsresult imgLoader::GetMimeTypeFromContent(const char* aContents,
                                            uint32_t aLength,
                                            nsACString& aContentType) {
   /* Is it a GIF? */
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2721,20 +2721,25 @@ void NS_SniffContent(const char* aSniffe
   if (channel) {
     nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
     if (loadInfo->GetSkipContentSniffing()) {
       /* Bug 1571742
        * We cannot skip snffing if the current MIME-Type might be a JSON.
        * The JSON-Viewer relies on its own sniffer to determine, if it can
        * render the page, so we need to make an exception if the Server provides
        * a application/ mime, as it might be json.
+
+       * Bug 1594766
+       * We also dont't skip sniffing if the currentContentType is empty
+       * because of legacy page compatibility issues.
        */
       nsAutoCString currentContentType;
       channel->GetContentType(currentContentType);
-      if (!StringBeginsWith(currentContentType,
+      if (!currentContentType.IsEmpty() &&
+          !StringBeginsWith(currentContentType,
                             NS_LITERAL_CSTRING("application/"))) {
         return;
       }
     }
   }
   nsCOMArray<nsIContentSniffer> sniffers;
   cache->GetEntries(sniffers);
   for (int32_t i = 0; i < sniffers.Count(); ++i) {
--- a/netwerk/streamconv/converters/nsUnknownDecoder.cpp
+++ b/netwerk/streamconv/converters/nsUnknownDecoder.cpp
@@ -321,24 +321,16 @@ nsUnknownDecoder::OnStopRequest(nsIReque
 //
 // ----
 NS_IMETHODIMP
 nsUnknownDecoder::GetMIMETypeFromContent(nsIRequest* aRequest,
                                          const uint8_t* aData, uint32_t aLength,
                                          nsACString& type) {
   // This is only used by sniffer, therefore we do not need to lock anything
   // here.
-  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
-  if (channel) {
-    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-    if (loadInfo->GetSkipContentSniffing()) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-  }
-
   mBuffer = const_cast<char*>(reinterpret_cast<const char*>(aData));
   mBufferLen = aLength;
   DetermineContentType(aRequest);
   mBuffer = nullptr;
   mBufferLen = 0;
   type.Assign(mContentType);
   mContentType.Truncate();
   return type.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
@@ -357,21 +349,16 @@ bool nsUnknownDecoder::AllowSniffing(nsI
     return false;
   }
 
   nsCOMPtr<nsIURI> uri;
   if (NS_FAILED(channel->GetURI(getter_AddRefs(uri))) || !uri) {
     return false;
   }
 
-  nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-  if (loadInfo->GetSkipContentSniffing()) {
-    return false;
-  }
-
   return !uri->SchemeIs("file");
 }
 
 /**
  * This is the array of sniffer entries that depend on "magic numbers"
  * in the file.  Each entry has either a type associated with it (set
  * these with the SNIFFER_ENTRY macro) or a function to be executed
  * (set these with the SNIFFER_ENTRY_WITH_FUNC macro).  The function
@@ -403,53 +390,21 @@ uint32_t nsUnknownDecoder::sSnifferEntry
 
 void nsUnknownDecoder::DetermineContentType(nsIRequest* aRequest) {
   {
     MutexAutoLock lock(mMutex);
     NS_ASSERTION(mContentType.IsEmpty(), "Content type is already known.");
     if (!mContentType.IsEmpty()) return;
   }
 
-  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
-  if (channel) {
-    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-    if (loadInfo->GetSkipContentSniffing()) {
-      /*
-       * If we did not get a useful Content-Type from the server
-       * but also have sniffing disabled, just determine whether
-       * to use text/plain or octetstream and log an error to the Console
-       */
-      LastDitchSniff(aRequest);
-
-      nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
-      if (httpChannel) {
-        nsAutoCString type;
-        httpChannel->GetContentType(type);
-        nsCOMPtr<nsIURI> requestUri;
-        httpChannel->GetURI(getter_AddRefs(requestUri));
-        nsAutoCString spec;
-        requestUri->GetSpec(spec);
-        if (spec.Length() > 50) {
-          spec.Truncate(50);
-          spec.AppendLiteral("...");
-        }
-        httpChannel->LogMimeTypeMismatch(
-            NS_LITERAL_CSTRING("XTCOWithMIMEValueMissing"), false,
-            NS_ConvertUTF8toUTF16(spec),
-            // Type is not used in the Error Message but required
-            NS_ConvertUTF8toUTF16(type));
-      }
-      return;
-    }
-  }
-
   const char* testData = mBuffer;
   uint32_t testDataLen = mBufferLen;
   // Check if data are compressed.
   nsAutoCString decodedData;
+  nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
 
   if (channel) {
     // ConvertEncodedData is always called only on a single thread for each
     // instance of an object.
     nsresult rv = ConvertEncodedData(aRequest, mBuffer, mBufferLen);
     if (NS_SUCCEEDED(rv)) {
       MutexAutoLock lock(mMutex);
       decodedData = mDecodedData;
@@ -611,19 +566,16 @@ bool nsUnknownDecoder::SniffForXML(nsIRe
   }
 
   return true;
 }
 
 bool nsUnknownDecoder::SniffURI(nsIRequest* aRequest) {
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-  if (loadInfo->GetSkipContentSniffing()) {
-    return false;
-  }
   nsCOMPtr<nsIMIMEService> mimeService(do_GetService("@mozilla.org/mime;1"));
   if (mimeService) {
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     if (channel) {
       nsCOMPtr<nsIURI> uri;
       nsresult result = channel->GetURI(getter_AddRefs(uri));
       if (NS_SUCCEEDED(result) && uri) {
         nsAutoCString type;
@@ -867,20 +819,16 @@ nsUnknownDecoder::CheckListenerChain() {
 
 void nsBinaryDetector::DetermineContentType(nsIRequest* aRequest) {
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   if (!httpChannel) {
     return;
   }
 
   nsCOMPtr<nsILoadInfo> loadInfo = httpChannel->LoadInfo();
-  if (loadInfo->GetSkipContentSniffing()) {
-    LastDitchSniff(aRequest);
-    return;
-  }
   // It's an HTTP channel.  Check for the text/plain mess
   nsAutoCString contentTypeHdr;
   Unused << httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Type"),
                                            contentTypeHdr);
   nsAutoCString contentType;
   httpChannel->GetContentType(contentType);
 
   // Make sure to do a case-sensitive exact match comparison here.  Apache
--- a/toolkit/components/mediasniffer/nsMediaSniffer.cpp
+++ b/toolkit/components/mediasniffer/nsMediaSniffer.cpp
@@ -135,20 +135,16 @@ static bool MatchesADTS(const uint8_t* a
 
 NS_IMETHODIMP
 nsMediaSniffer::GetMIMETypeFromContent(nsIRequest* aRequest,
                                        const uint8_t* aData,
                                        const uint32_t aLength,
                                        nsACString& aSniffedType) {
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   if (channel) {
-    nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
-    if (loadInfo->GetSkipContentSniffing()) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
     nsLoadFlags loadFlags = 0;
     channel->GetLoadFlags(&loadFlags);
     if (!(loadFlags & nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE)) {
       // For media, we want to sniff only if the Content-Type is unknown, or if
       // it is application/octet-stream.
       nsAutoCString contentType;
       nsresult rv = channel->GetContentType(contentType);
       NS_ENSURE_SUCCESS(rv, rv);