Bug 1560741 - Part 1: Disallow notification permission requests from cross-origin iframes; r=johannh
☠☠ backed out by 46b45c5243e9 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 12 Aug 2019 19:34:35 +0000
changeset 487509 9dc1d39d27866ce6254c5246a9d15f551ad02021
parent 487508 3fdd496723be2ad1db1fd9ce2ccff5cf6839578d
child 487510 c08aa2078829f2826a58a34ea60b7bca23008bd7
push id36425
push userbtara@mozilla.com
push dateTue, 13 Aug 2019 09:54:32 +0000
treeherdermozilla-central@e29ba984dad2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1560741
milestone70.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 1560741 - Part 1: Disallow notification permission requests from cross-origin iframes; r=johannh Differential Revision: https://phabricator.services.mozilla.com/D41305
dom/locales/en-US/chrome/dom/dom.properties
dom/notification/Notification.cpp
dom/notification/test/mochitest/blank.html
dom/notification/test/mochitest/mochitest.ini
dom/notification/test/mochitest/test_notification_crossorigin_iframe.html
modules/libpref/init/StaticPrefList.yaml
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -319,16 +319,17 @@ LargeAllocationSuccess=This page was loa
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name. Do not translate GET.
 LargeAllocationNonGetRequest=A Large-Allocation header was ignored due to the load being triggered by a non-GET request.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name. Do not translate `window.opener`.
 LargeAllocationNotOnlyToplevelInTabGroup=A Large-Allocation header was ignored due to the presence of windows which have a reference to this browsing context through the frame hierarchy or window.opener.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
 LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document not being loaded out of process.
 GeolocationInsecureRequestIsForbidden=A Geolocation request can only be fulfilled in a secure context.
 NotificationsInsecureRequestIsForbidden=The Notification permission may only be requested in a secure context.
