diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /browser/components/enterprisepolicies/helpers | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
8 files changed, 687 insertions, 0 deletions
diff --git a/browser/components/enterprisepolicies/helpers/BookmarksPolicies.sys.mjs b/browser/components/enterprisepolicies/helpers/BookmarksPolicies.sys.mjs new file mode 100644 index 0000000000..47b706c7b1 --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/BookmarksPolicies.sys.mjs @@ -0,0 +1,301 @@ +/* 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/. */ + +/* + * A Bookmark object received through the policy engine will be an + * object with the following properties: + * + * - URL (URL) + * (required) The URL for this bookmark + * + * - Title (string) + * (required) The title for this bookmark + * + * - Placement (string) + * (optional) Either "toolbar" or "menu". If missing or invalid, + * "toolbar" will be used + * + * - Folder (string) + * (optional) The name of the folder to put this bookmark into. + * If present, a folder with this name will be created in the + * chosen placement above, and the bookmark will be created there. + * If missing, the bookmark will be created directly into the + * chosen placement. + * + * - Favicon (URL) + * (optional) An http:, https: or data: URL with the favicon. + * If possible, we recommend against using this property, in order + * to keep the json file small. + * If a favicon is not provided through the policy, it will be loaded + * naturally after the user first visits the bookmark. + * + * + * Note: The Policy Engine automatically converts the strings given to + * the URL and favicon properties into a URL object. + * + * The schema for this object is defined in policies-schema.json. + */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PlacesUtils: "resource://gre/modules/PlacesUtils.sys.mjs", +}); + +const PREF_LOGLEVEL = "browser.policies.loglevel"; + +XPCOMUtils.defineLazyGetter(lazy, "log", () => { + let { ConsoleAPI } = ChromeUtils.importESModule( + "resource://gre/modules/Console.sys.mjs" + ); + return new ConsoleAPI({ + prefix: "BookmarksPolicies.jsm", + // tip: set maxLogLevel to "debug" and use log.debug() to create detailed + // messages during development. See LOG_LEVELS in Console.jsm for details. + maxLogLevel: "error", + maxLogLevelPref: PREF_LOGLEVEL, + }); +}); + +export const BookmarksPolicies = { + // These prefixes must only contain characters + // allowed by PlacesUtils.isValidGuid + BOOKMARK_GUID_PREFIX: "PolB-", + FOLDER_GUID_PREFIX: "PolF-", + + /* + * Process the bookmarks specified by the policy engine. + * + * @param param + * This will be an array of bookmarks objects, as + * described on the top of this file. + */ + processBookmarks(param) { + calculateLists(param).then(async function addRemoveBookmarks(results) { + for (let bookmark of results.add.values()) { + await insertBookmark(bookmark).catch(lazy.log.error); + } + for (let bookmark of results.remove.values()) { + await lazy.PlacesUtils.bookmarks.remove(bookmark).catch(lazy.log.error); + } + for (let bookmark of results.emptyFolders.values()) { + await lazy.PlacesUtils.bookmarks.remove(bookmark).catch(lazy.log.error); + } + + lazy.gFoldersMapPromise.then(map => map.clear()); + }); + }, +}; + +/* + * This function calculates the differences between the existing bookmarks + * that are managed by the policy engine (which are known through a guid + * prefix) and the specified bookmarks in the policy file. + * They can differ if the policy file has changed. + * + * @param specifiedBookmarks + * This will be an array of bookmarks objects, as + * described on the top of this file. + */ +async function calculateLists(specifiedBookmarks) { + // --------- STEP 1 --------- + // Build two Maps (one with the existing bookmarks, another with + // the specified bookmarks), to make iteration quicker. + + // LIST A + // MAP of url (string) -> bookmarks objects from the Policy Engine + let specifiedBookmarksMap = new Map(); + for (let bookmark of specifiedBookmarks) { + specifiedBookmarksMap.set(bookmark.URL.href, bookmark); + } + + // LIST B + // MAP of url (string) -> bookmarks objects from Places + let existingBookmarksMap = new Map(); + await lazy.PlacesUtils.bookmarks.fetch( + { guidPrefix: BookmarksPolicies.BOOKMARK_GUID_PREFIX }, + bookmark => existingBookmarksMap.set(bookmark.url.href, bookmark) + ); + + // --------- STEP 2 --------- + // + // /=====/====\=====\ + // / / \ \ + // | | | | + // | A | {} | B | + // | | | | + // \ \ / / + // \=====\====/=====/ + // + // Find the intersection of the two lists. Items in the intersection + // are removed from the original lists. + // + // The items remaining in list A are new bookmarks to be added. + // The items remaining in list B are old bookmarks to be removed. + // + // There's nothing to do with items in the intersection, so there's no + // need to keep track of them. + // + // BONUS: It's necessary to keep track of the folder names that were + // seen, to make sure we remove the ones that were left empty. + + let foldersSeen = new Set(); + + for (let [url, item] of specifiedBookmarksMap) { + foldersSeen.add(item.Folder); + + if (existingBookmarksMap.has(url)) { + lazy.log.debug(`Bookmark intersection: ${url}`); + // If this specified bookmark exists in the existing bookmarks list, + // we can remove it from both lists as it's in the intersection. + specifiedBookmarksMap.delete(url); + existingBookmarksMap.delete(url); + } + } + + for (let url of specifiedBookmarksMap.keys()) { + lazy.log.debug(`Bookmark to add: ${url}`); + } + + for (let url of existingBookmarksMap.keys()) { + lazy.log.debug(`Bookmark to remove: ${url}`); + } + + // SET of folders to be deleted (bookmarks object from Places) + let foldersToRemove = new Set(); + + // If no bookmarks will be deleted, then no folder will + // need to be deleted either, so this next section can be skipped. + if (existingBookmarksMap.size > 0) { + await lazy.PlacesUtils.bookmarks.fetch( + { guidPrefix: BookmarksPolicies.FOLDER_GUID_PREFIX }, + folder => { + if (!foldersSeen.has(folder.title)) { + lazy.log.debug(`Folder to remove: ${folder.title}`); + foldersToRemove.add(folder); + } + } + ); + } + + return { + add: specifiedBookmarksMap, + remove: existingBookmarksMap, + emptyFolders: foldersToRemove, + }; +} + +async function insertBookmark(bookmark) { + let parentGuid = await getParentGuid(bookmark.Placement, bookmark.Folder); + + await lazy.PlacesUtils.bookmarks.insert({ + url: Services.io.newURI(bookmark.URL.href), + title: bookmark.Title, + guid: lazy.PlacesUtils.generateGuidWithPrefix( + BookmarksPolicies.BOOKMARK_GUID_PREFIX + ), + parentGuid, + }); + + if (bookmark.Favicon) { + setFaviconForBookmark(bookmark); + } +} + +function setFaviconForBookmark(bookmark) { + let faviconURI; + let nullPrincipal = Services.scriptSecurityManager.createNullPrincipal({}); + + switch (bookmark.Favicon.protocol) { + case "data:": + // data urls must first call replaceFaviconDataFromDataURL, using a + // fake URL. Later, it's needed to call setAndFetchFaviconForPage + // with the same URL. + faviconURI = Services.io.newURI("fake-favicon-uri:" + bookmark.URL.href); + + lazy.PlacesUtils.favicons.replaceFaviconDataFromDataURL( + faviconURI, + bookmark.Favicon.href, + 0 /* max expiration length */, + nullPrincipal + ); + break; + + case "http:": + case "https:": + faviconURI = Services.io.newURI(bookmark.Favicon.href); + break; + + default: + lazy.log.error( + `Bad URL given for favicon on bookmark "${bookmark.Title}"` + ); + return; + } + + lazy.PlacesUtils.favicons.setAndFetchFaviconForPage( + Services.io.newURI(bookmark.URL.href), + faviconURI, + false /* forceReload */, + lazy.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, + null, + nullPrincipal + ); +} + +// Cache of folder names to guids to be used by the getParentGuid +// function. The name consists in the parentGuid (which should always +// be the menuGuid or the toolbarGuid) + the folder title. This is to +// support having the same folder name in both the toolbar and menu. +XPCOMUtils.defineLazyGetter(lazy, "gFoldersMapPromise", () => { + return new Promise(resolve => { + let foldersMap = new Map(); + return lazy.PlacesUtils.bookmarks + .fetch( + { + guidPrefix: BookmarksPolicies.FOLDER_GUID_PREFIX, + }, + result => { + foldersMap.set(`${result.parentGuid}|${result.title}`, result.guid); + } + ) + .then(() => resolve(foldersMap)); + }); +}); + +async function getParentGuid(placement, folderTitle) { + // Defaults to toolbar if no placement was given. + let parentGuid = + placement == "menu" + ? lazy.PlacesUtils.bookmarks.menuGuid + : lazy.PlacesUtils.bookmarks.toolbarGuid; + + if (!folderTitle) { + // If no folderTitle is given, this bookmark is to be placed directly + // into the toolbar or menu. + return parentGuid; + } + + let foldersMap = await lazy.gFoldersMapPromise; + let folderName = `${parentGuid}|${folderTitle}`; + + if (foldersMap.has(folderName)) { + return foldersMap.get(folderName); + } + + let guid = lazy.PlacesUtils.generateGuidWithPrefix( + BookmarksPolicies.FOLDER_GUID_PREFIX + ); + await lazy.PlacesUtils.bookmarks.insert({ + type: lazy.PlacesUtils.bookmarks.TYPE_FOLDER, + title: folderTitle, + guid, + parentGuid, + }); + + foldersMap.set(folderName, guid); + return guid; +} diff --git a/browser/components/enterprisepolicies/helpers/ProxyPolicies.sys.mjs b/browser/components/enterprisepolicies/helpers/ProxyPolicies.sys.mjs new file mode 100644 index 0000000000..8f2ba0b2a2 --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/ProxyPolicies.sys.mjs @@ -0,0 +1,111 @@ +/* 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"; + +const PREF_LOGLEVEL = "browser.policies.loglevel"; + +const lazy = {}; + +XPCOMUtils.defineLazyGetter(lazy, "log", () => { + let { ConsoleAPI } = ChromeUtils.importESModule( + "resource://gre/modules/Console.sys.mjs" + ); + return new ConsoleAPI({ + prefix: "ProxyPolicies.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, + }); +}); + +// Don't use const here because this is acessed by +// tests through the BackstagePass object. +export var PROXY_TYPES_MAP = new Map([ + ["none", Ci.nsIProtocolProxyService.PROXYCONFIG_DIRECT], + ["system", Ci.nsIProtocolProxyService.PROXYCONFIG_SYSTEM], + ["manual", Ci.nsIProtocolProxyService.PROXYCONFIG_MANUAL], + ["autoDetect", Ci.nsIProtocolProxyService.PROXYCONFIG_WPAD], + ["autoConfig", Ci.nsIProtocolProxyService.PROXYCONFIG_PAC], +]); + +export var ProxyPolicies = { + configureProxySettings(param, setPref) { + if (param.Mode) { + setPref("network.proxy.type", PROXY_TYPES_MAP.get(param.Mode)); + } + + if (param.AutoConfigURL) { + setPref("network.proxy.autoconfig_url", param.AutoConfigURL.href); + } + + if (param.UseProxyForDNS !== undefined) { + setPref("network.proxy.socks_remote_dns", param.UseProxyForDNS); + } + + if (param.AutoLogin !== undefined) { + setPref("signon.autologin.proxy", param.AutoLogin); + } + + if (param.SOCKSVersion !== undefined) { + if (param.SOCKSVersion != 4 && param.SOCKSVersion != 5) { + lazy.log.error("Invalid SOCKS version"); + } else { + setPref("network.proxy.socks_version", param.SOCKSVersion); + } + } + + if (param.Passthrough !== undefined) { + setPref("network.proxy.no_proxies_on", param.Passthrough); + } + + if (param.UseHTTPProxyForAllProtocols !== undefined) { + setPref( + "network.proxy.share_proxy_settings", + param.UseHTTPProxyForAllProtocols + ); + } + + if (param.FTPProxy) { + lazy.log.warn("FTPProxy support was removed in bug 1574475"); + } + + function setProxyHostAndPort(type, address) { + let url; + try { + // Prepend https just so we can use the URL parser + // instead of parsing manually. + url = new URL(`https://${address}`); + } catch (e) { + lazy.log.error(`Invalid address for ${type} proxy: ${address}`); + return; + } + + setPref(`network.proxy.${type}`, url.hostname); + if (url.port) { + setPref(`network.proxy.${type}_port`, Number(url.port)); + } + } + + if (param.HTTPProxy) { + setProxyHostAndPort("http", param.HTTPProxy); + + // network.proxy.share_proxy_settings is a UI feature, not handled by the + // network code. That pref only controls if the checkbox is checked, and + // then we must manually set the other values. + if (param.UseHTTPProxyForAllProtocols) { + param.SSLProxy = param.SOCKSProxy = param.HTTPProxy; + } + } + + if (param.SSLProxy) { + setProxyHostAndPort("ssl", param.SSLProxy); + } + + if (param.SOCKSProxy) { + setProxyHostAndPort("socks", param.SOCKSProxy); + } + }, +}; diff --git a/browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs b/browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs new file mode 100644 index 0000000000..8be0c9e204 --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs @@ -0,0 +1,188 @@ +/* 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/. */ + +/* + * This module implements the policy to block websites from being visited, + * or to only allow certain websites to be visited. + * + * The blocklist takes as input an array of MatchPattern strings, as documented + * at https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Match_patterns. + * + * The exceptions list takes the same as input. This list opens up + * exceptions for rules on the blocklist that might be too strict. + * + * In addition to that, this allows the user to create an allowlist approach, + * by using the special "<all_urls>" pattern for the blocklist, and then + * adding all allowlisted websites on the exceptions list. + * + * Note that this module only blocks top-level website navigations and embeds. + * It does not block any other accesses to these urls: image tags, scripts, XHR, etc., + * because that could cause unexpected breakage. This is a policy to block + * users from visiting certain websites, and not from blocking any network + * connections to those websites. If the admin is looking for that, the recommended + * way is to configure that with extensions or through a company firewall. + */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const LIST_LENGTH_LIMIT = 1000; + +const PREF_LOGLEVEL = "browser.policies.loglevel"; + +const lazy = {}; + +XPCOMUtils.defineLazyGetter(lazy, "log", () => { + let { ConsoleAPI } = ChromeUtils.importESModule( + "resource://gre/modules/Console.sys.mjs" + ); + return new ConsoleAPI({ + prefix: "WebsiteFilter Policy", + // 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, + }); +}); + +export let WebsiteFilter = { + init(blocklist, exceptionlist) { + let blockArray = [], + exceptionArray = []; + + for (let i = 0; i < blocklist.length && i < LIST_LENGTH_LIMIT; i++) { + try { + let pattern = new MatchPattern(blocklist[i].toLowerCase()); + blockArray.push(pattern); + lazy.log.debug( + `Pattern added to WebsiteFilter. Block: ${blocklist[i]}` + ); + } catch (e) { + lazy.log.error( + `Invalid pattern on WebsiteFilter. Block: ${blocklist[i]}` + ); + } + } + + this._blockPatterns = new MatchPatternSet(blockArray); + + for (let i = 0; i < exceptionlist.length && i < LIST_LENGTH_LIMIT; i++) { + try { + let pattern = new MatchPattern(exceptionlist[i].toLowerCase()); + exceptionArray.push(pattern); + lazy.log.debug( + `Pattern added to WebsiteFilter. Exception: ${exceptionlist[i]}` + ); + } catch (e) { + lazy.log.error( + `Invalid pattern on WebsiteFilter. Exception: ${exceptionlist[i]}` + ); + } + } + + if (exceptionArray.length) { + this._exceptionsPatterns = new MatchPatternSet(exceptionArray); + } + + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + if (!registrar.isContractIDRegistered(this.contractID)) { + registrar.registerFactory( + this.classID, + this.classDescription, + this.contractID, + this + ); + + Services.catMan.addCategoryEntry( + "content-policy", + this.contractID, + this.contractID, + false, + true + ); + } + // We have to do this to catch 30X redirects. + // See bug 456957. + Services.obs.addObserver(this, "http-on-examine-response", true); + }, + + shouldLoad(contentLocation, loadInfo, mimeTypeGuess) { + let contentType = loadInfo.externalContentPolicyType; + let url = contentLocation.spec; + if (contentLocation.scheme == "view-source") { + url = contentLocation.pathQueryRef; + } else if (url.toLowerCase().startsWith("about:reader")) { + url = decodeURIComponent( + url.toLowerCase().substr("about:reader?url=".length) + ); + } + if ( + contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT || + contentType == Ci.nsIContentPolicy.TYPE_SUBDOCUMENT + ) { + if (this._blockPatterns.matches(url.toLowerCase())) { + if ( + !this._exceptionsPatterns || + !this._exceptionsPatterns.matches(url.toLowerCase()) + ) { + return Ci.nsIContentPolicy.REJECT_POLICY; + } + } + } + return Ci.nsIContentPolicy.ACCEPT; + }, + shouldProcess(contentLocation, loadInfo, mimeTypeGuess) { + return Ci.nsIContentPolicy.ACCEPT; + }, + observe(subject, topic, data) { + try { + let channel = subject.QueryInterface(Ci.nsIHttpChannel); + if ( + !channel.isDocument || + channel.responseStatus < 300 || + channel.responseStatus >= 400 + ) { + return; + } + let location = channel.getResponseHeader("location"); + // location might not be a fully qualified URL + let url; + try { + url = new URL(location); + } catch (e) { + url = new URL(location, channel.URI.spec); + } + if (this._blockPatterns.matches(url.href.toLowerCase())) { + if ( + !this._exceptionsPatterns || + !this._exceptionsPatterns.matches(url.href.toLowerCase()) + ) { + channel.cancel(Cr.NS_ERROR_BLOCKED_BY_POLICY); + } + } + } catch (e) {} + }, + classDescription: "Policy Engine File Content Policy", + contractID: "@mozilla-org/policy-engine-file-content-policy-service;1", + classID: Components.ID("{c0bbb557-813e-4e25-809d-b46a531a258f}"), + QueryInterface: ChromeUtils.generateQI([ + "nsIContentPolicy", + "nsIObserver", + "nsISupportsWeakReference", + ]), + createInstance(iid) { + return this.QueryInterface(iid); + }, + isAllowed(url) { + if (this._blockPatterns?.matches(url.toLowerCase())) { + if ( + !this._exceptionsPatterns || + !this._exceptionsPatterns.matches(url.toLowerCase()) + ) { + return false; + } + } + return true; + }, +}; diff --git a/browser/components/enterprisepolicies/helpers/moz.build b/browser/components/enterprisepolicies/helpers/moz.build new file mode 100644 index 0000000000..4429fa5928 --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/moz.build @@ -0,0 +1,14 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +with Files("**"): + BUG_COMPONENT = ("Firefox", "Enterprise Policies") + +EXTRA_JS_MODULES.policies += [ + "BookmarksPolicies.sys.mjs", + "ProxyPolicies.sys.mjs", + "WebsiteFilter.sys.mjs", +] diff --git a/browser/components/enterprisepolicies/helpers/sample.json b/browser/components/enterprisepolicies/helpers/sample.json new file mode 100644 index 0000000000..250434cd5d --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/sample.json @@ -0,0 +1,14 @@ +{ + "policies": { + "BlockAboutProfiles": true, + "DontCheckDefaultBrowser": true, + + "InstallAddonsPermission": { + "Allow": ["https://www.example.com"], + + "Block": ["https://www.example.org"] + }, + + "CreateMasterPassword": false + } +} diff --git a/browser/components/enterprisepolicies/helpers/sample_bookmarks.json b/browser/components/enterprisepolicies/helpers/sample_bookmarks.json new file mode 100644 index 0000000000..c16f334abd --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/sample_bookmarks.json @@ -0,0 +1,37 @@ +{ + "policies": { + "DisplayBookmarksToolbar": true, + + "Bookmarks": [ + { + "Title": "Bookmark 1", + "URL": "https://bookmark1.example.com" + }, + { + "Title": "Bookmark 2", + "URL": "https://bookmark2.example.com", + "Favicon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwMDAsTBZbkNwAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABNElEQVQ4y8WSsU0DURBE3yyWIaAJaqAAN4DPSL6AlIACKIEOyJEgRsIgOOkiInJqgAKowNg7BHdn7MOksNl+zZ//dvbDf5cAiklp22BdVtXdeTEpDYDB9m1VzU6OJuVp2NdEQCaI96fH2YHG4+mDduKYNMYINTcjcGbXzQVDEAphG0k48zUsajIbnAiMIXThpW8EICE0RAK4dvoKg9NIcTiQ589otyHOZLnwqK5nLwBFUZ4igc3iM0d1ff8CMC6mZ6Ihiaqq3gi1aUAnArD00SW1fq5OLBg0ymYmSZsR2/t4e/rGyCLW0sbp3oq+yTYqVgytQWui2FS7XYF7GFprY921T4CNQt8zr47dNzCkIX7y/jBtH+v+RGMQrc828W8pApnZbmEVQp/Ae7BlOy2ttib81/UFc+WRWEbjckIAAAAASUVORK5CYII=", + "Folder": "Folder 1" + }, + { + "Title": "Bookmark 3", + "URL": "https://bookmark3.example.com", + "Favicon": "https://www.mozilla.org/favicon.ico", + "Placement": "menu" + }, + { + "Title": "Bookmark 4", + "URL": "https://bookmark4.example.com", + "Favicon": "https://www.mozilla.org/favicon.ico", + "Folder": "Folder 1" + }, + { + "Title": "Bookmark 5", + "URL": "https://bookmark5.example.com", + "Favicon": "https://www.mozilla.org/favicon.ico", + "Placement": "menu", + "Folder": "Folder 2" + } + ] + } +} diff --git a/browser/components/enterprisepolicies/helpers/sample_proxy.json b/browser/components/enterprisepolicies/helpers/sample_proxy.json new file mode 100644 index 0000000000..0daaf70409 --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/sample_proxy.json @@ -0,0 +1,13 @@ +{ + "policies": { + "Proxy": { + "Mode": "manual", + "Locked": true, + "HTTPProxy": "www.example.com:42", + "UseHTTPProxyForAllProtocols": true, + "Passthrough": "foo, bar, baz", + "SOCKSVersion": 4, + "UseProxyForDNS": true + } + } +} diff --git a/browser/components/enterprisepolicies/helpers/sample_websitefilter.json b/browser/components/enterprisepolicies/helpers/sample_websitefilter.json new file mode 100644 index 0000000000..6d20cd5ff0 --- /dev/null +++ b/browser/components/enterprisepolicies/helpers/sample_websitefilter.json @@ -0,0 +1,9 @@ +{ + "policies": { + "WebsiteFilter": { + "Block": ["*://*.mozilla.org/*", "invalid_pattern"], + + "Exceptions": ["*://*.mozilla.org/*about*"] + } + } +} |