Bug 624883 - Tests for ensuring that view-source is forbidden in iframes. r=bz
authorBob Owen <bobowencode@gmail.com>
Thu, 23 Jan 2014 16:02:10 +0000
changeset 167576 4e44b26f3c6705259c85e7d98af89df42634b03e
parent 167575 a57801cec8be42836f59ac06e52d6b7401c220f6
child 167577 5d252c7bbea4fe1b66f1d2aac81530ba93a64d4b
push id26174
push userkwierso@gmail.com
push dateSat, 08 Feb 2014 00:55:48 +0000
treeherdermozilla-central@2c873eff7dc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs624883
milestone30.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 624883 - Tests for ensuring that view-source is forbidden in iframes. r=bz
content/base/test/csp/file_CSP_frameancestors_main.html
content/base/test/csp/file_CSP_frameancestors_main.js
content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.html
content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.js
content/base/test/csp/test_CSP_frameancestors.html
docshell/test/chrome/chrome.ini
docshell/test/chrome/file_viewsource_forbidden_in_iframe.html
docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
parser/htmlparser/tests/mochitest/test_viewsource.html
--- a/content/base/test/csp/file_CSP_frameancestors_main.html
+++ b/content/base/test/csp/file_CSP_frameancestors_main.html
@@ -10,19 +10,16 @@
     
 <!-- These iframes will get populated by the attached javascript. -->
 <tt>  aa_allow:   /* innermost frame allows a */</tt><br/>
 <iframe id='aa_allow'></iframe><br/>
 
 <tt>  aa_block:     /* innermost frame denies a */</tt><br/>
 <iframe id='aa_block'></iframe><br/>
 
-<tt>  aa2_block:     /* innermost frame (view-source: URL) denies a */</tt><br/>
-<iframe id='aa2_block'></iframe><br/>
-
 <tt>  ab_allow:     /* innermost frame allows a */</tt><br/>
 <iframe id='ab_allow'></iframe><br/>
 
 <tt>  ab_block:     /* innermost frame denies a */</tt><br/>
 <iframe id='ab_block'></iframe><br/>
 
 <tt>  aba_allow:    /* innermost frame allows b,a */</tt><br/>
 <iframe id='aba_allow'></iframe><br/>
