forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			2843 lines
		
	
	
	
		
			86 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2843 lines
		
	
	
	
		
			86 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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/. */
 | |
| 
 | |
| import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
 | |
| 
 | |
| import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
 | |
| 
 | |
| const lazy = {};
 | |
| 
 | |
| XPCOMUtils.defineLazyServiceGetters(lazy, {
 | |
|   gCertDB: ["@mozilla.org/security/x509certdb;1", "nsIX509CertDB"],
 | |
|   gExternalProtocolService: [
 | |
|     "@mozilla.org/uriloader/external-protocol-service;1",
 | |
|     "nsIExternalProtocolService",
 | |
|   ],
 | |
|   gHandlerService: [
 | |
|     "@mozilla.org/uriloader/handler-service;1",
 | |
|     "nsIHandlerService",
 | |
|   ],
 | |
|   gMIMEService: ["@mozilla.org/mime;1", "nsIMIMEService"],
 | |
| });
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(lazy, {
 | |
|   AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
 | |
|   BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.sys.mjs",
 | |
|   CustomizableUI: "resource:///modules/CustomizableUI.sys.mjs",
 | |
|   FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
 | |
|   PdfJsDefaultPreferences: "resource://pdf.js/PdfJsDefaultPreferences.sys.mjs",
 | |
|   ProxyPolicies: "resource:///modules/policies/ProxyPolicies.sys.mjs",
 | |
|   UrlbarPrefs: "resource:///modules/UrlbarPrefs.sys.mjs",
 | |
|   WebsiteFilter: "resource:///modules/policies/WebsiteFilter.sys.mjs",
 | |
| });
 | |
| 
 | |
| const PREF_LOGLEVEL = "browser.policies.loglevel";
 | |
| const BROWSER_DOCUMENT_URL = AppConstants.BROWSER_CHROME_URL;
 | |
| const ABOUT_CONTRACT = "@mozilla.org/network/protocol/about;1?what=";
 | |
| 
 | |
| const isXpcshell = Services.env.exists("XPCSHELL_TEST_PROFILE_DIR");
 | |
| 
 | |
| ChromeUtils.defineLazyGetter(lazy, "log", () => {
 | |
|   let { ConsoleAPI } = ChromeUtils.importESModule(
 | |
|     "resource://gre/modules/Console.sys.mjs"
 | |
|   );
 | |
|   return new ConsoleAPI({
 | |
|     prefix: "Policies.jsm",
 | |
|     // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
 | |
|     // messages during development. See LOG_LEVELS in Console.sys.mjs for details.
 | |
|     maxLogLevel: "error",
 | |
|     maxLogLevelPref: PREF_LOGLEVEL,
 | |
|   });
 | |
| });
 | |
| 
 | |
| /*
 | |
|  * ============================
 | |
|  * = POLICIES IMPLEMENTATIONS =
 | |
|  * ============================
 | |
|  *
 | |
|  * The Policies object below is where the implementation for each policy
 | |
|  * happens. An object for each policy should be defined, containing
 | |
|  * callback functions that will be called by the engine.
 | |
|  *
 | |
|  * See the _callbacks object in EnterprisePolicies.js for the list of
 | |
|  * possible callbacks and an explanation of each.
 | |
|  *
 | |
|  * Each callback will be called with two parameters:
 | |
|  * - manager
 | |
|  *   This is the EnterprisePoliciesManager singleton object from
 | |
|  *   EnterprisePolicies.js
 | |
|  *
 | |
|  * - param
 | |
|  *   The parameter defined for this policy in policies-schema.json.
 | |
|  *   It will be different for each policy. It could be a boolean,
 | |
|  *   a string, an array or a complex object. All parameters have
 | |
|  *   been validated according to the schema, and no unknown
 | |
|  *   properties will be present on them.
 | |
|  *
 | |
|  * The callbacks will be bound to their parent policy object.
 | |
|  */
 | |
| export var Policies = {
 | |
|   // Used for cleaning up policies.
 | |
|   // Use the same timing that you used for setting up the policy.
 | |
|   _cleanup: {
 | |
|     onBeforeAddons(manager) {
 | |
|       if (Cu.isInAutomation || isXpcshell) {
 | |
|         console.log("_cleanup from onBeforeAddons");
 | |
|         clearBlockedAboutPages();
 | |
|       }
 | |
|     },
 | |
|     onProfileAfterChange(manager) {
 | |
|       if (Cu.isInAutomation || isXpcshell) {
 | |
|         console.log("_cleanup from onProfileAfterChange");
 | |
|       }
 | |
|     },
 | |
|     onBeforeUIStartup(manager) {
 | |
|       if (Cu.isInAutomation || isXpcshell) {
 | |
|         console.log("_cleanup from onBeforeUIStartup");
 | |
|       }
 | |
|     },
 | |
|     onAllWindowsRestored(manager) {
 | |
|       if (Cu.isInAutomation || isXpcshell) {
 | |
|         console.log("_cleanup from onAllWindowsRestored");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   "3rdparty": {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       manager.setExtensionPolicies(param.Extensions);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   AllowedDomainsForApps: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       Services.obs.addObserver(function (subject, topic, data) {
 | |
|         let channel = subject.QueryInterface(Ci.nsIHttpChannel);
 | |
|         if (channel.URI.host.endsWith(".google.com")) {
 | |
|           channel.setRequestHeader("X-GoogApps-Allowed-Domains", param, true);
 | |
|         }
 | |
|       }, "http-on-modify-request");
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   AppAutoUpdate: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       // Logic feels a bit reversed here, but it's correct. If AppAutoUpdate is
 | |
|       // true, we disallow turning off auto updating, and visa versa.
 | |
|       if (param) {
 | |
|         manager.disallowFeature("app-auto-updates-off");
 | |
|       } else {
 | |
|         manager.disallowFeature("app-auto-updates-on");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   AppUpdatePin: {
 | |
|     validate(param) {
 | |
|       // This is the version when pinning was introduced. Attempting to set a
 | |
|       // pin before this will not work, because Balrog's pinning table will
 | |
|       // never have the necessary entry.
 | |
|       const earliestPinMajorVersion = 102;
 | |
|       const earliestPinMinorVersion = 0;
 | |
| 
 | |
|       let pinParts = param.split(".");
 | |
| 
 | |
|       if (pinParts.length < 2) {
 | |
|         lazy.log.error("AppUpdatePin has too few dots.");
 | |
|         return false;
 | |
|       }
 | |
|       if (pinParts.length > 3) {
 | |
|         lazy.log.error("AppUpdatePin has too many dots.");
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       const trailingPinPart = pinParts.pop();
 | |
|       if (trailingPinPart != "") {
 | |
|         lazy.log.error("AppUpdatePin does not end with a trailing dot.");
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       const pinMajorVersionStr = pinParts.shift();
 | |
|       if (!pinMajorVersionStr.length) {
 | |
|         lazy.log.error("AppUpdatePin's major version is empty.");
 | |
|         return false;
 | |
|       }
 | |
|       if (!/^\d+$/.test(pinMajorVersionStr)) {
 | |
|         lazy.log.error(
 | |
|           "AppUpdatePin's major version contains a non-numeric character."
 | |
|         );
 | |
|         return false;
 | |
|       }
 | |
|       if (/^0/.test(pinMajorVersionStr)) {
 | |
|         lazy.log.error("AppUpdatePin's major version contains a leading 0.");
 | |
|         return false;
 | |
|       }
 | |
|       const pinMajorVersionInt = parseInt(pinMajorVersionStr, 10);
 | |
|       if (isNaN(pinMajorVersionInt)) {
 | |
|         lazy.log.error(
 | |
|           "AppUpdatePin's major version could not be parsed to an integer."
 | |
|         );
 | |
|         return false;
 | |
|       }
 | |
|       if (pinMajorVersionInt < earliestPinMajorVersion) {
 | |
|         lazy.log.error(
 | |
|           `AppUpdatePin must not be earlier than '${earliestPinMajorVersion}.${earliestPinMinorVersion}.'.`
 | |
|         );
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       if (pinParts.length) {
 | |
|         const pinMinorVersionStr = pinParts.shift();
 | |
|         if (!pinMinorVersionStr.length) {
 | |
|           lazy.log.error("AppUpdatePin's minor version is empty.");
 | |
|           return false;
 | |
|         }
 | |
|         if (!/^\d+$/.test(pinMinorVersionStr)) {
 | |
|           lazy.log.error(
 | |
|             "AppUpdatePin's minor version contains a non-numeric character."
 | |
|           );
 | |
|           return false;
 | |
|         }
 | |
|         if (/^0\d/.test(pinMinorVersionStr)) {
 | |
|           lazy.log.error("AppUpdatePin's minor version contains a leading 0.");
 | |
|           return false;
 | |
|         }
 | |
|         const pinMinorVersionInt = parseInt(pinMinorVersionStr, 10);
 | |
|         if (isNaN(pinMinorVersionInt)) {
 | |
|           lazy.log.error(
 | |
|             "AppUpdatePin's minor version could not be parsed to an integer."
 | |
|           );
 | |
|           return false;
 | |
|         }
 | |
|         if (
 | |
|           pinMajorVersionInt == earliestPinMajorVersion &&
 | |
|           pinMinorVersionInt < earliestPinMinorVersion
 | |
|         ) {
 | |
|           lazy.log.error(
 | |
|             `AppUpdatePin must not be earlier than '${earliestPinMajorVersion}.${earliestPinMinorVersion}.'.`
 | |
|           );
 | |
|           return false;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return true;
 | |
|     },
 | |
|     // No additional implementation needed here. UpdateService.jsm will check
 | |
|     // for this policy directly when determining the update URL.
 | |
|   },
 | |
| 
 | |
|   AppUpdateURL: {
 | |
|     // No implementation needed here. UpdateService.jsm will check for this
 | |
|     // policy directly when determining the update URL.
 | |
|   },
 | |
| 
 | |
|   Authentication: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       // When Authentication was originally implemented, it was always
 | |
|       // locked, so it defaults to locked.
 | |
|       let locked = true;
 | |
|       if ("Locked" in param) {
 | |
|         locked = param.Locked;
 | |
|       }
 | |
|       if ("SPNEGO" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "network.negotiate-auth.trusted-uris",
 | |
|           param.SPNEGO.join(", "),
 | |
|           locked
 | |
|         );
 | |
|       }
 | |
|       if ("Delegated" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "network.negotiate-auth.delegation-uris",
 | |
|           param.Delegated.join(", "),
 | |
|           locked
 | |
|         );
 | |
|       }
 | |
|       if ("NTLM" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "network.automatic-ntlm-auth.trusted-uris",
 | |
|           param.NTLM.join(", "),
 | |
|           locked
 | |
|         );
 | |
|       }
 | |
|       if ("AllowNonFQDN" in param) {
 | |
|         if ("NTLM" in param.AllowNonFQDN) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "network.automatic-ntlm-auth.allow-non-fqdn",
 | |
|             param.AllowNonFQDN.NTLM,
 | |
|             locked
 | |
|           );
 | |
|         }
 | |
|         if ("SPNEGO" in param.AllowNonFQDN) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "network.negotiate-auth.allow-non-fqdn",
 | |
|             param.AllowNonFQDN.SPNEGO,
 | |
|             locked
 | |
|           );
 | |
|         }
 | |
|       }
 | |
|       if ("AllowProxies" in param) {
 | |
|         if ("NTLM" in param.AllowProxies) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "network.automatic-ntlm-auth.allow-proxies",
 | |
|             param.AllowProxies.NTLM,
 | |
|             locked
 | |
|           );
 | |
|         }
 | |
|         if ("SPNEGO" in param.AllowProxies) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "network.negotiate-auth.allow-proxies",
 | |
|             param.AllowProxies.SPNEGO,
 | |
|             locked
 | |
|           );
 | |
|         }
 | |
|       }
 | |
