bug 761667 - update x-frame-options channel scanning to include multipart channels. (r=bz)
authorSid Stamm <sstamm@mozilla.com>
Mon, 22 Apr 2013 16:01:39 -0700
changeset 140605 e5f8c65894da148f3ef95e22a57f8a7ab510ed88
parent 140604 ef755a443f3c74a838ac5d18e6544c0ce4600154
child 140606 f27428a02d7b4f65d29f81e0e5f736f37d96d169
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs761667
milestone23.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 761667 - update x-frame-options channel scanning to include multipart channels. (r=bz)
content/base/test/file_x-frame-options_main.html
content/base/test/file_x-frame-options_page.sjs
content/base/test/test_x-frame-options.html
docshell/base/nsDSURIContentListener.cpp
docshell/base/nsDSURIContentListener.h
--- a/content/base/test/file_x-frame-options_main.html
+++ b/content/base/test/file_x-frame-options_main.html
@@ -16,16 +16,18 @@ window.addEventListener('load', parent.t
 <iframe id="sameorigin2" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin2&xfo=sameorigin"></iframe><br>
 <iframe id="sameorigin5" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin5&xfo=sameorigin2"></iframe><br>
 <iframe id="sameorigin6" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin6&xfo=sameorigin2"></iframe><br>
 <iframe id="sameorigin7" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin7&xfo=sameorigin3"></iframe><br>
 <iframe id="sameorigin8" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin8&xfo=sameorigin3"></iframe><br>
 <iframe id="mixedpolicy" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=mixedpolicy&xfo=mixedpolicy"></iframe><br>
 <iframe id="allow-from-allow" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=allow-from-allow&xfo=afa"></iframe><br>
 <iframe id="allow-from-deny" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=allow-from-deny&xfo=afd"></iframe><br>
+<iframe id="sameorigin-multipart" src="http://example.com/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin-multipart&xfo=sameorigin&multipart=1"></iframe><br>
+<iframe id="sameorigin-multipart2" src="http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs?testid=sameorigin-multipart2&xfo=sameorigin&multipart=1"></iframe><br>
 
 <!-- added for bug 836132 -->
 <script type="text/javascript">
 
   function addFrame(test, xfo) {
     var baseurl = "http://mochi.test:8888/tests/content/base/test/file_x-frame-options_page.sjs";
     var ifr = "<iframe id='" + test + "'" + "src='" + baseurl + "?testid=" + test + "&xfo=" + xfo + "' style='border:2px solid red;'></iframe>";
     document.write(ifr);
--- a/content/base/test/file_x-frame-options_page.sjs
+++ b/content/base/test/file_x-frame-options_page.sjs
@@ -1,19 +1,28 @@
 // SJS file for X-Frame-Options mochitests
 function handleRequest(request, response)
 {
   var query = {};
+  var BOUNDARY = "BOUNDARYOMG3984";
   request.queryString.split('&').forEach(function (val) {
     var [name, value] = val.split('=');
     query[name] = unescape(value);
   });
 
-  response.setHeader("Cache-Control", "no-cache", false);
-  response.setHeader("Content-Type", "text/html", false);
+  if (query['multipart'] == "1") {
+    response.setHeader("Content-Type", "multipart/x-mixed-replace;boundary=" + BOUNDARY, false);
+    response.setHeader("Cache-Control", "no-cache", false);
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.write("--" + BOUNDARY + "\r\n");
+    response.write("Content-Type: text/html\r\n\r\n");
+  } else {
+    response.setHeader("Content-Type", "text/html", false);
+    response.setHeader("Cache-Control", "no-cache", false);
+  }
 
   var testHeaders = {
     "deny": "DENY",
     "sameorigin": "SAMEORIGIN",
     "sameorigin2": "SAMEORIGIN, SAMEORIGIN",
     "sameorigin3": "SAMEORIGIN,SAMEORIGIN , SAMEORIGIN",
     "mixedpolicy": "DENY,SAMEORIGIN",
 
@@ -39,9 +48,13 @@ function handleRequest(request, response
 
   if (testHeaders.hasOwnProperty(query['xfo'])) {
     response.setHeader("X-Frame-Options", testHeaders[query['xfo']], false);
   }
 
   // from the test harness we'll be checking for the presence of this element
   // to test if the page loaded
   response.write("<h1 id=\"test\">" + query["testid"] + "</h1>");
+
+  if (query['multipart'] == "1") {
+    response.write("\r\n--" + BOUNDARY + "\r\n");
+  }
 }
--- a/content/base/test/test_x-frame-options.html
+++ b/content/base/test/test_x-frame-options.html
@@ -113,16 +113,27 @@ var testFramesLoaded = function() {
   var test11 = frame.contentDocument.getElementById("test").textContent;
   is(test11, "allow-from-allow", "test allow-from-allow");
 
   // iframe from different origin, with allow-from: other - should not load
   frame = harness.contentDocument.getElementById("allow-from-deny");
   var test12 = frame.contentDocument.getElementById("test");
   is(test12, null, "test allow-from-deny");
 
+  // iframe from different origin, X-F-O: SAMEORIGIN, multipart - should not load
+  frame = harness.contentDocument.getElementById("sameorigin-multipart");
+  var test13 = frame.contentDocument.getElementById("test");
+  is(test13, null, "test sameorigin-multipart");
+
+  // iframe from same origin, X-F-O: SAMEORIGIN, multipart - should load
+  frame = harness.contentDocument.getElementById("sameorigin-multipart2");
+  var test14 = frame.contentDocument.getElementById("test").textContent;
+  is(test14, "sameorigin-multipart2", "test sameorigin-multipart2");
+
+
   // frames from bug 836132 tests
   {
     frame = harness.contentDocument.getElementById("allow-from-allow-1");
     var theTestResult = frame.contentDocument.getElementById("test");
     isnot(theTestResult, null, "test afa1 should have been allowed");
     if(theTestResult) {
       is(theTestResult.textContent, "allow-from-allow-1", "test allow-from-allow-1");
     }
--- a/docshell/base/nsDSURIContentListener.cpp
+++ b/docshell/base/nsDSURIContentListener.cpp
@@ -255,34 +255,29 @@ nsDSURIContentListener::SetParentContent
     else
     {
         mWeakParentContentListener = nullptr;
         mParentContentListener = nullptr;
     }
     return NS_OK;
 }
 
-bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIRequest *request,
+bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel *httpChannel,
                                                         const nsAString& policy) {
     static const char allowFrom[] = "allow-from";
     const uint32_t allowFromLen = ArrayLength(allowFrom) - 1;
     bool isAllowFrom =
         StringHead(policy, allowFromLen).LowerCaseEqualsLiteral(allowFrom);
 
     // return early if header does not have one of the values with meaning
     if (!policy.LowerCaseEqualsLiteral("deny") &&
         !policy.LowerCaseEqualsLiteral("sameorigin") &&
         !isAllowFrom)
         return true;
 
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
-    if (!httpChannel) {
-        return true;
-    }
-
     nsCOMPtr<nsIURI> uri;
     httpChannel->GetURI(getter_AddRefs(uri));
 
     // XXXkhuey when does this happen?  Is returning true safe here?
     if (!mDocShell) {
         return true;
     }
 
@@ -397,17 +392,30 @@ bool nsDSURIContentListener::CheckOneFra
     return true;
 }
 
 // Check if X-Frame-Options permits this document to be loaded as a subdocument.
 // This will iterate through and check any number of X-Frame-Options policies
 // in the request (comma-separated in a header, multiple headers, etc).
 bool nsDSURIContentListener::CheckFrameOptions(nsIRequest *request)
 {
-    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
+    nsresult rv;
+    nsCOMPtr<nsIChannel> chan = do_QueryInterface(request);
+    if (!chan) {
+      return true;
+    }
+
+    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(chan);
+    if (!httpChannel) {
+      // check if it is hiding in a multipart channel
+      rv = mDocShell->GetHttpChannel(chan, getter_AddRefs(httpChannel));
+      if (NS_FAILED(rv))
+        return false;
+    }
+
     if (!httpChannel) {
         return true;
     }
 
     nsAutoCString xfoHeaderCValue;
     httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-Frame-Options"),
                                    xfoHeaderCValue);
     NS_ConvertUTF8toUTF16 xfoHeaderValue(xfoHeaderCValue);
@@ -416,17 +424,17 @@ bool nsDSURIContentListener::CheckFrameO
     if (xfoHeaderValue.IsEmpty())
         return true;
 
     // iterate through all the header values (usually there's only one, but can
     // be many.  If any want to deny the load, deny the load.
     nsCharSeparatedTokenizer tokenizer(xfoHeaderValue, ',');
     while (tokenizer.hasMoreTokens()) {
         const nsSubstring& tok = tokenizer.nextToken();
-        if (!CheckOneFrameOptionsPolicy(request, tok)) {
+        if (!CheckOneFrameOptionsPolicy(httpChannel, tok)) {
             // cancel the load and display about:blank
             httpChannel->Cancel(NS_BINDING_ABORTED);
             if (mDocShell) {
                 nsCOMPtr<nsIWebNavigation> webNav(do_QueryObject(mDocShell));
                 if (webNav) {
                     webNav->LoadURI(NS_LITERAL_STRING("about:blank").get(),
                                     0, nullptr, nullptr, nullptr);
                 }
--- a/docshell/base/nsDSURIContentListener.h
+++ b/docshell/base/nsDSURIContentListener.h
@@ -9,16 +9,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsIURIContentListener.h"
 #include "nsWeakReference.h"
 
 class nsDocShell;
 class nsIWebNavigationInfo;
+class nsIHttpChannel;
 
 class nsDSURIContentListener :
     public nsIURIContentListener,
     public nsSupportsWeakReference
 
 {
 friend class nsDocShell;
 public:
@@ -33,17 +34,17 @@ protected:
 
     void DropDocShellreference() {
         mDocShell = nullptr;
     }
 
     // Determine if X-Frame-Options allows content to be framed
     // as a subdocument
     bool CheckFrameOptions(nsIRequest* request);
-    bool CheckOneFrameOptionsPolicy(nsIRequest* request,
+    bool CheckOneFrameOptionsPolicy(nsIHttpChannel* httpChannel,
                                     const nsAString& policy);
 
     enum XFOHeader {
       eDENY,
       eSAMEORIGIN,
       eALLOWFROM
     };