--- a/content/base/test/csp/file_CSP_frameancestors_main.js
+++ b/content/base/test/csp/file_CSP_frameancestors_main.js
@@ -17,20 +17,16 @@ function setupFrames() {
   elt = $('aa_allow');
   elt.src = base.a + "?testid=aa_allow&internalframe=aa_a&csp=" + 
             escape("allow 'none'; frame-ancestors " + host.a + "; script-src 'self'");
 
   elt = $('aa_block');
   elt.src = base.a + "?testid=aa_block&internalframe=aa_b&csp=" + 
             escape("allow 'none'; frame-ancestors 'none'; script-src 'self'");
 
-  elt = $('aa2_block');
-  elt.src = "view-source:" + base.a + "?testid=aa2_block&internalframe=aa_b&csp=" +
-            escape("allow 'none'; frame-ancestors 'none'; script-src 'self'");
-
   elt = $('ab_allow');
   elt.src = base.b + "?testid=ab_allow&internalframe=ab_a&csp=" +
             escape("allow 'none'; frame-ancestors " + host.a + "; script-src 'self'");
 
   elt = $('ab_block');
   elt.src = base.b + "?testid=ab_block&internalframe=ab_b&csp=" +
             escape("allow 'none'; frame-ancestors 'none'; script-src 'self'");
 
--- a/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.html
+++ b/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.html
@@ -10,19 +10,16 @@
 
 <!-- These iframes will get populated by the attached javascript. -->
 <tt>  aa_allow:   /* innermost frame allows a */</tt><br/>
 <iframe id='aa_allow_spec_compliant'></iframe><br/>
 
 <tt>  aa_block:     /* innermost frame denies a */</tt><br/>
 <iframe id='aa_block_spec_compliant'></iframe><br/>
 
-<tt>  aa2_block:     /* innermost frame (view-source: URL) denies a */</tt><br/>
-<iframe id='aa2_block_spec_compliant'></iframe><br/>
-
 <tt>  ab_allow:     /* innermost frame allows a */</tt><br/>
 <iframe id='ab_allow_spec_compliant'></iframe><br/>
 
 <tt>  ab_block:     /* innermost frame denies a */</tt><br/>
 <iframe id='ab_block_spec_compliant'></iframe><br/>
 
 <tt>  aba_allow:    /* innermost frame allows b,a */</tt><br/>
 <iframe id='aba_allow_spec_compliant'></iframe><br/>
--- a/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.js
+++ b/content/base/test/csp/file_CSP_frameancestors_main_spec_compliant.js
@@ -17,20 +17,16 @@ function setupFrames() {
   elt = $('aa_allow_spec_compliant');
   elt.src = base.a + "?testid=aa_allow_spec_compliant&internalframe=aa_a&csp=" +
             escape("default-src 'none'; frame-ancestors " + host.a + "; script-src 'self'");
 
   elt = $('aa_block_spec_compliant');
   elt.src = base.a + "?testid=aa_block_spec_compliant&internalframe=aa_b&csp=" +
             escape("default-src 'none'; frame-ancestors 'none'; script-src 'self'");
 
-  elt = $('aa2_block_spec_compliant');
-  elt.src = "view-source:" + base.a + "?testid=aa2_block_spec_compliant&internalframe=aa_b&csp=" +
-            escape("default-src 'none'; frame-ancestors 'none'; script-src 'self'");
-
   elt = $('ab_allow_spec_compliant');
   elt.src = base.b + "?testid=ab_allow_spec_compliant&internalframe=ab_a&csp=" +
             escape("default-src 'none'; frame-ancestors " + host.a + "; script-src 'self'");
 
   elt = $('ab_block_spec_compliant');
   elt.src = base.b + "?testid=ab_block_spec_compliant&internalframe=ab_b&csp=" +
             escape("default-src 'none'; frame-ancestors 'none'; script-src 'self'");
 
--- a/content/base/test/csp/test_CSP_frameancestors.html
+++ b/content/base/test/csp/test_CSP_frameancestors.html
@@ -15,39 +15,37 @@
 
 var path = "/tests/content/base/test/csp/";
 
 // These are test results: -1 means it hasn't run,
 // true/false is the pass/fail result.
 var framesThatShouldLoad = {
   aa_allow: -1,    /* innermost frame allows a */
   //aa_block: -1,    /* innermost frame denies a */
-  //aa2_block: -1,    /* innermost frame denies a */
   ab_allow: -1,    /* innermost frame allows a */
   //ab_block: -1,    /* innermost frame denies a */
   aba_allow: -1,   /* innermost frame allows b,a */
   //aba_block: -1,   /* innermost frame denies b */
   //aba2_block: -1,  /* innermost frame denies a */
   abb_allow: -1,   /* innermost frame allows b,a */
   //abb_block: -1,   /* innermost frame denies b */
   //abb2_block: -1,  /* innermost frame denies a */
   aa_allow_spec_compliant: -1,    /* innermost frame allows a *
   //aa_block_spec_compliant: -1,    /* innermost frame denies a */
-  //aa2_block_spec_compliant: -1,    /* innermost frame denies a */
   ab_allow_spec_compliant: -1,    /* innermost frame allows a */
   //ab_block_spec_compliant: -1,    /* innermost frame denies a */
   aba_allow_spec_compliant: -1,   /* innermost frame allows b,a */
   //aba_block_spec_compliant: -1,   /* innermost frame denies b */
   //aba2_block_spec_compliant: -1,  /* innermost frame denies a */
   abb_allow_spec_compliant: -1,   /* innermost frame allows b,a */
   //abb_block_spec_compliant: -1,   /* innermost frame denies b */
   //abb2_block_spec_compliant: -1,  /* innermost frame denies a */
 };
 
-var expectedViolationsLeft = 14;
+var expectedViolationsLeft = 12;
 
 // This is used to watch the blocked data bounce off CSP and allowed data
 // get sent out to the wire.
 function examiner() {
   SpecialPowers.addObserver(this, "csp-on-violate-policy", false);
 }
 examiner.prototype  = {
   observe: function(subject, topic, data) {
--- a/docshell/test/chrome/chrome.ini
+++ b/docshell/test/chrome/chrome.ini
@@ -37,16 +37,17 @@ support-files =
   bug662200_window.xul
   bug690056_window.xul
   bug846906.html
   bug89419_window.xul
   bug909218.html
   bug909218.js
   bug92598_window.xul
   docshell_helpers.js
+  file_viewsource_forbidden_in_iframe.html
   generic.html
   mozFrameType_window.xul
 
 [test_allowContentRetargeting.html]
 [test_bug112564.xul]
 [test_bug113934.xul]
 [test_bug215405.xul]
 [test_bug293235.xul]
@@ -73,8 +74,9 @@ support-files =
 [test_bug789773.xul]
 [test_bug846906.xul]
 [test_bug89419.xul]
 [test_bug909218.html]
 [test_bug92598.xul]
 [test_mozFrameType.xul]
 [test_principalInherit.xul]
 [test_private_hidden_window.html]
+[test_viewsource_forbidden_in_iframe.xul]
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/file_viewsource_forbidden_in_iframe.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test ifranes for view-source forbidden in iframe tests</title>
+</head>
+<body>
+  <iframe id="testIframe"></iframe>
+  <iframe id="refIframe"></iframe>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/chrome/test_viewsource_forbidden_in_iframe.xul
@@ -0,0 +1,180 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin/"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=624883
+-->
+<window title="Mozilla Bug 624883"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=624883"
+     target="_blank">Mozilla Bug 624883</a>
+  </body>
+
+  <!-- test code goes here -->
+  <iframe type="content" onload="startTest()" src="file_viewsource_forbidden_in_iframe.html"></iframe>
+
+  <script type="application/javascript">
+  <![CDATA[
+
+  const Ci = Components.interfaces;
+  const Cu = Components.utils;
+
+  Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+  SimpleTest.waitForExplicitFinish();
+
+  // We create a promise that will resolve with the error message
+  // on a network error page load and reject on any other load.
+  function createNetworkErrorMessagePromise(frame) {
+    return new Promise(function(resolve, reject) {
+
+      // Error pages do not fire "load" events, so use a progressListener.
+      var originalDocumentURI = frame.contentDocument.documentURI;
+      var progressListener = {
+        onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
+          // Make sure nothing other than an error page is loaded.
+          if (!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE)) {
+            reject("location change was not to an error page");
+          }
+        },
+
+        onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
+          // Wait until the documentURI changes (from about:blank) this should
+          // be the error page URI.
+          var documentURI = frame.contentDocument.documentURI;
+          if (documentURI == originalDocumentURI) {
+            return;
+          }
+
+          aWebProgress.removeProgressListener(progressListener,
+                                              Ci.nsIWebProgress.NOTIFY_ALL);
+          var matchArray = /about:neterror\?.*&d=([^&]*)/.exec(documentURI);
+          if (!matchArray) {
+            reject("no network error message found in URI")
+            return;
+          }
+
+          var errorMsg = matchArray[1];
+          resolve(decodeURIComponent(errorMsg));
+        },
+
+        QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                               Ci.nsISupportsWeakReference])
+      };
+
+      frame.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebNavigation)
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebProgress)
+                         .addProgressListener(progressListener,
+                                              Ci.nsIWebProgress.NOTIFY_LOCATION |
+                                              Ci.nsIWebProgress.NOTIFY_STATE_REQUEST);
+    });
+  }
+
+  function startTest() {
+    // Get a reference message that we know will be an unknown protocol message,
+    // so we can use it for comparisons in the test cases.
+    var refIframe = window[0].document.getElementById("refIframe");
+    var refErrorPromise = createNetworkErrorMessagePromise(refIframe);
+
+    refErrorPromise.then(
+      function(msg) {
+        window.refErrorMsg = msg;
+        var testIframe = window[0].document.getElementById("testIframe");
+
+        // Run test cases on load of "about:blank", so that the URI always changes
+        // and we can detect this in our Promise.
+        testIframe.onload = runNextTestCase;
+        testIframe.src = "about:blank";
+      },
+      function(reason) {
+        ok(false, "Could not get reference error message", reason);
+        SimpleTest.finish();
+      })
+      .catch(function(e) {
+        ok(false, "Unexpected exception thrown getting reference error message", exception);
+      });
+
+    refIframe.src = "wibble://example.com";
+  }
+
+  function runTestCase(testCase) {
+    var testIframe = window[0].document.getElementById("testIframe");
+    var expectedErrorMsg = window.refErrorMsg.replace("wibble", testCase.expectedProtocolList);
+
+    var testErrorPromise = createNetworkErrorMessagePromise(testIframe);
+    testErrorPromise.then(
+      function(actualErrorMsg) {
+        is(actualErrorMsg, expectedErrorMsg, testCase.desc);
+        testIframe.src = "about:blank";
+      },
+      function(reason) {
+        ok(false, testCase.desc, reason);
+        testIframe.src = "about:blank";
+      })
+      .catch(function(e) {
+        ok(false, testCase.desc + " - unexpected exception thrown", exception);
+      });
+
+    testIframe.src = testCase.protocols + "://example.com/!/";
+  }
+
+  var testCaseIndex = -1;
+  testCases = [
+    {
+      desc: "Test 1: view-source should not be allowed in an iframe",
+      protocols: "view-source:http",
+      expectedProtocolList: "view-source, http"
+    },
+    {
+      desc: "Test 2: feed:view-source should not be allowed in an iframe",
+      protocols: "feed:view-source:http",
+      expectedProtocolList: "feed, view-source, http"
+    },
+    {
+      desc: "Test 3: jar:view-source should not be allowed in an iframe",
+      protocols: "jar:view-source:http",
+      expectedProtocolList: "jar, view-source, http"
+    },
+    {
+      desc: "Test 4: pcast:view-source should not be allowed in an iframe",
+      protocols: "pcast:view-source:http",
+      expectedProtocolList: "pcast, view-source, http"
+    },
+    {
+      desc: "Test 5: pcast:feed:view-source should not be allowed in an iframe",
+      protocols: "pcast:feed:view-source:http",
+      expectedProtocolList: "pcast, feed, view-source, http"
+    },
+    {
+      desc: "Test 6: if invalid protocol first should report before view-source",
+      protocols: "wibble:view-source:http",
+      // Nothing after the invalid protocol gets set as a proper nested URI,
+      // so the list stops there.
+      expectedProtocolList: "wibble"
+    },
+    {
+      desc: "Test 7: if view-source first should report before invalid protocol",
+      protocols: "view-source:wibble:http",
+      expectedProtocolList: "view-source, wibble"
+    }
+  ];
+
+  function runNextTestCase() {
+    ++testCaseIndex;
+    if (testCaseIndex == testCases.length) {
+      SimpleTest.finish();
+      return;
+    }
+
+    runTestCase(testCases[testCaseIndex]);
+  }
+
+  ]]>
+  </script>
+</window>
--- a/parser/htmlparser/tests/mochitest/test_viewsource.html
+++ b/parser/htmlparser/tests/mochitest/test_viewsource.html
@@ -1,13 +1,12 @@
 <!DOCTYPE HTML>
 <html>
   <head>
     <title>Test for view source</title>
