Bug 1429178 - Policy: Implement website blocklist/allowlist. r=mixedpuppy

MozReview-Commit-ID: EAX0VwxlggK

--HG--
extra : rebase_source : d795b96a0318e7b2bb21bf24c15413111628aa73
This commit is contained in:
Felipe Gomes 2018-03-28 09:29:17 -05:00
parent 043bf247e7
commit 7d1da2c7e5
11 changed files with 223 additions and 3 deletions

View File

@ -11,10 +11,11 @@ XPCOMUtils.defineLazyServiceGetter(this, "gXulStore",
"nsIXULStore");
XPCOMUtils.defineLazyModuleGetters(this, {
BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
AddonManager: "resource://gre/modules/AddonManager.jsm",
BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
WebsiteFilter: "resource:///modules/policies/WebsiteFilter.jsm",
});
const PREF_LOGLEVEL = "browser.policies.loglevel";
@ -579,7 +580,14 @@ var Policies = {
}
});
}
}
},
"WebsiteFilter": {
onBeforeUIStartup(manager, param) {
this.filter = new WebsiteFilter(param.Block || [], param.Exceptions || []);
}
},
};
/*

View File

@ -0,0 +1,108 @@
/* 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/. */
"use strict";
/*
* 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 a whitelist approach,
* by using the special "<all_urls>" pattern for the blocklist, and then
* adding all whitelisted websites on the exceptions list.
*
* Note that this module only blocks top-level website navigations. It doesn't
* 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.
*/
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
const LIST_LENGTH_LIMIT = 1000;
const PREF_LOGLEVEL = "browser.policies.loglevel";
XPCOMUtils.defineLazyGetter(this, "log", () => {
let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
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.jsm for details.
maxLogLevel: "error",
maxLogLevelPref: PREF_LOGLEVEL,
});
});
var EXPORTED_SYMBOLS = [ "WebsiteFilter" ];
function WebsiteFilter(blocklist, exceptionlist) {
let blockArray = [], exceptionArray = [];
for (let i = 0; i < blocklist.length && i < LIST_LENGTH_LIMIT; i++) {
try {
let pattern = new MatchPattern(blocklist[i]);
blockArray.push(pattern);
log.debug(`Pattern added to WebsiteFilter.Block list: ${blocklist[i]}`);
} catch (e) {
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]);
exceptionArray.push(pattern);
log.debug(`Pattern added to WebsiteFilter.Exceptions list: ${exceptionlist[i]}`);
} catch (e) {
log.error(`Invalid pattern on WebsiteFilter.Exceptions: ${exceptionlist[i]}`);
}
}
if (exceptionArray.length) {
this._exceptionsPatterns = new MatchPatternSet(exceptionArray);
}
Services.obs.addObserver(this, "http-on-modify-request", true);
}
WebsiteFilter.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]),
observe(subject, topic, data) {
let channel, isDocument = false;
try {
channel = subject.QueryInterface(Ci.nsIHttpChannel);
isDocument = channel.isDocument;
} catch (e) {
return;
}
// Only filter document accesses
if (!isDocument) {
return;
}
if (this._blockPatterns.matches(channel.URI)) {
if (!this._exceptionsPatterns ||
!this._exceptionsPatterns.matches(channel.URI)) {
// NS_ERROR_BLOCKED_BY_POLICY displays the error message
// designed for policy-related blocks.
channel.cancel(Cr.NS_ERROR_BLOCKED_BY_POLICY);
}
}
}
};

View File

@ -10,4 +10,5 @@ with Files("**"):
EXTRA_JS_MODULES.policies += [
'BookmarksPolicies.jsm',
'ProxyPolicies.jsm',
'WebsiteFilter.jsm',
]

View File

@ -0,0 +1,14 @@
{
"policies": {
"WebsiteFilter": {
"Block": [
"*://*.mozilla.org/*",
"invalid_pattern"
],
"Exceptions": [
"*://*.mozilla.org/*about*"
]
}
}
}

View File

@ -492,6 +492,29 @@
"type": "boolean"
}
}
},
"WebsiteFilter": {
"description": "Blocks websites from being visited. The parameters take an array of Match Patterns, as documented in https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Match_patterns. Only http/https accesses are supported at the moment. The arrays are limited to 1000 entries each.",
"first_available": "60.0",
"enterprise_only": "true",
"type": "object",
"properties": {
"Block": {
"type": "array",
"items": {
"type": "string"
}
},
"Exceptions": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}

View File

@ -6,6 +6,8 @@ support-files =
opensearch.html
opensearchEngine.xml
policytest.xpi
policy_websitefilter_block.html
policy_websitefilter_exception.html
[browser_policies_basic_tests.js]
[browser_policies_broken_json.js]
@ -45,3 +47,4 @@ support-files =
[browser_policy_search_engine.js]
[browser_policy_searchbar.js]
[browser_policy_set_homepage.js]
[browser_policy_websitefilter.js]

View File

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const SUPPORT_FILES_PATH = "http://mochi.test:8888/browser/browser/components/enterprisepolicies/tests/browser";
const BLOCKED_PAGE = `${SUPPORT_FILES_PATH}/policy_websitefilter_block.html`;
const EXCEPTION_PAGE = `${SUPPORT_FILES_PATH}/policy_websitefilter_exception.html`;
add_task(async function test() {
await setupPolicyEngineWithJson({
"policies": {
"WebsiteFilter": {
"Block": [
"*://mochi.test/*policy_websitefilter_*"
],
"Exceptions": [
"*://mochi.test/*_websitefilter_exception*"
]
}
}
});
await checkBlockedPage(BLOCKED_PAGE, true);
await checkBlockedPage(EXCEPTION_PAGE, false);
});

View File

@ -28,6 +28,23 @@ function checkUnlockedPref(prefName, prefValue) {
EnterprisePolicyTesting.checkPolicyPref(prefName, prefValue, false);
}
// Checks that a page was blocked by seeing if it was replaced with about:neterror
async function checkBlockedPage(url, expectedBlocked) {
await BrowserTestUtils.withNewTab({
gBrowser,
url,
waitForLoad: false,
waitForStateStop: true,
}, async function() {
await BrowserTestUtils.waitForCondition(async function() {
let blocked = await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
return content.document.documentURI.startsWith("about:neterror");
});
return blocked == expectedBlocked;
}, `Page ${url} block was correct (expected=${expectedBlocked}).`);
});
}
add_task(async function policies_headjs_startWithCleanSlate() {
if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
await setupPolicyEngineWithJson("");

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>This page should be blocked</title>
</head>
<body>
This page should not be seen.
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>This page should not be blocked</title>
</head>
<body>
This page should be seen.
</body>
</html>

View File

@ -157,6 +157,7 @@ XPC_MSG_DEF(NS_ERROR_REDIRECT_LOOP , "The request failed as a r
XPC_MSG_DEF(NS_ERROR_UNSAFE_CONTENT_TYPE , "The request failed because the content type returned by the server was not a type expected by the channel")
XPC_MSG_DEF(NS_ERROR_REMOTE_XUL , "Attempt to access remote XUL document that is not in website's whitelist")
XPC_MSG_DEF(NS_ERROR_LOAD_SHOWED_ERRORPAGE , "The load caused an error page to be displayed.")
XPC_MSG_DEF(NS_ERROR_BLOCKED_BY_POLICY , "The request was blocked by a policy set by the system administrator.")
XPC_MSG_DEF(NS_ERROR_FTP_LOGIN , "FTP error while logging in")
XPC_MSG_DEF(NS_ERROR_FTP_CWD , "FTP error while changing directory")