+NotificationsCrossOriginIframeRequestIsForbidden=The Notification permission may only be requested in a top-level document or same-origin iframe.
 NotificationsRequireUserGesture=The Notification permission may only be requested from inside a short running user-generated event handler.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name.
 LargeAllocationNonWin32=This page would be loaded in a new process due to a Large-Allocation header, however Large-Allocation process creation is disabled on non-Win32 platforms.
 # LOCALIZATION NOTE: Do not translate "content", "Window", and "window.top"
 WindowContentUntrustedWarning=The ‘content’ attribute of Window objects is deprecated.  Please use ‘window.top’ instead.
 # LOCALIZATION NOTE: The first %S is the tag name of the element that starts the loop, the second %S is the element's ID.
 SVGRefLoopWarning=The SVG <%S> with ID “%S” has a reference loop.
 # LOCALIZATION NOTE: The first %S is the tag name of the element in the chain where the chain was broken, the second %S is the element's ID.
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -465,28 +465,31 @@ NS_IMPL_RELEASE_INHERITED(NotificationPe
                           ContentPermissionRequestBase)
 
 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(
     NotificationPermissionRequest, ContentPermissionRequestBase, nsIRunnable,
     nsINamed)
 
 NS_IMETHODIMP
 NotificationPermissionRequest::Run() {
-  if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
+  bool isSystem = nsContentUtils::IsSystemPrincipal(mPrincipal);
+  bool blocked = false;
+  if (isSystem) {
     mPermission = NotificationPermission::Granted;
   } else {
     // File are automatically granted permission.
     nsCOMPtr<nsIURI> uri;
     mPrincipal->GetURI(getter_AddRefs(uri));
 
     if (uri && uri->SchemeIs("file")) {
       mPermission = NotificationPermission::Granted;
     } else if (!StaticPrefs::dom_webnotifications_allowinsecure() &&
                !mWindow->IsSecureContext()) {
       mPermission = NotificationPermission::Denied;
+      blocked = true;
       nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
       if (doc) {
         nsContentUtils::ReportToConsole(
             nsIScriptError::errorFlag, NS_LITERAL_CSTRING("DOM"), doc,
             nsContentUtils::eDOM_PROPERTIES,
             "NotificationsInsecureRequestIsForbidden");
       }
     }
@@ -504,16 +507,32 @@ NotificationPermissionRequest::Run() {
     case PromptResult::Denied:
       mPermission = NotificationPermission::Denied;
       break;
     default:
       // ignore
       break;
   }
 
+  // Check this after checking the prompt prefs to make sure this pref overrides
+  // those.  We rely on this for testing purposes.
+  if (!isSystem && !blocked &&
+      !StaticPrefs::dom_webnotifications_allowcrossoriginiframe() &&
+      !mPrincipal->Subsumes(mTopLevelPrincipal)) {
+    mPermission = NotificationPermission::Denied;
+    blocked = true;
+    nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
+    if (doc) {
+      nsContentUtils::ReportToConsole(
+          nsIScriptError::errorFlag, NS_LITERAL_CSTRING("DOM"), doc,
+          nsContentUtils::eDOM_PROPERTIES,
+          "NotificationsCrossOriginIframeRequestIsForbidden");
+    }
+  }
+
   if (mPermission != NotificationPermission::Default) {
     return DispatchResolvePromise();
   }
 
   return nsContentPermissionUtils::AskPermission(this, mWindow);
 }
 
 NS_IMETHODIMP
new file mode 100644
--- /dev/null
+++ b/dom/notification/test/mochitest/blank.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+  <body></body>
+</html>
--- a/dom/notification/test/mochitest/mochitest.ini
+++ b/dom/notification/test/mochitest/mochitest.ini
@@ -1,16 +1,18 @@
 [DEFAULT]
 
 support-files =
+  blank.html
   create_notification.html
   MockServices.js
   NotificationTest.js
 skip-if = toolkit == 'android' && !is_fennec # Bug 1531097
 
 [test_notification_basics.html]
+[test_notification_crossorigin_iframe.html]
 # This test needs to be run on HTTP (not HTTPS).
 [test_notification_insecure_context.html]
 [test_notification_storage.html]
 [test_bug931307.html]
 skip-if = (os == 'android') # Bug 1258975 on android.
 [test_notification_tag.html]
 fail-if = fission
new file mode 100644
--- /dev/null
+++ b/dom/notification/test/mochitest/test_notification_crossorigin_iframe.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests that Notification permissions are denied in cross-origin iframes.
+https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
+-->
+<head>
+  <title>Notification permission in cross-origin iframes</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+  <p id="display"></p>
+  <div id="content" style="display: none">
+  </div>
+  <pre id="test">
+  <script class="testbody" type="text/javascript">
+  SimpleTest.waitForExplicitFinish();
+
+  const kBlankURL = "https://example.com/tests/dom/notification/test/mochitest/blank.html";
+
+  (async function runTest() {
+    await SpecialPowers.setBoolPref("notification.prompt.testing", true);
+    await SpecialPowers.setBoolPref("notification.prompt.testing.allow", true);
+    await SpecialPowers.setBoolPref("dom.webnotifications.allowinsecure", true);
+
+    let iframe = document.createElement("iframe");
+    iframe.src = kBlankURL;
+    document.body.appendChild(iframe);
+    await new Promise(resolve => {
+      iframe.onload = resolve;
+    });
+
+    const Notif = SpecialPowers.wrap(iframe.contentWindow).Notification;
+    let response = await Notif.requestPermission();
+    is(response, "denied", "Denied permission in cross-origin iframe");
+
+    await SpecialPowers.pushPrefEnv({"set": [["dom.webnotifications.allowcrossoriginiframe", true]]});
+
+    response = await Notif.requestPermission();
+    is(response, "granted", "Granted permission in cross-origin iframe with pref set");
+
+    await SpecialPowers.clearUserPref("notification.prompt.testing");
+    await SpecialPowers.clearUserPref("notification.prompt.testing.allow");
+    await SpecialPowers.clearUserPref("dom.webnotifications.allowinsecure");
+
+    SimpleTest.finish();
+  })();
+  </script>
+  </pre>
+</body>
+</html>
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -2332,16 +2332,21 @@
   value: false
   mirror: always
 
 - name: dom.webnotifications.allowinsecure
   type: RelaxedAtomicBool
   value: false
   mirror: always
 
+- name: dom.webnotifications.allowcrossoriginiframe
+  type: RelaxedAtomicBool
+  value: false
+  mirror: always
+
 - name: dom.webnotifications.enabled
   type: RelaxedAtomicBool
   value: true
   mirror: always
 
 - name: dom.webnotifications.requireuserinteraction
   type: RelaxedAtomicBool
   value: @IS_EARLY_BETA_OR_EARLIER@