-    <script type="text/javascript" src="/MochiKit/packed.js"></script>
     <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   </head>
 
   <body>
 
 <!--
   this is a multi-line comment
@@ -20,44 +19,32 @@
       // Return the source text of the document at the given URL.
       function fetch(url) {
         var xhr = new XMLHttpRequest();
         xhr.open("GET", url, false);    // None of this async silliness,
         xhr.send();                     // we'll just wait.
         return xhr.responseText;
       }
 
-      // Start a "view source" test using the document at "testUrl".  Note that
-      // the test will actually complete asynchronously.
-      function startViewSourceTest(testUrl) {
-        
-        // We will "view" the source of the document in an IFRAME.
-        // If everything is working correctly, the "source" will simply be the
-        // text content of the IFRAME document's top element.
+      // We will "view" the source of the document in a new window.
+      // If everything is working correctly, the "source" will simply be the
+      // text content of the new window's document's body element.
+      // We have to use a window as view-source: is only allowed in top level,
+      // see bug 624883.
 
-        // Create the IFRAME.
-        var iframe = document.createElement('iframe');
-        iframe.src = "view-source:" + testUrl;
+      // Open the new window.
+      var windowWithSource = window.open("about:blank");
 
-        // The actual test will be carried out inside the IFRAME's onload handler.
-        iframe.onload = function () {
-
-          var apparentSource = this.contentDocument.body.textContent;
-          var actualSource = fetch(testUrl);
+      // The actual test will be carried out inside the window's onload handler.
+      windowWithSource.onload = function () {
+        var apparentSource = this.document.body.textContent;
+        var actualSource = fetch(location.href);
+        is(apparentSource, actualSource, "Sources should match");
 
-          // OK, verify that the apparent source and the actual source agree.
-          ok(apparentSource == actualSource, "Sources should match");
-
-          SimpleTest.finish();
-        }
-        
-        // Our test IFRAME is ready to go.  However, it will not load until we
-        // actually append it to the document.  Do that now.  Note that the test
-        // itself will run asynchronously some time after this function returns.
-        document.body.appendChild(iframe);
+        windowWithSource.close()
+        SimpleTest.finish();
       }
 
-      // Start a test using this document as the test document.
-      startViewSourceTest(document.location.href);
+      windowWithSource.location.href = "view-source:" + location.href;
     </script>
 
   </body>
 </html>