Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
/*
* RemotePageAccessManager determines which RPM functions a given
* about page is allowed to access. It does this based on a map from about
* page URLs to allowed functions for that page/URL.
*
* An RPM function will be exported into the page only if it appears
* in the access managers's accessMap for that page's uri.
*
* This module may be used from both the child and parent process.
*
* Please note that prefs that one wants to update need to be
* explicitly allowed within AsyncPrefs.sys.mjs.
*/
export let RemotePageAccessManager = {
/* The accessMap lists the permissions that are allowed per page.
* The structure should be of the following form:
* <URL> : {
* <function name>: [<keys>],
* ...
* }
* For the page with given URL, permission is allowed for each
* listed function with a matching key. The first argument to the
* function must match one of the keys. If keys is an array with a
* single asterisk element ["*"], then all values are permitted.
*/
accessMap: {
"about:certerror": {
RPMSendAsyncMessage: [
"Browser:EnableOnlineMode",
"Browser:ResetSSLPreferences",
"GetChangedCertPrefs",
"Browser:OpenCaptivePortalPage",
"Browser:SSLErrorGoBack",
"Browser:PrimeMitm",
"Browser:ResetEnterpriseRootsPref",
"DisplayOfflineSupportPage",
],
RPMRecordTelemetryEvent: ["*"],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMGetFormatURLPref: ["app.support.baseURL"],
RPMGetBoolPref: [
"security.certerrors.mitm.priming.enabled",
"security.certerrors.permanentOverride",
"security.enterprise_roots.auto-enabled",
"security.certerror.hideAddException",
"network.trr.display_fallback_warning",
],
RPMGetIntPref: [
"security.dialog_enable_delay",
"services.settings.clock_skew_seconds",
"services.settings.last_update_seconds",
],
RPMGetAppBuildID: ["*"],
RPMGetInnerMostURI: ["*"],
RPMIsWindowPrivate: ["*"],
RPMAddToHistogram: ["*"],
},
"about:home": {
RPMSendAsyncMessage: ["ActivityStream:ContentToMain"],
RPMAddMessageListener: ["ActivityStream:MainToContent"],
},
"about:httpsonlyerror": {
RPMGetFormatURLPref: ["app.support.baseURL"],
RPMGetIntPref: ["security.dialog_enable_delay"],
RPMSendAsyncMessage: ["goBack", "openInsecure"],
RPMAddMessageListener: ["WWWReachable"],
RPMTryPingSecureWWWLink: ["*"],
RPMOpenSecureWWWLink: ["*"],
},
"about:certificate": {
RPMSendQuery: ["getCertificates"],
},
"about:neterror": {
RPMSendAsyncMessage: [
"Browser:EnableOnlineMode",
"Browser:ResetSSLPreferences",
"GetChangedCertPrefs",
"Browser:OpenCaptivePortalPage",
"Browser:SSLErrorGoBack",
"Browser:PrimeMitm",
"Browser:ResetEnterpriseRootsPref",
"ReportBlockingError",
"DisplayOfflineSupportPage",
"OpenTRRPreferences",
],
RPMCheckAlternateHostAvailable: ["*"],
RPMRecordTelemetryEvent: [
"security.doh.neterror",
"security.ui.tlserror",
],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMGetFormatURLPref: [
"app.support.baseURL",
"network.trr_ui.skip_reason_learn_more_url",
],
RPMGetBoolPref: [
"security.certerror.hideAddException",
"security.xfocsp.errorReporting.automatic",
"security.xfocsp.errorReporting.enabled",
"network.trr.display_fallback_warning",
],
RPMSetPref: [
"security.xfocsp.errorReporting.automatic",
"network.trr.display_fallback_warning",
],
RPMAddToHistogram: ["*"],
RPMGetInnerMostURI: ["*"],
RPMGetHttpResponseHeader: ["*"],
RPMIsTRROnlyFailure: ["*"],
RPMIsFirefox: ["*"],
RPMIsNativeFallbackFailure: ["*"],
RPMGetTRRSkipReason: ["*"],
RPMGetTRRDomain: ["*"],
RPMIsSiteSpecificTRRError: ["*"],
RPMSetTRRDisabledLoadFlags: ["*"],
RPMSendQuery: ["Browser:AddTRRExcludedDomain"],
RPMGetIntPref: ["network.trr.mode"],
},
"about:newtab": {
RPMSendAsyncMessage: ["ActivityStream:ContentToMain"],
RPMAddMessageListener: ["ActivityStream:MainToContent"],
},
"about:pocket-saved": {
RPMSendAsyncMessage: ["*"],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMGetStringPref: ["extensions.pocket.site"],
},
"about:pocket-signup": {
RPMSendAsyncMessage: ["*"],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMGetStringPref: ["extensions.pocket.site"],
},
"about:pocket-home": {
RPMSendAsyncMessage: ["*"],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMGetStringPref: ["extensions.pocket.site"],
},
"about:pocket-style-guide": {
RPMSendAsyncMessage: ["*"],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
},
"about:privatebrowsing": {
RPMSendAsyncMessage: [
"OpenPrivateWindow",
"SearchBannerDismissed",
"OpenSearchPreferences",
"SearchHandoff",
],
RPMSendQuery: [
"IsPromoBlocked",
"ShouldShowSearchBanner",
"ShouldShowPromo",
"SpecialMessageActionDispatch",
],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMGetFormatURLPref: [
"app.support.baseURL",
"browser.privatebrowsing.vpnpromourl",
],
RPMIsWindowPrivate: ["*"],
RPMGetBoolPref: ["browser.privatebrowsing.felt-privacy-v1"],
},
"about:protections": {
RPMSendAsyncMessage: [
"OpenContentBlockingPreferences",
"OpenAboutLogins",
"OpenSyncPreferences",
"ClearMonitorCache",
"RecordEntryPoint",
],
RPMSendQuery: [
"FetchUserLoginsData",
"FetchMonitorData",
"FetchContentBlockingEvents",
"FetchMobileDeviceConnected",
"GetShowProxyCard",
"FetchEntryPoint",
"FetchVPNSubStatus",
"FetchShowVPNCard",
],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
RPMSetPref: [
"browser.contentblocking.report.show_mobile_app",
"browser.contentblocking.report.hide_vpn_banner",
],
RPMGetBoolPref: [
"browser.contentblocking.report.lockwise.enabled",
"browser.contentblocking.report.monitor.enabled",
"privacy.fingerprintingProtection",
"privacy.socialtracking.block_cookies.enabled",
"browser.contentblocking.report.proxy.enabled",
"privacy.trackingprotection.cryptomining.enabled",
"privacy.trackingprotection.fingerprinting.enabled",
"privacy.trackingprotection.enabled",
"privacy.trackingprotection.socialtracking.enabled",
"browser.contentblocking.report.show_mobile_app",
"browser.contentblocking.report.hide_vpn_banner",
"browser.vpn_promo.enabled",
],
RPMGetStringPref: [
"browser.contentblocking.category",
"browser.contentblocking.report.monitor.url",
"browser.contentblocking.report.monitor.sign_in_url",
"browser.contentblocking.report.manage_devices.url",
"browser.contentblocking.report.proxy_extension.url",
"browser.contentblocking.report.lockwise.mobile-android.url",
"browser.contentblocking.report.lockwise.mobile-ios.url",
"browser.contentblocking.report.mobile-ios.url",
"browser.contentblocking.report.mobile-android.url",
"browser.contentblocking.report.vpn.url",
"browser.contentblocking.report.vpn-promo.url",
"browser.contentblocking.report.vpn-android.url",
"browser.contentblocking.report.vpn-ios.url",
],
RPMGetIntPref: ["network.cookie.cookieBehavior"],
RPMGetFormatURLPref: [
"browser.contentblocking.report.monitor.how_it_works.url",
"browser.contentblocking.report.lockwise.how_it_works.url",
"browser.contentblocking.report.monitor.preferences_url",
"browser.contentblocking.report.monitor.home_page_url",
"browser.contentblocking.report.social.url",
"browser.contentblocking.report.cookie.url",
"browser.contentblocking.report.tracker.url",
"browser.contentblocking.report.fingerprinter.url",
"browser.contentblocking.report.cryptominer.url",
],
RPMRecordTelemetryEvent: ["*"],
},
"about:shoppingsidebar": {
RPMSetPref: [
"browser.shopping.experience2023.optedIn",
"browser.shopping.experience2023.active",
"browser.shopping.experience2023.ads.userEnabled",
"browser.shopping.experience2023.sidebarClosedCount",
"browser.shopping.experience2023.showKeepSidebarClosedMessage",
"browser.shopping.experience2023.autoOpen.userEnabled",
],
RPMGetFormatURLPref: ["app.support.baseURL"],
RPMGetIntPref: ["browser.shopping.experience2023.sidebarClosedCount"],
RPMGetBoolPref: [
"browser.shopping.experience2023.showKeepSidebarClosedMessage",
],
},
"about:tabcrashed": {
RPMSendAsyncMessage: ["Load", "closeTab", "restoreTab", "restoreAll"],
RPMAddMessageListener: ["*"],
RPMRemoveMessageListener: ["*"],
},
"about:welcome": {
RPMSendAsyncMessage: ["ActivityStream:ContentToMain"],
RPMAddMessageListener: ["ActivityStream:MainToContent"],
},
},
/**
* Check if access is allowed to the given feature for a given document.
* This should be called from within the child process.
*
* The feature within the accessMap must list the given aValue, for access to
* be granted.
*
* @param aDocument child process document to call from
* @param aFeature to feature to check access to
* @param aValue value that must be included with that feature's allow list
* @returns true if access is allowed or false otherwise
*/
checkAllowAccess(aDocument, aFeature, aValue) {
let principal = aDocument.nodePrincipal;
// if there is no content principal; deny access
if (!principal) {
return false;
}
return this.checkAllowAccessWithPrincipal(
principal,
aFeature,
aValue,
aDocument
);
},
/**
* Check if access is allowed to the given feature for a given principal.
* This may be called from within the child or parent process.
*
* The feature within the accessMap must list the given aValue, for access to
* be granted.
*
* In the parent process, the passed-in document is expected to be null.
*
* @param aPrincipal principal being called from
* @param aFeature to feature to check access to
* @param aValue value that must be included with that feature's allow list
* @param aDocument optional child process document to call from
* @returns true if access is allowed or false otherwise
*/
checkAllowAccessWithPrincipal(aPrincipal, aFeature, aValue, aDocument) {
let accessMapForFeature = this.checkAllowAccessToFeature(
aPrincipal,
aFeature,
aDocument
);
if (!accessMapForFeature) {
console.error(
"RemotePageAccessManager does not allow access to Feature: ",
aFeature,
" for: ",
aDocument.location
);
return false;
}
// If the actual value is in the allow list for that feature;
// allow access
if (accessMapForFeature.includes(aValue) || accessMapForFeature[0] == "*") {
return true;
}
return false;
},
/**
* Check if a particular feature can be accessed without checking for a
* specific feature value.
*
* @param aPrincipal principal being called from
* @param aFeature to feature to check access to
* @param aDocument optional child process document to call from
* @returns non-null allow list if access is allowed or null otherwise
*/
checkAllowAccessToFeature(aPrincipal, aFeature, aDocument) {
let spec;
if (!aPrincipal.isContentPrincipal) {
// For the sake of remote pages, when the principal has no uri,
// we want to access the "real" document URI directly, e.g. if the
// about: page is sandboxed.
if (!aDocument) {
return null;
}
if (!aDocument.documentURIObject.schemeIs("about")) {
return null;
}
spec =
aDocument.documentURIObject.prePath +
aDocument.documentURIObject.filePath;
} else {
if (!aPrincipal.schemeIs("about")) {
return null;
}
spec = aPrincipal.prePath + aPrincipal.filePath;
}
// Check if there is an entry for that requestying URI in the accessMap;
// if not, deny access.
let accessMapForURI = this.accessMap[spec];
if (!accessMapForURI) {
return null;
}
// Check if the feature is allowed to be accessed for that URI;
// if not, deny access.
return accessMapForURI[aFeature];
},
/**
* This function adds a new page to the access map, but can only
* be used in a test environment.
*/
addPage(aUrl, aFunctionMap) {
if (!Cu.isInAutomation) {
throw new Error("Cannot only modify privileges during testing");
}
if (aUrl in this.accessMap) {
throw new Error("Cannot modify privileges of existing page");
}
this.accessMap[aUrl] = aFunctionMap;
},
};