Bug 1335475 - Deny plugins from non-HTTP/HTTPS origins. r=qdot r=ksteuber draft
authorBenjamin Smedberg <benjamin@smedbergs.us>
Tue, 07 Mar 2017 09:34:03 -0500
changeset 578071 5124ed7f5e6e0db9de5f8d4aad5b902f0d9fed9d
parent 578070 13a84cf620ad87bd52d2a38f3356be2c0d24783d
child 628675 9e8a872def36b7ee93dd2aa89d5954ce6064473c
push id58878
push userbsmedberg@mozilla.com
push dateMon, 15 May 2017 20:41:10 +0000
reviewersqdot, ksteuber
bugs1335475
milestone55.0a1
Bug 1335475 - Deny plugins from non-HTTP/HTTPS origins. r=qdot r=ksteuber MozReview-Commit-ID: 3kPeycfMWVw
dom/base/nsDocument.cpp
dom/plugins/test/mochitest/browser.ini
dom/plugins/test/mochitest/browser_bug1335475.js
dom/plugins/test/mochitest/plugin_test.html
modules/libpref/init/all.js
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13230,33 +13230,55 @@ ArrayContainsTable(const nsTArray<nsCStr
  * For more information, see
  * toolkit/components/url-classifier/flash-block-lists.rst
  */
 FlashClassification
 nsDocument::PrincipalFlashClassification()
 {
   nsresult rv;
 
-  // If flash blocking is disabled, it is equivalent to all sites being
-  // on neither list.
-  if (!Preferences::GetBool("plugins.flashBlock.enabled")) {
+  bool httpOnly = Preferences::GetBool("plugins.http_https_only", true);
+  bool flashBlock = Preferences::GetBool("plugins.flashBlock.enabled", false);
+
+  // If neither pref is on, skip the null-principal and principal URI checks.
+  if (!httpOnly && !flashBlock) {
     return FlashClassification::Unknown;
   }
 
   nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
   if (principal->GetIsNullPrincipal()) {
     return FlashClassification::Denied;
   }
 
   nsCOMPtr<nsIURI> classificationURI;
   rv = principal->GetURI(getter_AddRefs(classificationURI));
   if (NS_FAILED(rv) || !classificationURI) {
     return FlashClassification::Denied;
   }
 
+  if (httpOnly) {
+    // Only allow plugins for documents from an HTTP/HTTPS origin. This should
+    // allow dependent data: URIs to load plugins, but not:
+    // * chrome documents
+    // * "bare" data: loads
+    // * FTP/gopher/file
+    nsAutoCString scheme;
+    rv = classificationURI->GetScheme(scheme);
+    if (NS_WARN_IF(NS_FAILED(rv)) ||
+        !(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https"))) {
+      return FlashClassification::Denied;
+    }
+  }
+
+  // If flash blocking is disabled, it is equivalent to all sites being
+  // on neither list.
+  if (!flashBlock) {
+    return FlashClassification::Unknown;
+  }
+
   nsAutoCString allowTables, allowExceptionsTables,
                 denyTables, denyExceptionsTables,
                 subDocDenyTables, subDocDenyExceptionsTables,
                 tables;
   Preferences::GetCString("urlclassifier.flashAllowTable", &allowTables);
   MaybeAddTableToTableList(allowTables, tables);
   Preferences::GetCString("urlclassifier.flashAllowExceptTable",
                           &allowExceptionsTables);
--- a/dom/plugins/test/mochitest/browser.ini
+++ b/dom/plugins/test/mochitest/browser.ini
@@ -8,8 +8,9 @@ support-files =
 [browser_bug1163570.js]
 skip-if = true # Bug 1249878
 [browser_bug1196539.js]
 skip-if = (!e10s || os != "win")
 [browser_tabswitchbetweenplugins.js]
 skip-if = (!e10s || os != "win")
 [browser_pluginscroll.js]
 skip-if = (true || !e10s || os != "win") # Bug 1213631
+[browser_bug1335475.js]
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/browser_bug1335475.js
@@ -0,0 +1,64 @@
+var rootDir = getRootDirectory(gTestPath);
+const gTestRoot = rootDir.replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
+
+add_task(function*() {
+  is(navigator.plugins.length, 0,
+     "plugins should not be available to chrome-privilege pages");
+  ok(!("application/x-test" in navigator.mimeTypes),
+     "plugins should not be available to chrome-privilege pages");
+
+  yield BrowserTestUtils.withNewTab({ gBrowser, url: "about:blank" }, function*(browser) {
+    // about:blank triggered from a toplevel load should not inherit permissions
+    yield ContentTask.spawn(browser, null, function*() {
+      is(content.window.navigator.plugins.length, 0,
+         "plugins should not be available to null-principal about:blank");
+      ok(!("application/x-test" in content.window.navigator.mimeTypes),
+         "plugins should not be available to null-principal about:blank");
+    });
+
+    let promise = BrowserTestUtils.browserLoaded(browser);
+    browser.loadURI(gTestRoot + "plugin_test.html");
+    yield promise;
+
+    yield ContentTask.spawn(browser, null, function*() {
+      ok(content.window.navigator.plugins.length > 0,
+         "plugins should be available to HTTP-loaded pages");
+      ok("application/x-test" in content.window.navigator.mimeTypes,
+         "plugins should be available to HTTP-loaded pages");
+
+      let subwindow = content.document.getElementById("subf").contentWindow;
+
+      ok("application/x-test" in subwindow.navigator.mimeTypes,
+         "plugins should be available to an about:blank subframe loaded from a site");
+    });
+
+    // navigate from the HTTP page to an about:blank page which ought to
+    // inherit permissions
+    promise = BrowserTestUtils.browserLoaded(browser);
+    yield ContentTask.spawn(browser, null, function*() {
+      content.document.getElementById("aboutlink").click();
+    });
+    yield promise;
+
+    yield ContentTask.spawn(browser, null, function*() {
+      is(content.window.location.href, "about:blank", "sanity-check about:blank load");
+      ok("application/x-test" in content.window.navigator.mimeTypes,
+         "plugins should be available when a site triggers an about:blank load");
+    });
+
+    // navigate to the file: URI, which shouldn't allow plugins. This might
+    // be wrapped in jar:, but that shouldn't matter for this test
+    promise = BrowserTestUtils.browserLoaded(browser);
+    let converteduri = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry).convertChromeURL(Services.io.newURI(rootDir + "plugin_test.html"));
+    browser.loadURI(converteduri.spec);
+    yield promise;
+
+    yield ContentTask.spawn(browser, null, function*() {
+      ok(!("application/x-test" in content.window.navigator.mimeTypes),
+         "plugins should not be available to file: URI content");
+    });
+  });
+
+  // As much as it would be nice, this doesn't actually check ftp:// because
+  // we don't have a synthetic server.
+});
--- a/dom/plugins/test/mochitest/plugin_test.html
+++ b/dom/plugins/test/mochitest/plugin_test.html
@@ -2,10 +2,15 @@
 <html>
 <head>
 <meta charset="utf-8">
 </head>
 <body>
   <embed id="testplugin" type="application/x-test" drawmode="solid" color="ff00ff00" wmode="window"
          style="position:absolute; top:50px; left:50px; width:500px; height:250px">
 <div style="display:block; height:3000px;"></div>
+
+<iframe id="subf" src="about:blank" width="300" height="300"></iframe>
+
+<a href="about:blank" id="aboutlink">Navigate to about:blank</a>
+
 </body>
 </html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5296,16 +5296,17 @@ pref("browser.safebrowsing.provider.mozi
 
 pref("urlclassifier.flashAllowTable", "test-flashallow-simple,allow-flashallow-digest256");
 pref("urlclassifier.flashAllowExceptTable", "testexcept-flashallow-simple,except-flashallow-digest256");
 pref("urlclassifier.flashTable", "test-flash-simple,block-flash-digest256");
 pref("urlclassifier.flashExceptTable", "testexcept-flash-simple,except-flash-digest256");
 pref("urlclassifier.flashSubDocTable", "test-flashsubdoc-simple,block-flashsubdoc-digest256");
 pref("urlclassifier.flashSubDocExceptTable", "testexcept-flashsubdoc-simple,except-flashsubdoc-digest256");
 
+pref("plugins.http_https_only", true);
 pref("plugins.flashBlock.enabled", false);
 
 // Allow users to ignore Safe Browsing warnings.
 pref("browser.safebrowsing.allowOverride", true);
 
 #ifdef MOZILLA_OFFICIAL
 // Normally the "client ID" sent in updates is appinfo.name, but for
 // official Firefox releases from Mozilla we use a special identifier.