summaryrefslogtreecommitdiffstats
path: root/browser/components/enterprisepolicies/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/enterprisepolicies/helpers')
-rw-r--r--browser/components/enterprisepolicies/helpers/BookmarksPolicies.sys.mjs301
-rw-r--r--browser/components/enterprisepolicies/helpers/ProxyPolicies.sys.mjs111
-rw-r--r--browser/components/enterprisepolicies/helpers/WebsiteFilter.sys.mjs188
-rw-r--r--browser/components/enterprisepolicies/helpers/moz.build14
-rw-r--r--browser/components/enterprisepolicies/helpers/sample.json18
-rw-r--r--browser/components/enterprisepolicies/helpers/sample_bookmarks.json37
-rw-r--r--browser/components/enterprisepolicies/helpers/sample_proxy.json13
-rw-r--r--browser/components/enterprisepolicies/helpers/sample_websitefilter.json14
8 files changed, 696 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..fcaefe3fcc
--- /dev/null
+++ b/browser/components/enterprisepolicies/helpers/sample.json
@@ -0,0 +1,18 @@
+{
+ "policies": {
+ "BlockAboutProfiles": true,
+ "DontCheckDefaultBrowser": true,
+
+ "FlashPlugin": {
+ "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..5393526aab
--- /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"
+ }
+ ]
+ }
+} \ No newline at end of file
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..d70b6cdea5
--- /dev/null
+++ b/browser/components/enterprisepolicies/helpers/sample_websitefilter.json
@@ -0,0 +1,14 @@
+{
+ "policies": {
+ "WebsiteFilter": {
+ "Block": [
+ "*://*.mozilla.org/*",
+ "invalid_pattern"
+ ],
+
+ "Exceptions": [
+ "*://*.mozilla.org/*about*"
+ ]
+ }
+ }
+}