Bug 1647825 - Part 2: Report the XFO and CSP: frame-ancestors error through the telemetry event. r=ckerschb,chutten,nhnt11
After user ticks the checkbox of allowing error reporting, we will report the error through the telemetry event. The event includes the error type, XFO policy, CSP policy, the frame uri and the top-level uri. Differential Revision: https://phabricator.services.mozilla.com/D82332
This commit is contained in:
@@ -29,6 +29,7 @@ class NetErrorChild extends RemotePageChild {
|
|||||||
"RPMPrefIsLocked",
|
"RPMPrefIsLocked",
|
||||||
"RPMAddToHistogram",
|
"RPMAddToHistogram",
|
||||||
"RPMRecordTelemetryEvent",
|
"RPMRecordTelemetryEvent",
|
||||||
|
"RPMGetHttpResponseHeader",
|
||||||
];
|
];
|
||||||
this.exportFunctions(exportableFunctions);
|
this.exportFunctions(exportableFunctions);
|
||||||
}
|
}
|
||||||
@@ -82,4 +83,24 @@ class NetErrorChild extends RemotePageChild {
|
|||||||
RPMRecordTelemetryEvent(category, event, object, value, extra) {
|
RPMRecordTelemetryEvent(category, event, object, value, extra) {
|
||||||
Services.telemetry.recordEvent(category, event, object, value, extra);
|
Services.telemetry.recordEvent(category, event, object, value, extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the header from the http response of the failed channel. This function
|
||||||
|
// is used in the 'about:neterror' page.
|
||||||
|
RPMGetHttpResponseHeader(responseHeader) {
|
||||||
|
let channel = this.contentWindow.docShell.failedChannel;
|
||||||
|
if (!channel) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
|
if (!httpChannel) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return httpChannel.getResponseHeader(responseHeader);
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ const { SessionStore } = ChromeUtils.import(
|
|||||||
);
|
);
|
||||||
const { HomePage } = ChromeUtils.import("resource:///modules/HomePage.jsm");
|
const { HomePage } = ChromeUtils.import("resource:///modules/HomePage.jsm");
|
||||||
|
|
||||||
|
const { TelemetryController } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/TelemetryController.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
const PREF_SSL_IMPACT_ROOTS = [
|
const PREF_SSL_IMPACT_ROOTS = [
|
||||||
"security.tls.version.",
|
"security.tls.version.",
|
||||||
"security.ssl3.",
|
"security.ssl3.",
|
||||||
@@ -114,6 +118,40 @@ class NetErrorParent extends JSWindowActorParent {
|
|||||||
errorReporter.reportTLSError(securityInfo, host, port);
|
errorReporter.reportTLSError(securityInfo, host, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ReportBlockingError(bcID, scheme, host, port, path, xfoAndCspInfo) {
|
||||||
|
// For reporting X-Frame-Options error and CSP: frame-ancestors errors, We
|
||||||
|
// are collecting 4 pieces of information.
|
||||||
|
// 1. The X-Frame-Options in the response header.
|
||||||
|
// 2. The CSP: frame-ancestors in the response header.
|
||||||
|
// 3. The URI of the frame who triggers this error.
|
||||||
|
// 4. The top-level URI which loads the frame.
|
||||||
|
//
|
||||||
|
// We will exclude the query strings from the reporting URIs.
|
||||||
|
//
|
||||||
|
// More details about the data we send can be found in
|
||||||
|
// https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/telemetry/data/xfocsp-error-report-ping.html
|
||||||
|
//
|
||||||
|
|
||||||
|
let topBC = BrowsingContext.get(bcID).top;
|
||||||
|
let topURI = topBC.currentWindowGlobal.documentURI;
|
||||||
|
|
||||||
|
// Get the URLs without query strings.
|
||||||
|
let frame_uri = `${scheme}//${host}${port == -1 ? "" : ":" + port}${path}`;
|
||||||
|
let top_uri = `${topURI.scheme}//${topURI.hostPort}${topURI.filePath}`;
|
||||||
|
|
||||||
|
TelemetryController.submitExternalPing(
|
||||||
|
"xfocsp-error-report",
|
||||||
|
{
|
||||||
|
...xfoAndCspInfo,
|
||||||
|
frame_hostname: host,
|
||||||
|
top_hostname: topURI.host,
|
||||||
|
frame_uri,
|
||||||
|
top_uri,
|
||||||
|
},
|
||||||
|
{ addClientId: false, addEnvironment: false }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the default start page for the cases when the user's own homepage is
|
* Return the default start page for the cases when the user's own homepage is
|
||||||
* infected, so we can get them somewhere safe.
|
* infected, so we can get them somewhere safe.
|
||||||
@@ -285,6 +323,16 @@ class NetErrorParent extends JSWindowActorParent {
|
|||||||
message.data.port
|
message.data.port
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "ReportBlockingError":
|
||||||
|
this.ReportBlockingError(
|
||||||
|
this.browsingContext.id,
|
||||||
|
message.data.scheme,
|
||||||
|
message.data.host,
|
||||||
|
message.data.port,
|
||||||
|
message.data.path,
|
||||||
|
message.data.xfoAndCspInfo
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
case "Browser:CertExceptionError":
|
case "Browser:CertExceptionError":
|
||||||
switch (message.data.elementId) {
|
switch (message.data.elementId) {
|
||||||
|
|||||||
@@ -480,6 +480,52 @@ function setupBlockingReportingUI() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showBlockingErrorReporting();
|
showBlockingErrorReporting();
|
||||||
|
|
||||||
|
if (reportingAutomatic) {
|
||||||
|
reportBlockingError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportBlockingError() {
|
||||||
|
// We only report if we are in a frame.
|
||||||
|
if (window === window.top) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = getErrorCode();
|
||||||
|
// Ensure we only deal with XFO and CSP here.
|
||||||
|
if (!["xfoBlocked", "cspBlocked"].includes(err)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xfo_header = RPMGetHttpResponseHeader("X-Frame-Options");
|
||||||
|
let csp_header = RPMGetHttpResponseHeader("Content-Security-Policy");
|
||||||
|
|
||||||
|
// Extract the 'CSP: frame-ancestors' from the CSP header.
|
||||||
|
let reg = /(?:^|\s)frame-ancestors\s([^;]*)[$]*/i;
|
||||||
|
let match = reg.exec(csp_header);
|
||||||
|
csp_header = match ? match[1] : "";
|
||||||
|
|
||||||
|
// If it's the csp error page without the CSP: frame-ancestors, this means
|
||||||
|
// this error page is not triggered by CSP: frame-ancestors. So, we bail out
|
||||||
|
// early.
|
||||||
|
if (err === "cspBlocked" && !csp_header) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xfoAndCspInfo = {
|
||||||
|
error_type: err === "xfoBlocked" ? "xfo" : "csp",
|
||||||
|
xfo_header,
|
||||||
|
csp_header,
|
||||||
|
};
|
||||||
|
|
||||||
|
RPMSendAsyncMessage("ReportBlockingError", {
|
||||||
|
scheme: document.location.protocol,
|
||||||
|
host: document.location.host,
|
||||||
|
port: parseInt(document.location.port) || -1,
|
||||||
|
path: document.location.pathname,
|
||||||
|
xfoAndCspInfo,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSetAutomatic(checked) {
|
function onSetAutomatic(checked) {
|
||||||
@@ -501,6 +547,11 @@ function onSetAutomatic(checked) {
|
|||||||
|
|
||||||
function onSetBlockingReportAutomatic(checked) {
|
function onSetBlockingReportAutomatic(checked) {
|
||||||
RPMSetBoolPref("security.xfocsp.errorReporting.automatic", checked);
|
RPMSetBoolPref("security.xfocsp.errorReporting.automatic", checked);
|
||||||
|
|
||||||
|
// If we're enabling reports, send a report for this failure.
|
||||||
|
if (checked) {
|
||||||
|
reportBlockingError();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setNetErrorMessageFromCode() {
|
async function setNetErrorMessageFromCode() {
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
|
||||||
|
"xfocsp-error-report" ping
|
||||||
|
==========================
|
||||||
|
|
||||||
|
This opt-in ping is sent when an X-Frame-Options error or a CSP: frame-ancestors
|
||||||
|
happens to report the error. Users can opt-in this by checking the reporting
|
||||||
|
checkbox. After users opt-in, this ping will be sent every time the error
|
||||||
|
happens. Users can opt-out this by un-checking the reporting checkbox on the
|
||||||
|
error page. The client_id and environment are not sent with this ping.
|
||||||
|
|
||||||
|
Structure:
|
||||||
|
|
||||||
|
.. code-block:: js
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "xfocsp-error-report",
|
||||||
|
... common ping data
|
||||||
|
"payload": {
|
||||||
|
"error_type": <string>,
|
||||||
|
"xfo_header": <string>,
|
||||||
|
"csp_header": <string>,
|
||||||
|
"frame_hostname": <string>,
|
||||||
|
"top_hostname": <string>,
|
||||||
|
"frame_uri": <string>,
|
||||||
|
"top_uri": <string>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info
|
||||||
|
----
|
||||||
|
|
||||||
|
error_type
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
The type of what error triggers this ping. This could be either "xfo" or "csp".
|
||||||
|
|
||||||
|
xfo_header
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
The X-Frame-Options value in the response HTTP header.
|
||||||
|
|
||||||
|
csp_header
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
The CSP: frame-ancestors value in the response HTTP header.
|
||||||
|
|
||||||
|
frame_hostname
|
||||||
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The hostname of the frame which triggers the error.
|
||||||
|
|
||||||
|
top_hostname
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The hostname of the top-level page which loads the frame.
|
||||||
|
|
||||||
|
frame_uri
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
The uri of the frame which triggers the error. This excludes the query strings.
|
||||||
|
|
||||||
|
top_uri
|
||||||
|
~~~~~~~
|
||||||
|
|
||||||
|
The uri of the top-level page which loads the frame. This excludes the query
|
||||||
|
strings.
|
||||||
|
|
||||||
|
|
||||||
|
See also: :doc:`common ping fields <common-ping>`
|
||||||
@@ -81,6 +81,7 @@ let RemotePageAccessManager = {
|
|||||||
"Browser:SSLErrorGoBack",
|
"Browser:SSLErrorGoBack",
|
||||||
"Browser:PrimeMitm",
|
"Browser:PrimeMitm",
|
||||||
"Browser:ResetEnterpriseRootsPref",
|
"Browser:ResetEnterpriseRootsPref",
|
||||||
|
"ReportBlockingError",
|
||||||
],
|
],
|
||||||
RPMAddMessageListener: ["*"],
|
RPMAddMessageListener: ["*"],
|
||||||
RPMRemoveMessageListener: ["*"],
|
RPMRemoveMessageListener: ["*"],
|
||||||
@@ -101,6 +102,7 @@ let RemotePageAccessManager = {
|
|||||||
],
|
],
|
||||||
RPMPrefIsLocked: ["security.tls.version.min"],
|
RPMPrefIsLocked: ["security.tls.version.min"],
|
||||||
RPMAddToHistogram: ["*"],
|
RPMAddToHistogram: ["*"],
|
||||||
|
RPMGetHttpResponseHeader: ["*"],
|
||||||
},
|
},
|
||||||
"about:newinstall": {
|
"about:newinstall": {
|
||||||
RPMGetUpdateChannel: ["*"],
|
RPMGetUpdateChannel: ["*"],
|
||||||
|
|||||||
@@ -37,5 +37,6 @@ module.exports = {
|
|||||||
RPMRecordTelemetryEvent: false,
|
RPMRecordTelemetryEvent: false,
|
||||||
RPMAddToHistogram: false,
|
RPMAddToHistogram: false,
|
||||||
RPMRemoveMessageListener: false,
|
RPMRemoveMessageListener: false,
|
||||||
|
RPMGetHttpResponseHeader: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user