|       if ("PrivateBrowsing" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "network.auth.private-browsing-sso",
 | |
|           param.PrivateBrowsing,
 | |
|           locked
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   AutoLaunchProtocolsFromOrigins: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       for (let info of param) {
 | |
|         addAllowDenyPermissions(
 | |
|           `open-protocol-handler^${info.protocol}`,
 | |
|           info.allowed_origins
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   BackgroundAppUpdate: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("app-background-update-off");
 | |
|       } else {
 | |
|         manager.disallowFeature("app-background-update-on");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   BlockAboutAddons: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         blockAboutPage(manager, "about:addons", true);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   BlockAboutConfig: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         blockAboutPage(manager, "about:config");
 | |
|         setAndLockPref("devtools.chrome.enabled", false);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   BlockAboutProfiles: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         blockAboutPage(manager, "about:profiles");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   BlockAboutSupport: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         blockAboutPage(manager, "about:support");
 | |
|         manager.disallowFeature("aboutSupport");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Bookmarks: {
 | |
|     onAllWindowsRestored(manager, param) {
 | |
|       lazy.BookmarksPolicies.processBookmarks(param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   CaptivePortal: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("network.captive-portal-service.enabled", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Certificates: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("ImportEnterpriseRoots" in param) {
 | |
|         setAndLockPref(
 | |
|           "security.enterprise_roots.enabled",
 | |
|           param.ImportEnterpriseRoots
 | |
|         );
 | |
|       }
 | |
|       if ("Install" in param) {
 | |
|         (async () => {
 | |
|           let dirs = [];
 | |
|           let platform = AppConstants.platform;
 | |
|           if (platform == "win") {
 | |
|             dirs = [
 | |
|               // Ugly, but there is no official way to get %USERNAME\AppData\Roaming\Mozilla.
 | |
|               Services.dirsvc.get("XREUSysExt", Ci.nsIFile).parent,
 | |
|               // Even more ugly, but there is no official way to get %USERNAME\AppData\Local\Mozilla.
 | |
|               Services.dirsvc.get("DefProfLRt", Ci.nsIFile).parent.parent,
 | |
|             ];
 | |
|           } else if (platform == "macosx" || platform == "linux") {
 | |
|             dirs = [
 | |
|               // These two keys are named wrong. They return the Mozilla directory.
 | |
|               Services.dirsvc.get("XREUserNativeManifests", Ci.nsIFile),
 | |
|               Services.dirsvc.get("XRESysNativeManifests", Ci.nsIFile),
 | |
|             ];
 | |
|           }
 | |
|           dirs.unshift(Services.dirsvc.get("XREAppDist", Ci.nsIFile));
 | |
|           for (let certfilename of param.Install) {
 | |
|             let certfile;
 | |
|             try {
 | |
|               certfile = Cc["@mozilla.org/file/local;1"].createInstance(
 | |
|                 Ci.nsIFile
 | |
|               );
 | |
|               certfile.initWithPath(certfilename);
 | |
|             } catch (e) {
 | |
|               for (let dir of dirs) {
 | |
|                 certfile = dir.clone();
 | |
|                 certfile.append(
 | |
|                   platform == "linux" ? "certificates" : "Certificates"
 | |
|                 );
 | |
|                 certfile.append(certfilename);
 | |
|                 if (certfile.exists()) {
 | |
|                   break;
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|             let file;
 | |
|             try {
 | |
|               file = await File.createFromNsIFile(certfile);
 | |
|             } catch (e) {
 | |
|               lazy.log.error(`Unable to find certificate - ${certfilename}`);
 | |
|               continue;
 | |
|             }
 | |
|             let reader = new FileReader();
 | |
|             reader.onloadend = function () {
 | |
|               if (reader.readyState != reader.DONE) {
 | |
|                 lazy.log.error(`Unable to read certificate - ${certfile.path}`);
 | |
|                 return;
 | |
|               }
 | |
|               let certFile = reader.result;
 | |
|               let certFileArray = [];
 | |
|               for (let i = 0; i < certFile.length; i++) {
 | |
|                 certFileArray.push(certFile.charCodeAt(i));
 | |
|               }
 | |
|               let cert;
 | |
|               try {
 | |
|                 cert = lazy.gCertDB.constructX509(certFileArray);
 | |
|               } catch (e) {
 | |
|                 lazy.log.debug(
 | |
|                   `constructX509 failed with error '${e}' - trying constructX509FromBase64.`
 | |
|                 );
 | |
|                 try {
 | |
|                   // It might be PEM instead of DER.
 | |
|                   cert = lazy.gCertDB.constructX509FromBase64(
 | |
|                     pemToBase64(certFile)
 | |
|                   );
 | |
|                 } catch (ex) {
 | |
|                   lazy.log.error(
 | |
|                     `Unable to add certificate - ${certfile.path}`,
 | |
|                     ex
 | |
|                   );
 | |
|                 }
 | |
|               }
 | |
|               if (cert) {
 | |
|                 if (
 | |
|                   lazy.gCertDB.isCertTrusted(
 | |
|                     cert,
 | |
|                     Ci.nsIX509Cert.CA_CERT,
 | |
|                     Ci.nsIX509CertDB.TRUSTED_SSL
 | |
|                   )
 | |
|                 ) {
 | |
|                   // Certificate is already installed.
 | |
|                   return;
 | |
|                 }
 | |
|                 try {
 | |
|                   lazy.gCertDB.addCert(certFile, "CT,CT,");
 | |
|                 } catch (e) {
 | |
|                   // It might be PEM instead of DER.
 | |
|                   lazy.gCertDB.addCertFromBase64(
 | |
|                     pemToBase64(certFile),
 | |
|                     "CT,CT,"
 | |
|                   );
 | |
|                 }
 | |
|               }
 | |
|             };
 | |
|             reader.readAsBinaryString(file);
 | |
|           }
 | |
|         })();
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Containers: {
 | |
|     // Queried directly by ContextualIdentityService.sys.mjs
 | |
|   },
 | |
| 
 | |
|   Cookies: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       addAllowDenyPermissions("cookie", param.Allow, param.Block);
 | |
| 
 | |
|       if (param.AllowSession) {
 | |
|         for (let origin of param.AllowSession) {
 | |
|           try {
 | |
|             Services.perms.addFromPrincipal(
 | |
|               Services.scriptSecurityManager.createContentPrincipalFromOrigin(
 | |
|                 origin
 | |
|               ),
 | |
|               "cookie",
 | |
|               Ci.nsICookiePermission.ACCESS_SESSION,
 | |
|               Ci.nsIPermissionManager.EXPIRE_POLICY
 | |
|             );
 | |
|           } catch (ex) {
 | |
|             lazy.log.error(
 | |
|               `Unable to add cookie session permission - ${origin.href}`
 | |
|             );
 | |
|           }
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (param.Block) {
 | |
|         const hosts = param.Block.map(url => url.hostname)
 | |
|           .sort()
 | |
|           .join("\n");
 | |
|         runOncePerModification("clearCookiesForBlockedHosts", hosts, () => {
 | |
|           for (let blocked of param.Block) {
 | |
|             Services.cookies.removeCookiesWithOriginAttributes(
 | |
|               "{}",
 | |
|               blocked.hostname
 | |
|             );
 | |
|           }
 | |
|         });
 | |
|       }
 | |
| 
 | |
|       if (param.ExpireAtSessionEnd != undefined) {
 | |
|         lazy.log.error(
 | |
|           "'ExpireAtSessionEnd' has been deprecated and it has no effect anymore."
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       // New Cookie Behavior option takes precendence
 | |
|       let defaultPref = Services.prefs.getDefaultBranch("");
 | |
|       let newCookieBehavior = defaultPref.getIntPref(
 | |
|         "network.cookie.cookieBehavior"
 | |
|       );
 | |
|       let newCookieBehaviorPB = defaultPref.getIntPref(
 | |
|         "network.cookie.cookieBehavior.pbmode"
 | |
|       );
 | |
|       if ("Behavior" in param || "BehaviorPrivateBrowsing" in param) {
 | |
|         let behaviors = {
 | |
|           accept: Ci.nsICookieService.BEHAVIOR_ACCEPT,
 | |
|           "reject-foreign": Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN,
 | |
|           reject: Ci.nsICookieService.BEHAVIOR_REJECT,
 | |
|           "limit-foreign": Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN,
 | |
|           "reject-tracker": Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER,
 | |
|           "reject-tracker-and-partition-foreign":
 | |
|             Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
 | |
|         };
 | |
|         if ("Behavior" in param) {
 | |
|           newCookieBehavior = behaviors[param.Behavior];
 | |
|         }
 | |
|         if ("BehaviorPrivateBrowsing" in param) {
 | |
|           newCookieBehaviorPB = behaviors[param.BehaviorPrivateBrowsing];
 | |
|         }
 | |
|       } else {
 | |
|         // Default, AcceptThirdParty, and RejectTracker are being
 | |
|         // deprecated in favor of Behavior. They will continue
 | |
|         // to be supported, though.
 | |
|         if (
 | |
|           param.Default !== undefined ||
 | |
|           param.AcceptThirdParty !== undefined ||
 | |
|           param.RejectTracker !== undefined ||
 | |
|           param.Locked
 | |
|         ) {
 | |
|           newCookieBehavior = Ci.nsICookieService.BEHAVIOR_ACCEPT;
 | |
|           if (param.Default !== undefined && !param.Default) {
 | |
|             newCookieBehavior = Ci.nsICookieService.BEHAVIOR_REJECT;
 | |
|           } else if (param.AcceptThirdParty) {
 | |
|             if (param.AcceptThirdParty == "never") {
 | |
|               newCookieBehavior = Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
 | |
|             } else if (param.AcceptThirdParty == "from-visited") {
 | |
|               newCookieBehavior = Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
 | |
|             }
 | |
|           } else if (param.RejectTracker) {
 | |
|             newCookieBehavior = Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
 | |
|           }
 | |
|         }
 | |
|         // With the old cookie policy, we made private browsing the same.
 | |
|         newCookieBehaviorPB = newCookieBehavior;
 | |
|       }
 | |
|       // We set the values no matter what just in case the policy was only used to lock.
 | |
|       PoliciesUtils.setDefaultPref(
 | |
|         "network.cookie.cookieBehavior",
 | |
|         newCookieBehavior,
 | |
|         param.Locked
 | |
|       );
 | |
|       PoliciesUtils.setDefaultPref(
 | |
|         "network.cookie.cookieBehavior.pbmode",
 | |
|         newCookieBehaviorPB,
 | |
|         param.Locked
 | |
|       );
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DefaultDownloadDirectory: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       PoliciesUtils.setDefaultPref(
 | |
|         "browser.download.dir",
 | |
|         replacePathVariables(param)
 | |
|       );
 | |
|       // If a custom download directory is being used, just lock folder list to 2.
 | |
|       setAndLockPref("browser.download.folderList", 2);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableAccounts: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("identity.fxaccounts.enabled", false);
 | |
|         setAndLockPref("browser.aboutwelcome.enabled", false);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableAppUpdate: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("appUpdate");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableBuiltinPDFViewer: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("pdfjs.disabled", true);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisabledCiphers: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       let cipherPrefs = {
 | |
|         TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
 | |
|           "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256",
 | |
|         TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
 | |
|           "security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256",
 | |
|         TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
 | |
|           "security.ssl3.ecdhe_ecdsa_chacha20_poly1305_sha256",
 | |
|         TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
 | |
|           "security.ssl3.ecdhe_rsa_chacha20_poly1305_sha256",
 | |
|         TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
 | |
|           "security.ssl3.ecdhe_ecdsa_aes_256_gcm_sha384",
 | |
|         TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
 | |
|           "security.ssl3.ecdhe_rsa_aes_256_gcm_sha384",
 | |
|         TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
 | |
|           "security.ssl3.ecdhe_rsa_aes_128_sha",
 | |
|         TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
 | |
|           "security.ssl3.ecdhe_ecdsa_aes_128_sha",
 | |
|         TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
 | |
|           "security.ssl3.ecdhe_rsa_aes_256_sha",
 | |
|         TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
 | |
|           "security.ssl3.ecdhe_ecdsa_aes_256_sha",
 | |
|         TLS_DHE_RSA_WITH_AES_128_CBC_SHA: "security.ssl3.dhe_rsa_aes_128_sha",
 | |
|         TLS_DHE_RSA_WITH_AES_256_CBC_SHA: "security.ssl3.dhe_rsa_aes_256_sha",
 | |
|         TLS_RSA_WITH_AES_128_GCM_SHA256: "security.ssl3.rsa_aes_128_gcm_sha256",
 | |
|         TLS_RSA_WITH_AES_256_GCM_SHA384: "security.ssl3.rsa_aes_256_gcm_sha384",
 | |
|         TLS_RSA_WITH_AES_128_CBC_SHA: "security.ssl3.rsa_aes_128_sha",
 | |
|         TLS_RSA_WITH_AES_256_CBC_SHA: "security.ssl3.rsa_aes_256_sha",
 | |
|         TLS_RSA_WITH_3DES_EDE_CBC_SHA:
 | |
|           "security.ssl3.deprecated.rsa_des_ede3_sha",
 | |
|       };
 | |
| 
 | |
|       for (let cipher in param) {
 | |
|         setAndLockPref(cipherPrefs[cipher], !param[cipher]);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableDefaultBrowserAgent: {
 | |
|     // The implementation of this policy is in the default browser agent itself
 | |
|     // (/toolkit/mozapps/defaultagent); we need an entry for it here so that it
 | |
|     // shows up in about:policies as a real policy and not as an error.
 | |
|   },
 | |
| 
 | |
|   DisableDeveloperTools: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("devtools.policy.disabled", true);
 | |
|         setAndLockPref("devtools.chrome.enabled", false);
 | |
| 
 | |
|         manager.disallowFeature("devtools");
 | |
|         blockAboutPage(manager, "about:debugging");
 | |
|         blockAboutPage(manager, "about:devtools-toolbox");
 | |
|         blockAboutPage(manager, "about:profiling");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableFeedbackCommands: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("feedbackCommands");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableFirefoxAccounts: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       // If DisableAccounts is set, let it take precedence.
 | |
|       if ("DisableAccounts" in manager.getActivePolicies()) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       if (param) {
 | |
|         setAndLockPref("identity.fxaccounts.enabled", false);
 | |
|         setAndLockPref("browser.aboutwelcome.enabled", false);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableFirefoxScreenshots: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("extensions.screenshots.disabled", true);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableFirefoxStudies: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("Shield");
 | |
|         setAndLockPref(
 | |
|           "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
 | |
|           false
 | |
|         );
 | |
|         setAndLockPref(
 | |
|           "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
 | |
|           false
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableForgetButton: {
 | |
|     onProfileAfterChange(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("privacy.panicButton.enabled", false);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableFormHistory: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("browser.formfill.enable", false);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableMasterPasswordCreation: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("createMasterPassword");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisablePasswordReveal: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("passwordReveal");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisablePocket: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("extensions.pocket.enabled", false);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisablePrivateBrowsing: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("privatebrowsing");
 | |
|         blockAboutPage(manager, "about:privatebrowsing", true);
 | |
|         setAndLockPref("browser.privatebrowsing.autostart", false);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableProfileImport: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("profileImport");
 | |
|         setAndLockPref(
 | |
|           "browser.newtabpage.activity-stream.migrationExpired",
 | |
|           true
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableProfileRefresh: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("profileRefresh");
 | |
|         setAndLockPref("browser.disableResetPrompt", true);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableSafeMode: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("safeMode");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableSecurityBypass: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if ("InvalidCertificate" in param) {
 | |
|         setAndLockPref(
 | |
|           "security.certerror.hideAddException",
 | |
|           param.InvalidCertificate
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       if ("SafeBrowsing" in param) {
 | |
|         setAndLockPref(
 | |
|           "browser.safebrowsing.allowOverride",
 | |
|           !param.SafeBrowsing
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableSetDesktopBackground: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("setDesktopBackground");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableSystemAddonUpdate: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("SysAddonUpdate");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableTelemetry: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         setAndLockPref("datareporting.healthreport.uploadEnabled", false);
 | |
|         setAndLockPref("datareporting.policy.dataSubmissionEnabled", false);
 | |
|         setAndLockPref("toolkit.telemetry.archive.enabled", false);
 | |
|         blockAboutPage(manager, "about:telemetry");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisableThirdPartyModuleBlocking: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("thirdPartyModuleBlocking");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisplayBookmarksToolbar: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       let visibility;
 | |
|       if (typeof param === "boolean") {
 | |
|         visibility = param ? "always" : "newtab";
 | |
|       } else {
 | |
|         visibility = param;
 | |
|       }
 | |
|       // This policy is meant to change the default behavior, not to force it.
 | |
|       // If this policy was already applied and the user chose to re-hide the
 | |
|       // bookmarks toolbar, do not show it again.
 | |
|       runOncePerModification("displayBookmarksToolbar", visibility, () => {
 | |
|         let visibilityPref = "browser.toolbars.bookmarks.visibility";
 | |
|         Services.prefs.setCharPref(visibilityPref, visibility);
 | |
|       });
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DisplayMenuBar: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       let value;
 | |
|       if (
 | |
|         typeof param === "boolean" ||
 | |
|         param == "default-on" ||
 | |
|         param == "default-off"
 | |
|       ) {
 | |
|         switch (param) {
 | |
|           case "default-on":
 | |
|             value = "false";
 | |
|             break;
 | |
|           case "default-off":
 | |
|             value = "true";
 | |
|             break;
 | |
|           default:
 | |
|             value = (!param).toString();
 | |
|             break;
 | |
|         }
 | |
|         // This policy is meant to change the default behavior, not to force it.
 | |
|         // If this policy was already applied and the user chose to re-hide the
 | |
|         // menu bar, do not show it again.
 | |
|         runOncePerModification("displayMenuBar", value, () => {
 | |
|           Services.xulStore.setValue(
 | |
|             BROWSER_DOCUMENT_URL,
 | |
|             "toolbar-menubar",
 | |
|             "autohide",
 | |
|             value
 | |
|           );
 | |
|         });
 | |
|       } else {
 | |
|         switch (param) {
 | |
|           case "always":
 | |
|             value = "false";
 | |
|             break;
 | |
|           case "never":
 | |
|             // Make sure Alt key doesn't show the menubar
 | |
|             setAndLockPref("ui.key.menuAccessKeyFocuses", false);
 | |
|             value = "true";
 | |
|             break;
 | |
|         }
 | |
|         Services.xulStore.setValue(
 | |
|           BROWSER_DOCUMENT_URL,
 | |
|           "toolbar-menubar",
 | |
|           "autohide",
 | |
|           value
 | |
|         );
 | |
|         manager.disallowFeature("hideShowMenuBar");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DNSOverHTTPS: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("Enabled" in param) {
 | |
|         let mode = param.Enabled ? 2 : 5;
 | |
|         PoliciesUtils.setDefaultPref("network.trr.mode", mode, param.Locked);
 | |
|       }
 | |
|       if ("ProviderURL" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "network.trr.uri",
 | |
|           param.ProviderURL.href,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("ExcludedDomains" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "network.trr.excluded-domains",
 | |
|           param.ExcludedDomains.join(","),
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DontCheckDefaultBrowser: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       setAndLockPref("browser.shell.checkDefaultBrowser", !param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   DownloadDirectory: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("browser.download.dir", replacePathVariables(param));
 | |
|       // If a custom download directory is being used, just lock folder list to 2.
 | |
|       setAndLockPref("browser.download.folderList", 2);
 | |
|       // Per Chrome spec, user can't choose to download every time
 | |
|       // if this is set.
 | |
|       setAndLockPref("browser.download.useDownloadDir", true);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   EnableTrackingProtection: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param.Value) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "privacy.trackingprotection.enabled",
 | |
|           true,
 | |
|           param.Locked
 | |
|         );
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "privacy.trackingprotection.pbmode.enabled",
 | |
|           true,
 | |
|           param.Locked
 | |
|         );
 | |
|       } else {
 | |
|         setAndLockPref("privacy.trackingprotection.enabled", false);
 | |
|         setAndLockPref("privacy.trackingprotection.pbmode.enabled", false);
 | |
|       }
 | |
|       if ("Cryptomining" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "privacy.trackingprotection.cryptomining.enabled",
 | |
|           param.Cryptomining,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("Fingerprinting" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "privacy.trackingprotection.fingerprinting.enabled",
 | |
|           param.Fingerprinting,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("EmailTracking" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "privacy.trackingprotection.emailtracking.enabled",
 | |
|           param.EmailTracking,
 | |
|           param.Locked
 | |
|         );
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "privacy.trackingprotection.emailtracking.pbmode.enabled",
 | |
|           param.EmailTracking,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("Exceptions" in param) {
 | |
|         addAllowDenyPermissions("trackingprotection", param.Exceptions);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   EncryptedMediaExtensions: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("Enabled" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "media.eme.enabled",
 | |
|           param.Enabled,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   ExemptDomainFileTypePairsFromFileTypeDownloadWarnings: {
 | |
|     // This policy is handled directly in EnterprisePoliciesParent.jsm
 | |
|     // and requires no validation (It's done by the schema).
 | |
|   },
 | |
| 
 | |
|   Extensions: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       let uninstallingPromise = Promise.resolve();
 | |
|       if ("Uninstall" in param) {
 | |
|         uninstallingPromise = runOncePerModification(
 | |
|           "extensionsUninstall",
 | |
|           JSON.stringify(param.Uninstall),
 | |
|           async () => {
 | |
|             // If we're uninstalling add-ons, re-run the extensionsInstall runOnce even if it hasn't
 | |
|             // changed, which will allow add-ons to be updated.
 | |
|             Services.prefs.clearUserPref(
 | |
|               "browser.policies.runOncePerModification.extensionsInstall"
 | |
|             );
 | |
|             let addons = await lazy.AddonManager.getAddonsByIDs(
 | |
|               param.Uninstall
 | |
|             );
 | |
|             for (let addon of addons) {
 | |
|               if (addon) {
 | |
|                 try {
 | |
|                   await addon.uninstall();
 | |
|                 } catch (e) {
 | |
|                   // This can fail for add-ons that can't be uninstalled.
 | |
|                   lazy.log.debug(
 | |
|                     `Add-on ID (${addon.id}) couldn't be uninstalled.`
 | |
|                   );
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         );
 | |
|       }
 | |
|       if ("Install" in param) {
 | |
|         runOncePerModification(
 | |
|           "extensionsInstall",
 | |
|           JSON.stringify(param.Install),
 | |
|           async () => {
 | |
|             await uninstallingPromise;
 | |
|             for (let location of param.Install) {
 | |
|               let uri;
 | |
|               try {
 | |
|                 // We need to try as a file first because
 | |
|                 // Windows paths are valid URIs.
 | |
|                 // This is done for legacy support (old API)
 | |
|                 let xpiFile = new lazy.FileUtils.File(location);
 | |
|                 uri = Services.io.newFileURI(xpiFile);
 | |
|               } catch (e) {
 | |
|                 uri = Services.io.newURI(location);
 | |
|               }
 | |
|               installAddonFromURL(uri.spec);
 | |
|             }
 | |
|           }
 | |
|         );
 | |
|       }
 | |
|       if ("Locked" in param) {
 | |
|         for (let ID of param.Locked) {
 | |
|           manager.disallowFeature(`uninstall-extension:${ID}`);
 | |
|           manager.disallowFeature(`disable-extension:${ID}`);
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   ExtensionSettings: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       try {
 | |
|         manager.setExtensionSettings(param);
 | |
|       } catch (e) {
 | |
|         lazy.log.error("Invalid ExtensionSettings");
 | |
|       }
 | |
|     },
 | |
|     async onBeforeUIStartup(manager, param) {
 | |
|       let extensionSettings = param;
 | |
|       let blockAllExtensions = false;
 | |
|       if ("*" in extensionSettings) {
 | |
|         if (
 | |
|           "installation_mode" in extensionSettings["*"] &&
 | |
|           extensionSettings["*"].installation_mode == "blocked"
 | |
|         ) {
 | |
|           blockAllExtensions = true;
 | |
|           // Turn off discovery pane in about:addons
 | |
|           setAndLockPref("extensions.getAddons.showPane", false);
 | |
|           // Turn off recommendations
 | |
|           setAndLockPref(
 | |
|             "extensions.htmlaboutaddons.recommendations.enable",
 | |
|             false
 | |
|           );
 | |
|           // Block about:debugging
 | |
|           blockAboutPage(manager, "about:debugging");
 | |
|         }
 | |
|         if ("restricted_domains" in extensionSettings["*"]) {
 | |
|           let restrictedDomains = Services.prefs
 | |
|             .getCharPref("extensions.webextensions.restrictedDomains")
 | |
|             .split(",");
 | |
|           setAndLockPref(
 | |
|             "extensions.webextensions.restrictedDomains",
 | |
|             restrictedDomains
 | |
|               .concat(extensionSettings["*"].restricted_domains)
 | |
|               .join(",")
 | |
|           );
 | |
|         }
 | |
|       }
 | |
|       let addons = await lazy.AddonManager.getAllAddons();
 | |
|       let allowedExtensions = [];
 | |
|       for (let extensionID in extensionSettings) {
 | |
|         if (extensionID == "*") {
 | |
|           // Ignore global settings
 | |
|           continue;
 | |
|         }
 | |
|         if ("installation_mode" in extensionSettings[extensionID]) {
 | |
|           if (
 | |
|             extensionSettings[extensionID].installation_mode ==
 | |
|               "force_installed" ||
 | |
|             extensionSettings[extensionID].installation_mode ==
 | |
|               "normal_installed"
 | |
|           ) {
 | |
|             if (!extensionSettings[extensionID].install_url) {
 | |
|               throw new Error(`Missing install_url for ${extensionID}`);
 | |
|             }
 | |
|             installAddonFromURL(
 | |
|               extensionSettings[extensionID].install_url,
 | |
|               extensionID,
 | |
|               addons.find(addon => addon.id == extensionID)
 | |
|             );
 | |
|             manager.disallowFeature(`uninstall-extension:${extensionID}`);
 | |
|             if (
 | |
|               extensionSettings[extensionID].installation_mode ==
 | |
|               "force_installed"
 | |
|             ) {
 | |
|               manager.disallowFeature(`disable-extension:${extensionID}`);
 | |
|             }
 | |
|             allowedExtensions.push(extensionID);
 | |
|           } else if (
 | |
|             extensionSettings[extensionID].installation_mode == "allowed"
 | |
|           ) {
 | |
|             allowedExtensions.push(extensionID);
 | |
|           } else if (
 | |
|             extensionSettings[extensionID].installation_mode == "blocked"
 | |
|           ) {
 | |
|             if (addons.find(addon => addon.id == extensionID)) {
 | |
|               // Can't use the addon from getActiveAddons since it doesn't have uninstall.
 | |
|               let addon = await lazy.AddonManager.getAddonByID(extensionID);
 | |
|               try {
 | |
|                 await addon.uninstall();
 | |
|               } catch (e) {
 | |
|                 // This can fail for add-ons that can't be uninstalled.
 | |
|                 lazy.log.debug(
 | |
|                   `Add-on ID (${addon.id}) couldn't be uninstalled.`
 | |
|                 );
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       if (blockAllExtensions) {
 | |
|         for (let addon of addons) {
 | |
|           if (
 | |
|             addon.isSystem ||
 | |
|             addon.isBuiltin ||
 | |
|             !(addon.scope & lazy.AddonManager.SCOPE_PROFILE)
 | |
|           ) {
 | |
|             continue;
 | |
|           }
 | |
|           if (!allowedExtensions.includes(addon.id)) {
 | |
|             try {
 | |
|               // Can't use the addon from getActiveAddons since it doesn't have uninstall.
 | |
|               let addonToUninstall = await lazy.AddonManager.getAddonByID(
 | |
|                 addon.id
 | |
|               );
 | |
|               await addonToUninstall.uninstall();
 | |
|             } catch (e) {
 | |
|               // This can fail for add-ons that can't be uninstalled.
 | |
|               lazy.log.debug(
 | |
|                 `Add-on ID (${addon.id}) couldn't be uninstalled.`
 | |
|               );
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   ExtensionUpdate: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (!param) {
 | |
|         setAndLockPref("extensions.update.enabled", param);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   FirefoxHome: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("Search" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.showSearch",
 | |
|           param.Search,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("TopSites" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.feeds.topsites",
 | |
|           param.TopSites,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("SponsoredTopSites" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.showSponsoredTopSites",
 | |
|           param.SponsoredTopSites,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("Highlights" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.feeds.section.highlights",
 | |
|           param.Highlights,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("Pocket" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.feeds.system.topstories",
 | |
|           param.Pocket,
 | |
|           param.Locked
 | |
|         );
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.feeds.section.topstories",
 | |
|           param.Pocket,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("SponsoredPocket" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.showSponsored",
 | |
|           param.SponsoredPocket,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("Snippets" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.feeds.snippets",
 | |
|           param.Snippets,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   FirefoxSuggest: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       (async () => {
 | |
|         await lazy.UrlbarPrefs.firefoxSuggestScenarioStartupPromise;
 | |
|         if ("WebSuggestions" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "browser.urlbar.suggest.quicksuggest.nonsponsored",
 | |
|             param.WebSuggestions,
 | |
|             param.Locked
 | |
|           );
 | |
|         }
 | |
|         if ("SponsoredSuggestions" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "browser.urlbar.suggest.quicksuggest.sponsored",
 | |
|             param.SponsoredSuggestions,
 | |
|             param.Locked
 | |
|           );
 | |
|         }
 | |
|         if ("ImproveSuggest" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "browser.urlbar.quicksuggest.dataCollection.enabled",
 | |
|             param.ImproveSuggest,
 | |
|             param.Locked
 | |
|           );
 | |
|         }
 | |
|       })();
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   GoToIntranetSiteForSingleWordEntryInAddressBar: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("browser.fixup.dns_first_for_single_words", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Handlers: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("mimeTypes" in param) {
 | |
|         for (let mimeType in param.mimeTypes) {
 | |
|           let mimeInfo = param.mimeTypes[mimeType];
 | |
|           let realMIMEInfo = lazy.gMIMEService.getFromTypeAndExtension(
 | |
|             mimeType,
 | |
|             ""
 | |
|           );
 | |
|           processMIMEInfo(mimeInfo, realMIMEInfo);
 | |
|         }
 | |
|       }
 | |
|       if ("extensions" in param) {
 | |
|         for (let extension in param.extensions) {
 | |
|           let mimeInfo = param.extensions[extension];
 | |
|           try {
 | |
|             let realMIMEInfo = lazy.gMIMEService.getFromTypeAndExtension(
 | |
|               "",
 | |
|               extension
 | |
|             );
 | |
|             processMIMEInfo(mimeInfo, realMIMEInfo);
 | |
|           } catch (e) {
 | |
|             lazy.log.error(`Invalid file extension (${extension})`);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|       if ("schemes" in param) {
 | |
|         for (let scheme in param.schemes) {
 | |
|           let handlerInfo = param.schemes[scheme];
 | |
|           let realHandlerInfo =
 | |
|             lazy.gExternalProtocolService.getProtocolHandlerInfo(scheme);
 | |
|           processMIMEInfo(handlerInfo, realHandlerInfo);
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   HardwareAcceleration: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (!param) {
 | |
|         setAndLockPref("layers.acceleration.disabled", true);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Homepage: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if ("StartPage" in param && param.StartPage == "none") {
 | |
|         // For blank startpage, we use about:blank rather
 | |
|         // than messing with browser.startup.page
 | |
|         param.URL = new URL("about:blank");
 | |
|       }
 | |
|       // |homepages| will be a string containing a pipe-separated ('|') list of
 | |
|       // URLs because that is what the "Home page" section of about:preferences
 | |
|       // (and therefore what the pref |browser.startup.homepage|) accepts.
 | |
|       if ("URL" in param) {
 | |
|         let homepages = param.URL.href;
 | |
|         if (param.Additional && param.Additional.length) {
 | |
|           homepages += "|" + param.Additional.map(url => url.href).join("|");
 | |
|         }
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.startup.homepage",
 | |
|           homepages,
 | |
|           param.Locked
 | |
|         );
 | |
|         if (param.Locked) {
 | |
|           setAndLockPref(
 | |
|             "pref.browser.homepage.disable_button.current_page",
 | |
|             true
 | |
|           );
 | |
|           setAndLockPref(
 | |
|             "pref.browser.homepage.disable_button.bookmark_page",
 | |
|             true
 | |
|           );
 | |
|           setAndLockPref(
 | |
|             "pref.browser.homepage.disable_button.restore_default",
 | |
|             true
 | |
|           );
 | |
|         } else {
 | |
|           // Clear out old run once modification that is no longer used.
 | |
|           clearRunOnceModification("setHomepage");
 | |
|         }
 | |
|         // If a homepage has been set via policy, show the home button
 | |
|         if (param.URL != "about:blank") {
 | |
|           manager.disallowFeature("removeHomeButtonByDefault");
 | |
|         }
 | |
|       }
 | |
|       if (param.StartPage) {
 | |
|         let prefValue;
 | |
|         switch (param.StartPage) {
 | |
|           case "homepage":
 | |
|           case "homepage-locked":
 | |
|           case "none":
 | |
|             prefValue = 1;
 | |
|             break;
 | |
|           case "previous-session":
 | |
|             prefValue = 3;
 | |
|             break;
 | |
|         }
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.startup.page",
 | |
|           prefValue,
 | |
|           param.StartPage == "homepage-locked"
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   InstallAddonsPermission: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if ("Allow" in param) {
 | |
|         addAllowDenyPermissions("install", param.Allow, null);
 | |
|       }
 | |
|       if ("Default" in param) {
 | |
|         setAndLockPref("xpinstall.enabled", param.Default);
 | |
|         if (!param.Default) {
 | |
|           blockAboutPage(manager, "about:debugging");
 | |
|           setAndLockPref(
 | |
|             "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
 | |
|             false
 | |
|           );
 | |
|           setAndLockPref(
 | |
|             "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
 | |
|             false
 | |
|           );
 | |
|           manager.disallowFeature("xpinstall");
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   LegacyProfiles: {
 | |
|     // Handled in nsToolkitProfileService.cpp (Windows only)
 | |
|   },
 | |
| 
 | |
|   LegacySameSiteCookieBehaviorEnabled: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       PoliciesUtils.setDefaultPref(
 | |
|         "network.cookie.sameSite.laxByDefault",
 | |
|         !param
 | |
|       );
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   LegacySameSiteCookieBehaviorEnabledForDomainList: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       PoliciesUtils.setDefaultPref(
 | |
|         "network.cookie.sameSite.laxByDefault.disabledHosts",
 | |
|         param.join(",")
 | |
|       );
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   LocalFileLinks: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       // If there are existing capabilities, lock them with the policy pref.
 | |
|       let policyNames = Services.prefs
 | |
|         .getCharPref("capability.policy.policynames", "")
 | |
|         .split(" ");
 | |
|       policyNames.push("localfilelinks_policy");
 | |
|       setAndLockPref("capability.policy.policynames", policyNames.join(" "));
 | |
|       setAndLockPref(
 | |
|         "capability.policy.localfilelinks_policy.checkloaduri.enabled",
 | |
|         "allAccess"
 | |
|       );
 | |
|       setAndLockPref(
 | |
|         "capability.policy.localfilelinks_policy.sites",
 | |
|         param.join(" ")
 | |
|       );
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   ManagedBookmarks: {},
 | |
| 
 | |
|   ManualAppUpdateOnly: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("autoAppUpdateChecking");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   NetworkPrediction: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("network.dns.disablePrefetch", !param);
 | |
|       setAndLockPref("network.dns.disablePrefetchFromHTTPS", !param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   NewTabPage: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("browser.newtabpage.enabled", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   NoDefaultBookmarks: {
 | |
|     onProfileAfterChange(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("defaultBookmarks");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   OfferToSaveLogins: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       setAndLockPref("signon.rememberSignons", param);
 | |
|       setAndLockPref("services.passwordSavingEnabled", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   OfferToSaveLoginsDefault: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       let policies = Services.policies.getActivePolicies();
 | |
|       if ("OfferToSaveLogins" in policies) {
 | |
|         lazy.log.error(
 | |
|           `OfferToSaveLoginsDefault ignored because OfferToSaveLogins is present.`
 | |
|         );
 | |
|       } else {
 | |
|         PoliciesUtils.setDefaultPref("signon.rememberSignons", param);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   OverrideFirstRunPage: {
 | |
|     onProfileAfterChange(manager, param) {
 | |
|       let url = param ? param : "";
 | |
|       setAndLockPref("startup.homepage_welcome_url", url);
 | |
|       setAndLockPref("browser.aboutwelcome.enabled", false);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   OverridePostUpdatePage: {
 | |
|     onProfileAfterChange(manager, param) {
 | |
|       let url = param ? param.href : "";
 | |
|       setAndLockPref("startup.homepage_override_url", url);
 | |
|       // The pref startup.homepage_override_url is only used
 | |
|       // as a fallback when the update.xml file hasn't provided
 | |
|       // a specific post-update URL.
 | |
|       manager.disallowFeature("postUpdateCustomPage");
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   PasswordManagerEnabled: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (!param) {
 | |
|         blockAboutPage(manager, "about:logins", true);
 | |
|         setAndLockPref("pref.privacy.disable_button.view_passwords", true);
 | |
|       }
 | |
|       setAndLockPref("signon.rememberSignons", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   PasswordManagerExceptions: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       addAllowDenyPermissions("login-saving", null, param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   PDFjs: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("Enabled" in param) {
 | |
|         setAndLockPref("pdfjs.disabled", !param.Enabled);
 | |
|       }
 | |
|       if ("EnablePermissions" in param) {
 | |
|         setAndLockPref("pdfjs.enablePermissions", param.EnablePermissions);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Permissions: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param.Camera) {
 | |
|         addAllowDenyPermissions(
 | |
|           "camera",
 | |
|           param.Camera.Allow,
 | |
|           param.Camera.Block
 | |
|         );
 | |
|         setDefaultPermission("camera", param.Camera);
 | |
|       }
 | |
| 
 | |
|       if (param.Microphone) {
 | |
|         addAllowDenyPermissions(
 | |
|           "microphone",
 | |
|           param.Microphone.Allow,
 | |
|           param.Microphone.Block
 | |
|         );
 | |
|         setDefaultPermission("microphone", param.Microphone);
 | |
|       }
 | |
| 
 | |
|       if (param.Autoplay) {
 | |
|         addAllowDenyPermissions(
 | |
|           "autoplay-media",
 | |
|           param.Autoplay.Allow,
 | |
|           param.Autoplay.Block
 | |
|         );
 | |
|         if ("Default" in param.Autoplay) {
 | |
|           let prefValue;
 | |
|           switch (param.Autoplay.Default) {
 | |
|             case "allow-audio-video":
 | |
|               prefValue = 0;
 | |
|               break;
 | |
|             case "block-audio":
 | |
|               prefValue = 1;
 | |
|               break;
 | |
|             case "block-audio-video":
 | |
|               prefValue = 5;
 | |
|               break;
 | |
|           }
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "media.autoplay.default",
 | |
|             prefValue,
 | |
|             param.Autoplay.Locked
 | |
|           );
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (param.Location) {
 | |
|         addAllowDenyPermissions(
 | |
|           "geo",
 | |
|           param.Location.Allow,
 | |
|           param.Location.Block
 | |
|         );
 | |
|         setDefaultPermission("geo", param.Location);
 | |
|       }
 | |
| 
 | |
|       if (param.Notifications) {
 | |
|         addAllowDenyPermissions(
 | |
|           "desktop-notification",
 | |
|           param.Notifications.Allow,
 | |
|           param.Notifications.Block
 | |
|         );
 | |
|         setDefaultPermission("desktop-notification", param.Notifications);
 | |
|       }
 | |
| 
 | |
|       if ("VirtualReality" in param) {
 | |
|         addAllowDenyPermissions(
 | |
|           "xr",
 | |
|           param.VirtualReality.Allow,
 | |
|           param.VirtualReality.Block
 | |
|         );
 | |
|         setDefaultPermission("xr", param.VirtualReality);
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   PictureInPicture: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("Enabled" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "media.videocontrols.picture-in-picture.video-toggle.enabled",
 | |
|           param.Enabled
 | |
|         );
 | |
|       }
 | |
|       if (param.Locked) {
 | |
|         Services.prefs.lockPref(
 | |
|           "media.videocontrols.picture-in-picture.video-toggle.enabled"
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   PopupBlocking: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       addAllowDenyPermissions("popup", param.Allow, null);
 | |
| 
 | |
|       if (param.Locked) {
 | |
|         let blockValue = true;
 | |
|         if (param.Default !== undefined && !param.Default) {
 | |
|           blockValue = false;
 | |
|         }
 | |
|         setAndLockPref("dom.disable_open_during_load", blockValue);
 | |
|       } else if (param.Default !== undefined) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "dom.disable_open_during_load",
 | |
|           !!param.Default
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Preferences: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       let allowedPrefixes = [
 | |
|         "accessibility.",
 | |
|         "app.update.",
 | |
|         "browser.",
 | |
|         "datareporting.policy.",
 | |
|         "dom.",
 | |
|         "extensions.",
 | |
|         "general.autoScroll",
 | |
|         "general.smoothScroll",
 | |
|         "geo.",
 | |
|         "gfx.",
 | |
|         "intl.",
 | |
|         "keyword.enabled",
 | |
|         "layers.",
 | |
|         "layout.",
 | |
|         "media.",
 | |
|         "network.",
 | |
|         "pdfjs.",
 | |
|         "places.",
 | |
|         "print.",
 | |
|         "signon.",
 | |
|         "spellchecker.",
 | |
|         "toolkit.legacyUserProfileCustomizations.stylesheets",
 | |
|         "ui.",
 | |
|         "widget.",
 | |
|         "xpinstall.whitelist.required",
 | |
|       ];
 | |
|       if (!AppConstants.MOZ_REQUIRE_SIGNING) {
 | |
|         allowedPrefixes.push("xpinstall.signatures.required");
 | |
|       }
 | |
|       const allowedSecurityPrefs = [
 | |
|         "security.block_fileuri_script_with_wrong_mime",
 | |
|         "security.default_personal_cert",
 | |
|         "security.insecure_connection_text.enabled",
 | |
|         "security.insecure_connection_text.pbmode.enabled",
 | |
|         "security.mixed_content.block_active_content",
 | |
|         "security.osclientcerts.assume_rsa_pss_support",
 | |
|         "security.osclientcerts.autoload",
 | |
|         "security.OCSP.enabled",
 | |
|         "security.OCSP.require",
 | |
|         "security.ssl.enable_ocsp_stapling",
 | |
|         "security.ssl.errorReporting.enabled",
 | |
|         "security.ssl.require_safe_negotiation",
 | |
|         "security.tls.enable_0rtt_data",
 | |
|         "security.tls.hello_downgrade_check",
 | |
|         "security.tls.version.enable-deprecated",
 | |
|         "security.warn_submit_secure_to_insecure",
 | |
|       ];
 | |
|       const blockedPrefs = [
 | |
|         "app.update.channel",
 | |
|         "app.update.lastUpdateTime",
 | |
|         "app.update.migrated",
 | |
|         "browser.vpn_promo.disallowed_regions",
 | |
|       ];
 | |
| 
 | |
|       for (let preference in param) {
 | |
|         if (blockedPrefs.includes(preference)) {
 | |
|           lazy.log.error(
 | |
|             `Unable to set preference ${preference}. Preference not allowed for security reasons.`
 | |
|           );
 | |
|           continue;
 | |
|         }
 | |
|         if (preference.startsWith("security.")) {
 | |
|           if (!allowedSecurityPrefs.includes(preference)) {
 | |
|             lazy.log.error(
 | |
|               `Unable to set preference ${preference}. Preference not allowed for security reasons.`
 | |
|             );
 | |
|             continue;
 | |
|           }
 | |
|         } else if (
 | |
|           !allowedPrefixes.some(prefix => preference.startsWith(prefix))
 | |
|         ) {
 | |
|           lazy.log.error(
 | |
|             `Unable to set preference ${preference}. Preference not allowed for stability reasons.`
 | |
|           );
 | |
|           continue;
 | |
|         }
 | |
|         if (typeof param[preference] != "object") {
 | |
|           // Legacy policy preferences
 | |
|           setAndLockPref(preference, param[preference]);
 | |
|         } else {
 | |
|           if (param[preference].Status == "clear") {
 | |
|             Services.prefs.clearUserPref(preference);
 | |
|             continue;
 | |
|           }
 | |
| 
 | |
|           let prefBranch;
 | |
|           if (param[preference].Status == "user") {
 | |
|             prefBranch = Services.prefs;
 | |
|           } else {
 | |
|             prefBranch = Services.prefs.getDefaultBranch("");
 | |
|           }
 | |
| 
 | |
|           // Prefs that were previously locked should stay locked,
 | |
|           // but policy can update the value.
 | |
|           let prefWasLocked = Services.prefs.prefIsLocked(preference);
 | |
|           if (prefWasLocked) {
 | |
|             Services.prefs.unlockPref(preference);
 | |
|           }
 | |
|           try {
 | |
|             switch (typeof param[preference].Value) {
 | |
|               case "boolean":
 | |
|                 prefBranch.setBoolPref(preference, param[preference].Value);
 | |
|                 break;
 | |
| 
 | |
|               case "number":
 | |
|                 if (!Number.isInteger(param[preference].Value)) {
 | |
|                   throw new Error(`Non-integer value for ${preference}`);
 | |
|                 }
 | |
| 
 | |
|                 // This is ugly, but necessary. On Windows GPO and macOS
 | |
|                 // configs, booleans are converted to 0/1. In the previous
 | |
|                 // Preferences implementation, the schema took care of
 | |
|                 // automatically converting these values to booleans.
 | |
|                 // Since we allow arbitrary prefs now, we have to do
 | |
|                 // something different. See bug 1666836.
 | |
|                 // Even uglier, because pdfjs prefs are set async, we need
 | |
|                 // to get their type from PdfJsDefaultPreferences.
 | |
|                 if (preference.startsWith("pdfjs.")) {
 | |
|                   let preferenceTail = preference.replace("pdfjs.", "");
 | |
|                   if (
 | |
|                     preferenceTail in lazy.PdfJsDefaultPreferences &&
 | |
|                     typeof lazy.PdfJsDefaultPreferences[preferenceTail] ==
 | |
|                       "number"
 | |
|                   ) {
 | |
|                     prefBranch.setIntPref(preference, param[preference].Value);
 | |
|                   } else {
 | |
|                     prefBranch.setBoolPref(
 | |
|                       preference,
 | |
|                       !!param[preference].Value
 | |
|                     );
 | |
|                   }
 | |
|                 } else if (
 | |
|                   prefBranch.getPrefType(preference) == prefBranch.PREF_INT ||
 | |
|                   ![0, 1].includes(param[preference].Value)
 | |
|                 ) {
 | |
|                   prefBranch.setIntPref(preference, param[preference].Value);
 | |
|                 } else {
 | |
|                   prefBranch.setBoolPref(preference, !!param[preference].Value);
 | |
|                 }
 | |
|                 break;
 | |
| 
 | |
|               case "string":
 | |
|                 prefBranch.setStringPref(preference, param[preference].Value);
 | |
|                 break;
 | |
|             }
 | |
|           } catch (e) {
 | |
|             lazy.log.error(
 | |
|               `Unable to set preference ${preference}. Probable type mismatch.`
 | |
|             );
 | |
|           }
 | |
| 
 | |
|           if (param[preference].Status == "locked" || prefWasLocked) {
 | |
|             Services.prefs.lockPref(preference);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   PrimaryPassword: {
 | |
|     onAllWindowsRestored(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("removeMasterPassword");
 | |
|       } else {
 | |
|         manager.disallowFeature("createMasterPassword");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   PromptForDownloadLocation: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("browser.download.useDownloadDir", !param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   Proxy: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param.Locked) {
 | |
|         manager.disallowFeature("changeProxySettings");
 | |
|         lazy.ProxyPolicies.configureProxySettings(param, setAndLockPref);
 | |
|       } else {
 | |
|         lazy.ProxyPolicies.configureProxySettings(
 | |
|           param,
 | |
|           PoliciesUtils.setDefaultPref
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   RequestedLocales: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       let requestedLocales;
 | |
|       if (Array.isArray(param)) {
 | |
|         requestedLocales = param;
 | |
|       } else if (param) {
 | |
|         requestedLocales = param.split(",");
 | |
|       } else {
 | |
|         requestedLocales = [];
 | |
|       }
 | |
|       runOncePerModification(
 | |
|         "requestedLocales",
 | |
|         JSON.stringify(requestedLocales),
 | |
|         () => {
 | |
|           Services.locale.requestedLocales = requestedLocales;
 | |
|         }
 | |
|       );
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SanitizeOnShutdown: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (typeof param === "boolean") {
 | |
|         setAndLockPref("privacy.sanitize.sanitizeOnShutdown", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.cache", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.cookies", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.downloads", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.formdata", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.history", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.sessions", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.siteSettings", param);
 | |
|         setAndLockPref("privacy.clearOnShutdown.offlineApps", param);
 | |
|       } else {
 | |
|         let locked = true;
 | |
|         // Needed to preserve original behavior in perpetuity.
 | |
|         let lockDefaultPrefs = true;
 | |
|         if ("Locked" in param) {
 | |
|           locked = param.Locked;
 | |
|           lockDefaultPrefs = false;
 | |
|         }
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "privacy.sanitize.sanitizeOnShutdown",
 | |
|           true,
 | |
|           locked
 | |
|         );
 | |
|         if ("Cache" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.cache",
 | |
|             param.Cache,
 | |
|             locked
 | |
|           );
 | |
|         } else {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.cache",
 | |
|             false,
 | |
|             lockDefaultPrefs
 | |
|           );
 | |
|         }
 | |
|         if ("Cookies" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.cookies",
 | |
|             param.Cookies,
 | |
|             locked
 | |
|           );
 | |
|         } else {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.cookies",
 | |
|             false,
 | |
|             lockDefaultPrefs
 | |
|           );
 | |
|         }
 | |
|         if ("Downloads" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.downloads",
 | |
|             param.Downloads,
 | |
|             locked
 | |
|           );
 | |
|         } else {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.downloads",
 | |
|             false,
 | |
|             lockDefaultPrefs
 | |
|           );
 | |
|         }
 | |
|         if ("FormData" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.formdata",
 | |
|             param.FormData,
 | |
|             locked
 | |
|           );
 | |
|         } else {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.formdata",
 | |
|             false,
 | |
|             lockDefaultPrefs
 | |
|           );
 | |
|         }
 | |
|         if ("History" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.history",
 | |
|             param.History,
 | |
|             locked
 | |
|           );
 | |
|         } else {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.history",
 | |
|             false,
 | |
|             lockDefaultPrefs
 | |
|           );
 | |
|         }
 | |
|         if ("Sessions" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.sessions",
 | |
|             param.Sessions,
 | |
|             locked
 | |
|           );
 | |
|         } else {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.sessions",
 | |
|             false,
 | |
|             lockDefaultPrefs
 | |
|           );
 | |
|         }
 | |
|         if ("SiteSettings" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.siteSettings",
 | |
|             param.SiteSettings,
 | |
|             locked
 | |
|           );
 | |
|         }
 | |
|         if ("OfflineApps" in param) {
 | |
|           PoliciesUtils.setDefaultPref(
 | |
|             "privacy.clearOnShutdown.offlineApps",
 | |
|             param.OfflineApps,
 | |
|             locked
 | |
|           );
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SearchBar: {
 | |
|     onAllWindowsRestored(manager, param) {
 | |
|       // This policy is meant to change the default behavior, not to force it.
 | |
|       // If this policy was already applied and the user chose move the search
 | |
|       // bar, don't move it again.
 | |
|       runOncePerModification("searchInNavBar", param, () => {
 | |
|         if (param == "separate") {
 | |
|           lazy.CustomizableUI.addWidgetToArea(
 | |
|             "search-container",
 | |
|             lazy.CustomizableUI.AREA_NAVBAR,
 | |
|             lazy.CustomizableUI.getPlacementOfWidget("urlbar-container")
 | |
|               .position + 1
 | |
|           );
 | |
|         } else if (param == "unified") {
 | |
|           lazy.CustomizableUI.removeWidgetFromArea("search-container");
 | |
|         }
 | |
|       });
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SearchEngines: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       if (param.PreventInstalls) {
 | |
|         manager.disallowFeature("installSearchEngine", true);
 | |
|       }
 | |
|     },
 | |
|     onAllWindowsRestored(manager, param) {
 | |
|       Services.search.init().then(async () => {
 | |
|         // Adding of engines is handled by the SearchService in the init().
 | |
|         // Remove can happen after those are added - no engines are allowed
 | |
|         // to replace the application provided engines, even if they have been
 | |
|         // removed.
 | |
|         if (param.Remove) {
 | |
|           // Only rerun if the list of engine names has changed.
 | |
|           await runOncePerModification(
 | |
|             "removeSearchEngines",
 | |
|             JSON.stringify(param.Remove),
 | |
|             async function () {
 | |
|               for (let engineName of param.Remove) {
 | |
|                 let engine = Services.search.getEngineByName(engineName);
 | |
|                 if (engine) {
 | |
|                   try {
 | |
|                     await Services.search.removeEngine(engine);
 | |
|                   } catch (ex) {
 | |
|                     lazy.log.error("Unable to remove the search engine", ex);
 | |
|                   }
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           );
 | |
|         }
 | |
|         if (param.Default) {
 | |
|           await runOncePerModification(
 | |
|             "setDefaultSearchEngine",
 | |
|             param.Default,
 | |
|             async () => {
 | |
|               let defaultEngine;
 | |
|               try {
 | |
|                 defaultEngine = Services.search.getEngineByName(param.Default);
 | |
|                 if (!defaultEngine) {
 | |
|                   throw new Error("No engine by that name could be found");
 | |
|                 }
 | |
|               } catch (ex) {
 | |
|                 lazy.log.error(
 | |
|                   `Search engine lookup failed when attempting to set ` +
 | |
|                     `the default engine. Requested engine was ` +
 | |
|                     `"${param.Default}".`,
 | |
|                   ex
 | |
|                 );
 | |
|               }
 | |
|               if (defaultEngine) {
 | |
|                 try {
 | |
|                   await Services.search.setDefault(
 | |
|                     defaultEngine,
 | |
|                     Ci.nsISearchService.CHANGE_REASON_ENTERPRISE
 | |
|                   );
 | |
|                 } catch (ex) {
 | |
|                   lazy.log.error("Unable to set the default search engine", ex);
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           );
 | |
|         }
 | |
|         if (param.DefaultPrivate) {
 | |
|           await runOncePerModification(
 | |
|             "setDefaultPrivateSearchEngine",
 | |
|             param.DefaultPrivate,
 | |
|             async () => {
 | |
|               let defaultPrivateEngine;
 | |
|               try {
 | |
|                 defaultPrivateEngine = Services.search.getEngineByName(
 | |
|                   param.DefaultPrivate
 | |
|                 );
 | |
|                 if (!defaultPrivateEngine) {
 | |
|                   throw new Error("No engine by that name could be found");
 | |
|                 }
 | |
|               } catch (ex) {
 | |
|                 lazy.log.error(
 | |
|                   `Search engine lookup failed when attempting to set ` +
 | |
|                     `the default private engine. Requested engine was ` +
 | |
|                     `"${param.DefaultPrivate}".`,
 | |
|                   ex
 | |
|                 );
 | |
|               }
 | |
|               if (defaultPrivateEngine) {
 | |
|                 try {
 | |
|                   await Services.search.setDefaultPrivate(
 | |
|                     defaultPrivateEngine,
 | |
|                     Ci.nsISearchService.CHANGE_REASON_ENTERPRISE
 | |
|                   );
 | |
|                 } catch (ex) {
 | |
|                   lazy.log.error(
 | |
|                     "Unable to set the default private search engine",
 | |
|                     ex
 | |
|                   );
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           );
 | |
|         }
 | |
|       });
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SearchSuggestEnabled: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("browser.urlbar.suggest.searches", param);
 | |
|       setAndLockPref("browser.search.suggest.enabled", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SecurityDevices: {
 | |
|     onProfileAfterChange(manager, param) {
 | |
|       let pkcs11db = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
 | |
|         Ci.nsIPKCS11ModuleDB
 | |
|       );
 | |
|       let securityDevices;
 | |
|       if (param.Add || param.Delete) {
 | |
|         // We're using the new syntax.
 | |
|         securityDevices = param.Add;
 | |
|         if (param.Delete) {
 | |
|           for (let deviceName of param.Delete) {
 | |
|             try {
 | |
|               pkcs11db.deleteModule(deviceName);
 | |
|             } catch (e) {
 | |
|               // Ignoring errors here since it might stick around in policy
 | |
|               // after removing. Alternative would be to listModules and
 | |
|               // make sure it's there before removing, but that seems
 | |
|               // like unnecessary work.
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         securityDevices = param;
 | |
|       }
 | |
|       if (!securityDevices) {
 | |
|         return;
 | |
|       }
 | |
|       for (let deviceName in securityDevices) {
 | |
|         let foundModule = false;
 | |
|         for (let module of pkcs11db.listModules()) {
 | |
|           if (module && module.libName === securityDevices[deviceName]) {
 | |
|             foundModule = true;
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         if (foundModule) {
 | |
|           continue;
 | |
|         }
 | |
|         try {
 | |
|           pkcs11db.addModule(deviceName, securityDevices[deviceName], 0, 0);
 | |
|         } catch (ex) {
 | |
|           lazy.log.error(`Unable to add security device ${deviceName}`);
 | |
|           lazy.log.debug(ex);
 | |
|         }
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   ShowHomeButton: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if (param) {
 | |
|         manager.disallowFeature("removeHomeButtonByDefault");
 | |
|       }
 | |
|     },
 | |
|     onAllWindowsRestored(manager, param) {
 | |
|       if (param) {
 | |
|         let homeButtonPlacement =
 | |
|           lazy.CustomizableUI.getPlacementOfWidget("home-button");
 | |
|         if (!homeButtonPlacement) {
 | |
|           let placement =
 | |
|             lazy.CustomizableUI.getPlacementOfWidget("forward-button");
 | |
|           lazy.CustomizableUI.addWidgetToArea(
 | |
|             "home-button",
 | |
|             lazy.CustomizableUI.AREA_NAVBAR,
 | |
|             placement.position + 2
 | |
|           );
 | |
|         }
 | |
|       } else {
 | |
|         lazy.CustomizableUI.removeWidgetFromArea("home-button");
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SSLVersionMax: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       let tlsVersion;
 | |
|       switch (param) {
 | |
|         case "tls1":
 | |
|           tlsVersion = 1;
 | |
|           break;
 | |
|         case "tls1.1":
 | |
|           tlsVersion = 2;
 | |
|           break;
 | |
|         case "tls1.2":
 | |
|           tlsVersion = 3;
 | |
|           break;
 | |
|         case "tls1.3":
 | |
|           tlsVersion = 4;
 | |
|           break;
 | |
|       }
 | |
|       setAndLockPref("security.tls.version.max", tlsVersion);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SSLVersionMin: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       let tlsVersion;
 | |
|       switch (param) {
 | |
|         case "tls1":
 | |
|           tlsVersion = 1;
 | |
|           break;
 | |
|         case "tls1.1":
 | |
|           tlsVersion = 2;
 | |
|           break;
 | |
|         case "tls1.2":
 | |
|           tlsVersion = 3;
 | |
|           break;
 | |
|         case "tls1.3":
 | |
|           tlsVersion = 4;
 | |
|           break;
 | |
|       }
 | |
|       setAndLockPref("security.tls.version.min", tlsVersion);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   StartDownloadsInTempDirectory: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("browser.download.start_downloads_in_tmp_dir", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   SupportMenu: {
 | |
|     onProfileAfterChange(manager, param) {
 | |
|       manager.setSupportMenu(param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   UserMessaging: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       if ("WhatsNew" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.messaging-system.whatsNewPanel.enabled",
 | |
|           param.WhatsNew,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("ExtensionRecommendations" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons",
 | |
|           param.ExtensionRecommendations,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("FeatureRecommendations" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features",
 | |
|           param.FeatureRecommendations,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("UrlbarInterventions" in param && !param.UrlbarInterventions) {
 | |
|         manager.disallowFeature("urlbarinterventions");
 | |
|       }
 | |
|       if ("SkipOnboarding" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.aboutwelcome.enabled",
 | |
|           !param.SkipOnboarding,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|       if ("MoreFromMozilla" in param) {
 | |
|         PoliciesUtils.setDefaultPref(
 | |
|           "browser.preferences.moreFromMozilla",
 | |
|           param.MoreFromMozilla,
 | |
|           param.Locked
 | |
|         );
 | |
|       }
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   UseSystemPrintDialog: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("print.prefer_system_dialog", param);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   WebsiteFilter: {
 | |
|     onBeforeUIStartup(manager, param) {
 | |
|       lazy.WebsiteFilter.init(param.Block || [], param.Exceptions || []);
 | |
|     },
 | |
|   },
 | |
| 
 | |
|   WindowsSSO: {
 | |
|     onBeforeAddons(manager, param) {
 | |
|       setAndLockPref("network.http.windows-sso.enabled", param);
 | |
|     },
 | |
|   },
 | |
| };
 | |
| 
 | |
| /*
 | |
|  * ====================
 | |
|  * = HELPER FUNCTIONS =
 | |
|  * ====================
 | |
|  *
 | |
|  * The functions below are helpers to be used by several policies.
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * setAndLockPref
 | |
|  *
 | |
|  * Sets the _default_ value of a pref, and locks it (meaning that
 | |
|  * the default value will always be returned, independent from what
 | |
|  * is stored as the user value).
 | |
|  * The value is only changed in memory, and not stored to disk.
 | |
|  *
 | |
|  * @param {string} prefName
 | |
|  *        The pref to be changed
 | |
|  * @param {boolean,number,string} prefValue
 | |
|  *        The value to set and lock
 | |
|  */
 | |
| export function setAndLockPref(prefName, prefValue) {
 | |
|   PoliciesUtils.setDefaultPref(prefName, prefValue, true);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * setDefaultPref
 | |
|  *
 | |
|  * Sets the _default_ value of a pref and optionally locks it.
 | |
|  * The value is only changed in memory, and not stored to disk.
 | |
|  *
 | |
|  * @param {string} prefName
 | |
|  *        The pref to be changed
 | |
|  * @param {boolean,number,string} prefValue
 | |
|  *        The value to set
 | |
|  * @param {boolean} locked
 | |
|  *        Optionally lock the pref
 | |
|  */
 | |
| 
 | |
| export var PoliciesUtils = {
 | |
|   setDefaultPref(prefName, prefValue, locked) {
 | |
|     let prefWasLocked = Services.prefs.prefIsLocked(prefName);
 | |
|     if (prefWasLocked) {
 | |
|       Services.prefs.unlockPref(prefName);
 | |
|     }
 | |
| 
 | |
|     let defaults = Services.prefs.getDefaultBranch("");
 | |
| 
 | |
|     switch (typeof prefValue) {
 | |
|       case "boolean":
 | |
|         defaults.setBoolPref(prefName, prefValue);
 | |
|         break;
 | |
| 
 | |
|       case "number":
 | |
|         if (!Number.isInteger(prefValue)) {
 | |
|           throw new Error(`Non-integer value for ${prefName}`);
 | |
|         }
 | |
| 
 | |
|         // This is ugly, but necessary. On Windows GPO and macOS
 | |
|         // configs, booleans are converted to 0/1. In the previous
 | |
|         // Preferences implementation, the schema took care of
 | |
|         // automatically converting these values to booleans.
 | |
|         // Since we allow arbitrary prefs now, we have to do
 | |
|         // something different. See bug 1666836.
 | |
|         if (
 | |
|           defaults.getPrefType(prefName) == defaults.PREF_INT ||
 | |
|           ![0, 1].includes(prefValue)
 | |
|         ) {
 | |
|           defaults.setIntPref(prefName, prefValue);
 | |
|         } else {
 | |
|           defaults.setBoolPref(prefName, !!prefValue);
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case "string":
 | |
|         defaults.setStringPref(prefName, prefValue);
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     // Prefs can only be unlocked explicitly.
 | |
|     // If they were locked before, they stay locked.
 | |
|     if (locked || (prefWasLocked && locked !== false)) {
 | |
|       Services.prefs.lockPref(prefName);
 | |
|     }
 | |
|   },
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * setDefaultPermission
 | |
|  *
 | |
|  * Helper function to set preferences appropriately for the policy
 | |
|  *
 | |
|  * @param {string} policyName
 | |
|  *        The name of the policy to set
 | |
|  * @param {object} policyParam
 | |
|  *        The object containing param for the policy
 | |
|  */
 | |
| function setDefaultPermission(policyName, policyParam) {
 | |
|   if ("BlockNewRequests" in policyParam) {
 | |
|     let prefName = "permissions.default." + policyName;
 | |
| 
 | |
|     if (policyParam.BlockNewRequests) {
 | |
|       PoliciesUtils.setDefaultPref(prefName, 2, policyParam.Locked);
 | |
|     } else {
 | |
|       PoliciesUtils.setDefaultPref(prefName, 0, policyParam.Locked);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * addAllowDenyPermissions
 | |
|  *
 | |
|  * Helper function to call the permissions manager (Services.perms.addFromPrincipal)
 | |
|  * for two arrays of URLs.
 | |
|  *
 | |
|  * @param {string} permissionName
 | |
|  *        The name of the permission to change
 | |
|  * @param {array} allowList
 | |
|  *        The list of URLs to be set as ALLOW_ACTION for the chosen permission.
 | |
|  * @param {array} blockList
 | |
|  *        The list of URLs to be set as DENY_ACTION for the chosen permission.
 | |
|  */
 | |
| function addAllowDenyPermissions(permissionName, allowList, blockList) {
 | |
|   allowList = allowList || [];
 | |
|   blockList = blockList || [];
 | |
| 
 | |
|   for (let origin of allowList) {
 | |
|     try {
 | |
|       Services.perms.addFromPrincipal(
 | |
|         Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin),
 | |
|         permissionName,
 | |
|         Ci.nsIPermissionManager.ALLOW_ACTION,
 | |
|         Ci.nsIPermissionManager.EXPIRE_POLICY
 | |
|       );
 | |
|     } catch (ex) {
 | |
|       // It's possible if the origin was invalid, we'll have a string instead of an origin.
 | |
|       lazy.log.error(
 | |
|         `Unable to add ${permissionName} permission for ${
 | |
|           origin.href || origin
 | |
|         }`
 | |
|       );
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (let origin of blockList) {
 | |
|     Services.perms.addFromPrincipal(
 | |
|       Services.scriptSecurityManager.createContentPrincipalFromOrigin(origin),
 | |
|       permissionName,
 | |
|       Ci.nsIPermissionManager.DENY_ACTION,
 | |
|       Ci.nsIPermissionManager.EXPIRE_POLICY
 | |
|     );
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * runOnce
 | |
|  *
 | |
|  * Helper function to run a callback only once per policy.
 | |
|  *
 | |
|  * @param {string} actionName
 | |
|  *        A given name which will be used to track if this callback has run.
 | |
|  * @param {Functon} callback
 | |
|  *        The callback to run only once.
 | |
|  */
 | |
| // eslint-disable-next-line no-unused-vars
 | |
| export function runOnce(actionName, callback) {
 | |
|   let prefName = `browser.policies.runonce.${actionName}`;
 | |
|   if (Services.prefs.getBoolPref(prefName, false)) {
 | |
|     lazy.log.debug(
 | |
|       `Not running action ${actionName} again because it has already run.`
 | |
|     );
 | |
|     return;
 | |
|   }
 | |
|   Services.prefs.setBoolPref(prefName, true);
 | |
|   callback();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * runOncePerModification
 | |
|  *
 | |
|  * Helper function similar to runOnce. The difference is that runOnce runs the
 | |
|  * callback once when the policy is set, then never again.
 | |
|  * runOncePerModification runs the callback once each time the policy value
 | |
|  * changes from its previous value.
 | |
|  * If the callback that was passed is an async function, you can await on this
 | |
|  * function to await for the callback.
 | |
|  *
 | |
|  * @param {string} actionName
 | |
|  *        A given name which will be used to track if this callback has run.
 | |
|  *        This string will be part of a pref name.
 | |
|  * @param {string} policyValue
 | |
|  *        The current value of the policy. This will be compared to previous
 | |
|  *        values given to this function to determine if the policy value has
 | |
|  *        changed. Regardless of the data type of the policy, this must be a
 | |
|  *        string.
 | |
|  * @param {Function} callback
 | |
|  *        The callback to be run when the pref value changes
 | |
|  * @returns Promise
 | |
|  *        A promise that will resolve once the callback finishes running.
 | |
|  *
 | |
|  */
 | |
| async function runOncePerModification(actionName, policyValue, callback) {
 | |
|   let prefName = `browser.policies.runOncePerModification.${actionName}`;
 | |
|   let oldPolicyValue = Services.prefs.getStringPref(prefName, undefined);
 | |
|   if (policyValue === oldPolicyValue) {
 | |
|     lazy.log.debug(
 | |
|       `Not running action ${actionName} again because the policy's value is unchanged`
 | |
|     );
 | |
|     return Promise.resolve();
 | |
|   }
 | |
|   Services.prefs.setStringPref(prefName, policyValue);
 | |
|   return callback();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * clearRunOnceModification
 | |
|  *
 | |
|  * Helper function that clears a runOnce policy.
 | |
|  */
 | |
| function clearRunOnceModification(actionName) {
 | |
|   let prefName = `browser.policies.runOncePerModification.${actionName}`;
 | |
|   Services.prefs.clearUserPref(prefName);
 | |
| }
 | |
| 
 | |
| function replacePathVariables(path) {
 | |
|   if (path.includes("${home}")) {
 | |
|     return path.replace(
 | |
|       "${home}",
 | |
|       Services.dirsvc.get("Home", Ci.nsIFile).path
 | |
|     );
 | |
|   }
 | |
|   return path;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * installAddonFromURL
 | |
|  *
 | |
|  * Helper function that installs an addon from a URL
 | |
|  * and verifies that the addon ID matches.
 | |
|  */
 | |
| function installAddonFromURL(url, extensionID, addon) {
 | |
|   if (
 | |
|     addon &&
 | |
|     addon.sourceURI &&
 | |
|     addon.sourceURI.spec == url &&
 | |
|     !addon.sourceURI.schemeIs("file")
 | |
|   ) {
 | |
|     // It's the same addon, don't reinstall.
 | |
|     return;
 | |
|   }
 | |
|   lazy.AddonManager.getInstallForURL(url, {
 | |
|     telemetryInfo: { source: "enterprise-policy" },
 | |
|   }).then(install => {
 | |
|     if (install.addon && install.addon.appDisabled) {
 | |
|       lazy.log.error(`Incompatible add-on - ${install.addon.id}`);
 | |
|       install.cancel();
 | |
|       return;
 | |
|     }
 | |
|     let listener = {
 | |
|       /* eslint-disable-next-line no-shadow */
 | |
|       onDownloadEnded: install => {
 | |
|         // Install failed, error will be reported elsewhere.
 | |
|         if (!install.addon) {
 | |
|           return;
 | |
|         }
 | |
|         if (extensionID && install.addon.id != extensionID) {
 | |
|           lazy.log.error(
 | |
|             `Add-on downloaded from ${url} had unexpected id (got ${install.addon.id} expected ${extensionID})`
 | |
|           );
 | |
|           install.removeListener(listener);
 | |
|           install.cancel();
 | |
|         }
 | |
|         if (install.addon.appDisabled) {
 | |
|           lazy.log.error(`Incompatible add-on - ${url}`);
 | |
|           install.removeListener(listener);
 | |
|           install.cancel();
 | |
|         }
 | |
|         if (
 | |
|           addon &&
 | |
|           Services.vc.compare(addon.version, install.addon.version) == 0
 | |
|         ) {
 | |
|           lazy.log.debug(
 | |
|             "Installation cancelled because versions are the same"
 | |
|           );
 | |
|           install.removeListener(listener);
 | |
|           install.cancel();
 | |
|         }
 | |
|       },
 | |
|       onDownloadFailed: () => {
 | |
|         install.removeListener(listener);
 | |
|         lazy.log.error(
 | |
|           `Download failed - ${lazy.AddonManager.errorToString(
 | |
|             install.error
 | |
|           )} - ${url}`
 | |
|         );
 | |
|         clearRunOnceModification("extensionsInstall");
 | |
|       },
 | |
|       onInstallFailed: () => {
 | |
|         install.removeListener(listener);
 | |
|         lazy.log.error(
 | |
|           `Installation failed - ${lazy.AddonManager.errorToString(
 | |
|             install.error
 | |
|           )} - {url}`
 | |
|         );
 | |
|       },
 | |
|       /* eslint-disable-next-line no-shadow */
 | |
|       onInstallEnded: (install, addon) => {
 | |
|         if (addon.type == "theme") {
 | |
|           addon.enable();
 | |
|         }
 | |
|         install.removeListener(listener);
 | |
|         lazy.log.debug(`Installation succeeded - ${url}`);
 | |
|       },
 | |
|     };
 | |
|     // If it's a local file install, onDownloadEnded is never called.
 | |
|     // So we call it manually, to handle some error cases.
 | |
|     if (url.startsWith("file:")) {
 | |
|       listener.onDownloadEnded(install);
 | |
|       if (install.state == lazy.AddonManager.STATE_CANCELLED) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|     install.addListener(listener);
 | |
|     install.install();
 | |
|   });
 | |
| }
 | |
| 
 | |
| let gBlockedAboutPages = [];
 | |
| 
 | |
| function clearBlockedAboutPages() {
 | |
|   gBlockedAboutPages = [];
 | |
| }
 | |
| 
 | |
| function blockAboutPage(manager, feature, neededOnContentProcess = false) {
 | |
|   addChromeURLBlocker();
 | |
|   gBlockedAboutPages.push(feature);
 | |
| 
 | |
|   try {
 | |
|     let aboutModule = Cc[ABOUT_CONTRACT + feature.split(":")[1]].getService(
 | |
|       Ci.nsIAboutModule
 | |
|     );
 | |
|     let chromeURL = aboutModule.getChromeURI(Services.io.newURI(feature)).spec;
 | |
|     gBlockedAboutPages.push(chromeURL);
 | |
|   } catch (e) {
 | |
|     // Some about pages don't have chrome URLS (compat)
 | |
|   }
 | |
| }
 | |
| 
 | |
| let ChromeURLBlockPolicy = {
 | |
|   shouldLoad(contentLocation, loadInfo, mimeTypeGuess) {
 | |
|     let contentType = loadInfo.externalContentPolicyType;
 | |
|     if (
 | |
|       (contentLocation.scheme != "chrome" &&
 | |
|         contentLocation.scheme != "about") ||
 | |
|       (contentType != Ci.nsIContentPolicy.TYPE_DOCUMENT &&
 | |
|         contentType != Ci.nsIContentPolicy.TYPE_SUBDOCUMENT)
 | |
|     ) {
 | |
|       return Ci.nsIContentPolicy.ACCEPT;
 | |
|     }
 | |
|     let contentLocationSpec = contentLocation.spec.toLowerCase();
 | |
|     if (
 | |
|       gBlockedAboutPages.some(function (aboutPage) {
 | |
|         return contentLocationSpec.startsWith(aboutPage.toLowerCase());
 | |
|       })
 | |
|     ) {
 | |
|       return Ci.nsIContentPolicy.REJECT_POLICY;
 | |
|     }
 | |
|     return Ci.nsIContentPolicy.ACCEPT;
 | |
|   },
 | |
|   shouldProcess(contentLocation, loadInfo, mimeTypeGuess) {
 | |
|     return Ci.nsIContentPolicy.ACCEPT;
 | |
|   },
 | |
|   classDescription: "Policy Engine Content Policy",
 | |
|   contractID: "@mozilla-org/policy-engine-content-policy-service;1",
 | |
|   classID: Components.ID("{ba7b9118-cabc-4845-8b26-4215d2a59ed7}"),
 | |
|   QueryInterface: ChromeUtils.generateQI(["nsIContentPolicy"]),
 | |
|   createInstance(iid) {
 | |
|     return this.QueryInterface(iid);
 | |
|   },
 | |
| };
 | |
| 
 | |
| function addChromeURLBlocker() {
 | |
|   if (Cc[ChromeURLBlockPolicy.contractID]) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
 | |
|   registrar.registerFactory(
 | |
|     ChromeURLBlockPolicy.classID,
 | |
|     ChromeURLBlockPolicy.classDescription,
 | |
|     ChromeURLBlockPolicy.contractID,
 | |
|     ChromeURLBlockPolicy
 | |
|   );
 | |
| 
 | |
|   Services.catMan.addCategoryEntry(
 | |
|     "content-policy",
 | |
|     ChromeURLBlockPolicy.contractID,
 | |
|     ChromeURLBlockPolicy.contractID,
 | |
|     false,
 | |
|     true
 | |
|   );
 | |
| }
 | |
| 
 | |
| function pemToBase64(pem) {
 | |
|   return pem
 | |
|     .replace(/(.*)-----BEGIN CERTIFICATE-----/, "")
 | |
|     .replace(/-----END CERTIFICATE-----(.*)/, "")
 | |
|     .replace(/[\r\n]/g, "");
 | |
| }
 | |
| 
 | |
| function processMIMEInfo(mimeInfo, realMIMEInfo) {
 | |
|   if ("handlers" in mimeInfo) {
 | |
|     let firstHandler = true;
 | |
|     for (let handler of mimeInfo.handlers) {
 | |
|       // handler can be null which means they don't
 | |
|       // want a preferred handler.
 | |
|       if (handler) {
 | |
|         let handlerApp;
 | |
|         if ("path" in handler) {
 | |
|           try {
 | |
|             let file = new lazy.FileUtils.File(handler.path);
 | |
|             handlerApp = Cc[
 | |
|               "@mozilla.org/uriloader/local-handler-app;1"
 | |
|             ].createInstance(Ci.nsILocalHandlerApp);
 | |
|             handlerApp.executable = file;
 | |
|           } catch (ex) {
 | |
|             lazy.log.error(
 | |
|               `Unable to create handler executable (${handler.path})`
 | |
|             );
 | |
|             continue;
 | |
|           }
 | |
|         } else if ("uriTemplate" in handler) {
 | |
|           let templateURL = new URL(handler.uriTemplate);
 | |
|           if (templateURL.protocol != "https:") {
 | |
|             lazy.log.error(
 | |
|               `Web handler must be https (${handler.uriTemplate})`
 | |
|             );
 | |
|             continue;
 | |
|           }
 | |
|           if (
 | |
|             !templateURL.pathname.includes("%s") &&
 | |
|             !templateURL.search.includes("%s")
 | |
|           ) {
 | |
|             lazy.log.error(
 | |
|               `Web handler must contain %s (${handler.uriTemplate})`
 | |
|             );
 | |
|             continue;
 | |
|           }
 | |
|           handlerApp = Cc[
 | |
|             "@mozilla.org/uriloader/web-handler-app;1"
 | |
|           ].createInstance(Ci.nsIWebHandlerApp);
 | |
|           handlerApp.uriTemplate = handler.uriTemplate;
 | |
|         } else {
 | |
|           lazy.log.error("Invalid handler");
 | |
|           continue;
 | |
|         }
 | |
|         if ("name" in handler) {
 | |
|           handlerApp.name = handler.name;
 | |
|         }
 | |
|         realMIMEInfo.possibleApplicationHandlers.appendElement(handlerApp);
 | |
|         if (firstHandler) {
 | |
|           realMIMEInfo.preferredApplicationHandler = handlerApp;
 | |
|         }
 | |
|       }
 | |
|       firstHandler = false;
 | |
|     }
 | |
|   }
 | |
|   if ("action" in mimeInfo) {
 | |
|     let action = realMIMEInfo[mimeInfo.action];
 | |
|     if (
 | |
|       action == realMIMEInfo.useHelperApp &&
 | |
|       !realMIMEInfo.possibleApplicationHandlers.length
 | |
|     ) {
 | |
|       lazy.log.error("useHelperApp requires a handler");
 | |
|       return;
 | |
|     }
 | |
|     realMIMEInfo.preferredAction = action;
 | |
|   }
 | |
|   if ("ask" in mimeInfo) {
 | |
|     realMIMEInfo.alwaysAskBeforeHandling = mimeInfo.ask;
 | |
|   }
 | |
|   lazy.gHandlerService.store(realMIMEInfo);
 | |
| }
 | 
