mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-11 01:57:00 +00:00
Bug 1914203 - Add telemetry for notification permission r=asuth,bvandersloot
Following the parent patch in bug 1914417, this will allow us to decide whether we can block ABA access or not. (Bonus, also whether we can remove requestPermission in non-secure context.) Differential Revision: https://phabricator.services.mozilla.com/D219777
This commit is contained in:
parent
b710b85738
commit
183d09046b
@ -10,10 +10,38 @@
|
||||
#include "mozilla/Components.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/dom/NotificationBinding.h"
|
||||
#include "mozilla/glean/GleanMetrics.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
|
||||
namespace mozilla::dom::notification {
|
||||
|
||||
using GleanLabel = glean::web_notification::ShowOriginLabel;
|
||||
|
||||
static void ReportTelemetry(GleanLabel aLabel,
|
||||
PermissionCheckPurpose aPurpose) {
|
||||
switch (aPurpose) {
|
||||
case PermissionCheckPurpose::PermissionAttribute:
|
||||
glean::web_notification::permission_origin
|
||||
.EnumGet(static_cast<glean::web_notification::PermissionOriginLabel>(
|
||||
aLabel))
|
||||
.Add();
|
||||
return;
|
||||
case PermissionCheckPurpose::PermissionRequest:
|
||||
glean::web_notification::request_permission_origin
|
||||
.EnumGet(static_cast<
|
||||
glean::web_notification::RequestPermissionOriginLabel>(
|
||||
aLabel))
|
||||
.Add();
|
||||
return;
|
||||
case PermissionCheckPurpose::NotificationShow:
|
||||
glean::web_notification::show_origin.EnumGet(aLabel).Add();
|
||||
return;
|
||||
default:
|
||||
MOZ_CRASH("Unknown permission checker");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsNotificationAllowedFor(nsIPrincipal* aPrincipal) {
|
||||
if (aPrincipal->IsSystemPrincipal()) {
|
||||
return true;
|
||||
@ -34,6 +62,7 @@ bool IsNotificationForbiddenFor(nsIPrincipal* aPrincipal,
|
||||
|
||||
if (!isSecureContext) {
|
||||
if (aRequestorDoc) {
|
||||
glean::web_notification::insecure_context_permission_request.Add();
|
||||
nsContentUtils::ReportToConsole(
|
||||
nsIScriptError::errorFlag, "DOM"_ns, aRequestorDoc,
|
||||
nsContentUtils::eDOM_PROPERTIES,
|
||||
@ -48,6 +77,7 @@ bool IsNotificationForbiddenFor(nsIPrincipal* aPrincipal,
|
||||
if (aEffectiveStoragePrincipal->OriginAttributesRef()
|
||||
.mPartitionKey.IsEmpty()) {
|
||||
// first party
|
||||
ReportTelemetry(GleanLabel::eFirstParty, aPurpose);
|
||||
return false;
|
||||
}
|
||||
nsString outScheme;
|
||||
@ -58,10 +88,12 @@ bool IsNotificationForbiddenFor(nsIPrincipal* aPrincipal,
|
||||
outPort, outForeignByAncestorContext);
|
||||
if (outForeignByAncestorContext) {
|
||||
// nested first party
|
||||
ReportTelemetry(GleanLabel::eNestedFirstParty, aPurpose);
|
||||
return false;
|
||||
}
|
||||
|
||||
// third party
|
||||
ReportTelemetry(GleanLabel::eThirdParty, aPurpose);
|
||||
if (aRequestorDoc) {
|
||||
nsContentUtils::ReportToConsole(
|
||||
nsIScriptError::errorFlag, "DOM"_ns, aRequestorDoc,
|
||||
|
69
dom/notification/metrics.yaml
Normal file
69
dom/notification/metrics.yaml
Normal file
@ -0,0 +1,69 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# Adding a new metric? We have docs for that!
|
||||
# https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/new_definitions_file.html
|
||||
|
||||
---
|
||||
$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0
|
||||
$tags:
|
||||
- 'Core :: DOM: Notifications'
|
||||
|
||||
web_notification:
|
||||
insecure_context_permission_request:
|
||||
type: counter
|
||||
description: >
|
||||
Whether we saw a permission request from an insecure context.
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
notification_emails:
|
||||
- krosylight@mozilla.com
|
||||
expires: never
|
||||
show_origin:
|
||||
type: labeled_counter
|
||||
description: >
|
||||
The category of the origin that calls new Notification/showNotification().
|
||||
labels:
|
||||
- first_party
|
||||
- third_party
|
||||
- nested_first_party
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
notification_emails:
|
||||
- krosylight@mozilla.com
|
||||
expires: never
|
||||
permission_origin:
|
||||
type: labeled_counter
|
||||
description: >
|
||||
The category of the origin that retrieves Notification.permission.
|
||||
labels:
|
||||
- first_party
|
||||
- third_party
|
||||
- nested_first_party
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
notification_emails:
|
||||
- krosylight@mozilla.com
|
||||
expires: never
|
||||
request_permission_origin:
|
||||
type: labeled_counter
|
||||
description: >
|
||||
The category of the origin that calls Notification.requestPermission().
|
||||
labels:
|
||||
- first_party
|
||||
- third_party
|
||||
- nested_first_party
|
||||
bugs:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
data_reviews:
|
||||
- https://bugzilla.mozilla.org/show_bug.cgi?id=1914203
|
||||
notification_emails:
|
||||
- krosylight@mozilla.com
|
||||
expires: never
|
53
dom/notification/test/mochitest/GleanTest.js
Normal file
53
dom/notification/test/mochitest/GleanTest.js
Normal file
@ -0,0 +1,53 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Bug 1799977: Using workaround to test telemetry in plain mochitests
|
||||
const GleanTest = new Proxy(
|
||||
{
|
||||
async testResetFOG() {
|
||||
return SpecialPowers.spawnChrome([], async () => {
|
||||
await Services.fog.testFlushAllChildren();
|
||||
Services.fog.testResetFOG();
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
get(gleanTestObj, gleanTestProp) {
|
||||
if (gleanTestProp in gleanTestObj) {
|
||||
return gleanTestObj[gleanTestProp];
|
||||
}
|
||||
|
||||
return new Proxy(
|
||||
{},
|
||||
{
|
||||
get(categoryObj, categoryProp) {
|
||||
return new Proxy(
|
||||
{},
|
||||
{
|
||||
get(metricObj, metricProp) {
|
||||
return {
|
||||
async testGetValue() {
|
||||
return SpecialPowers.spawnChrome(
|
||||
[gleanTestProp, categoryProp, metricProp],
|
||||
async (categoryName, metricName, label) => {
|
||||
await Services.fog.testFlushAllChildren();
|
||||
const window = this.browsingContext.topChromeWindow;
|
||||
return window.Glean[categoryName][metricName][
|
||||
label
|
||||
].testGetValue();
|
||||
}
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
@ -10,7 +10,7 @@ var NotificationTest = (function () {
|
||||
// it can be used to track data between tests
|
||||
var context = {};
|
||||
|
||||
(function executeRemainingTests(remainingTests) {
|
||||
(async function executeRemainingTests(remainingTests) {
|
||||
if (!remainingTests.length) {
|
||||
callback();
|
||||
return;
|
||||
@ -21,14 +21,14 @@ var NotificationTest = (function () {
|
||||
var startTest = nextTest.call.bind(nextTest, context, finishTest);
|
||||
|
||||
try {
|
||||
startTest();
|
||||
await startTest();
|
||||
// if no callback was defined for test function,
|
||||
// we must manually invoke finish to continue
|
||||
if (nextTest.length === 0) {
|
||||
finishTest();
|
||||
}
|
||||
} catch (e) {
|
||||
ok(false, "Test threw exception!");
|
||||
ok(false, `Test threw exception: ${e}`);
|
||||
finishTest();
|
||||
}
|
||||
})(tests);
|
||||
|
@ -3,6 +3,7 @@ scheme = "https"
|
||||
support-files = [
|
||||
"MockAlertsService.js",
|
||||
"NotificationTest.js",
|
||||
"GleanTest.js",
|
||||
]
|
||||
|
||||
["test_notification_basics.html"]
|
||||
@ -14,6 +15,9 @@ skip-if = [
|
||||
["test_notification_crossorigin_iframe.html"]
|
||||
support-files = ["blank.html"]
|
||||
|
||||
["test_notification_crossorigin_iframe_nested_glean.html"]
|
||||
support-files = ["blank.html"]
|
||||
|
||||
# This test needs to be run on HTTP (not HTTPS).
|
||||
["test_notification_insecure_context.html"]
|
||||
skip-if = [
|
||||
|
@ -3,14 +3,15 @@
|
||||
<head>
|
||||
<title>Notification Basics</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="NotificationTest.js"></script>
|
||||
<script src="NotificationTest.js"></script>
|
||||
<script src="GleanTest.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"></pre>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
var info = NotificationTest.info;
|
||||
var options;
|
||||
@ -18,7 +19,7 @@
|
||||
SimpleTest.requestFlakyTimeout("untriaged");
|
||||
|
||||
var steps = [
|
||||
function() {
|
||||
async function() {
|
||||
info("Test notification spec");
|
||||
ok(Notification, "Notification constructor exists");
|
||||
ok(Notification.permission, "Notification.permission exists");
|
||||
@ -30,6 +31,23 @@
|
||||
Notification.requestPermission();
|
||||
},
|
||||
|
||||
async function() {
|
||||
info("Test Glean telemetry");
|
||||
await GleanTest.testResetFOG();
|
||||
|
||||
await Notification.requestPermission();
|
||||
const requestCount = await GleanTest.webNotification.requestPermissionOrigin.first_party.testGetValue();
|
||||
is(requestCount, 1, "Notification first party request permission counter should increment once.");
|
||||
|
||||
Notification.permission;
|
||||
const permissionCount = await GleanTest.webNotification.permissionOrigin.first_party.testGetValue();
|
||||
is(permissionCount, 1, "Notification first party request permission counter should increment once.");
|
||||
|
||||
await new Promise(r => new Notification("first party").onerror = r);
|
||||
const showCount = await GleanTest.webNotification.showOrigin.first_party.testGetValue();
|
||||
is(showCount, 1, "Notification first party request permission counter should increment once.");
|
||||
},
|
||||
|
||||
async function(done) {
|
||||
info("Test requestPermission deny");
|
||||
function assertPermissionDenied(perm) {
|
||||
|
@ -7,6 +7,7 @@ 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>
|
||||
<script src="GleanTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
@ -37,6 +38,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
|
||||
]);
|
||||
});
|
||||
|
||||
await GleanTest.testResetFOG();
|
||||
|
||||
let checkRequest = async (expectedResponse, msg) => {
|
||||
let response = await this.content.Notification.requestPermission();
|
||||
Assert.equal(response, expectedResponse, msg);
|
||||
@ -46,6 +49,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
|
||||
["denied", "Denied permission in cross-origin iframe"],
|
||||
checkRequest);
|
||||
|
||||
const requestCount = await GleanTest.webNotification.requestPermissionOrigin.third_party.testGetValue();
|
||||
is(requestCount, 1, "Notification third party request permission counter should increment once.");
|
||||
|
||||
let checkPermission = async (expectedPermission, msg) => {
|
||||
let permission = this.content.Notification.permission;
|
||||
Assert.equal(permission, expectedPermission, msg);
|
||||
@ -55,6 +61,25 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1560741
|
||||
["denied", "Permission is denied in cross-origin iframe"],
|
||||
checkPermission);
|
||||
|
||||
const permissionCount = await GleanTest.webNotification.permissionOrigin.third_party.testGetValue();
|
||||
is(permissionCount, 1, "Notification third party permission read counter should increment once.");
|
||||
|
||||
let checkConstruct = async (expectedShown, msg) => {
|
||||
const shown = await new Promise(r => {
|
||||
const n = new this.content.Notification("cross origin");
|
||||
n.onshow = () => r(true);
|
||||
n.onerror = () => r(false);
|
||||
});
|
||||
Assert.equal(shown, expectedShown, msg);
|
||||
};
|
||||
|
||||
await SpecialPowers.spawn(iframe,
|
||||
[false, "Notification constructor should error in cross-origin iframe"],
|
||||
checkConstruct);
|
||||
|
||||
const showCount = await GleanTest.webNotification.showOrigin.third_party.testGetValue();
|
||||
is(showCount, 1, "Notification third party show attempt counter should increment once.");
|
||||
|
||||
await SpecialPowers.pushPrefEnv({"set": [["dom.webnotifications.allowcrossoriginiframe", true]]});
|
||||
|
||||
await SpecialPowers.spawn(iframe,
|
||||
|
@ -0,0 +1,79 @@
|
||||
<!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>
|
||||
<script src="GleanTest.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 kBlankPath = "/tests/dom/notification/test/mochitest/blank.html"
|
||||
const kParentURL = "https://example.org" + kBlankPath;
|
||||
const kChildURL = "https://example.com" + kBlankPath;
|
||||
|
||||
(async function runTest() {
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.src = kParentURL;
|
||||
document.body.appendChild(iframe);
|
||||
await new Promise(resolve => {
|
||||
iframe.onload = resolve;
|
||||
});
|
||||
|
||||
await SpecialPowers.spawn(iframe, [kChildURL], async (childURL) => {
|
||||
const nested = this.content.document.createElement("iframe");
|
||||
nested.src = childURL;
|
||||
this.content.document.body.appendChild(nested);
|
||||
await new Promise(resolve => {
|
||||
nested.onload = resolve;
|
||||
});
|
||||
});
|
||||
|
||||
await GleanTest.testResetFOG();
|
||||
|
||||
await SpecialPowers.spawn(iframe, [], async () => {
|
||||
const nested = this.content.document.querySelector("iframe");
|
||||
await SpecialPowers.spawn(nested, [], async () => {
|
||||
await this.content.Notification.requestPermission();
|
||||
});
|
||||
});
|
||||
|
||||
const requestCount = await GleanTest.webNotification.requestPermissionOrigin.nested_first_party.testGetValue();
|
||||
is(requestCount, 1, "Notification third party request permission counter should increment once.");
|
||||
|
||||
await SpecialPowers.spawn(iframe, [], async () => {
|
||||
const nested = this.content.document.querySelector("iframe");
|
||||
await SpecialPowers.spawn(nested, [], async () => {
|
||||
this.content.Notification.permission;
|
||||
});
|
||||
});
|
||||
|
||||
const permissionCount = await GleanTest.webNotification.permissionOrigin.nested_first_party.testGetValue();
|
||||
is(permissionCount, 1, "Notification third party permission read counter should increment once.");
|
||||
|
||||
await SpecialPowers.spawn(iframe, [], async () => {
|
||||
const nested = this.content.document.querySelector("iframe");
|
||||
await SpecialPowers.spawn(nested, [], async () => {
|
||||
new this.content.Notification("cross origin");
|
||||
});
|
||||
});
|
||||
|
||||
const showCount = await GleanTest.webNotification.showOrigin.nested_first_party.testGetValue();
|
||||
is(showCount, 1, "Notification third party show attempt counter should increment once.");
|
||||
|
||||
SimpleTest.finish();
|
||||
})();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -24,6 +24,7 @@ gecko_metrics = [
|
||||
"dom/media/metrics.yaml",
|
||||
"dom/media/webrtc/metrics.yaml",
|
||||
"dom/metrics.yaml",
|
||||
"dom/notification/metrics.yaml",
|
||||
"dom/performance/metrics.yaml",
|
||||
"dom/security/metrics.yaml",
|
||||
"dom/webauthn/metrics.yaml",
|
||||
|
Loading…
x
Reference in New Issue
Block a user