mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 17:55:50 +00:00
Bug 1429178 - Policy: Implement website blocklist/allowlist. r=mixedpuppy
MozReview-Commit-ID: EAX0VwxlggK --HG-- extra : rebase_source : d795b96a0318e7b2bb21bf24c15413111628aa73
This commit is contained in:
parent
043bf247e7
commit
7d1da2c7e5
@ -11,10 +11,11 @@ XPCOMUtils.defineLazyServiceGetter(this, "gXulStore",
|
|||||||
"nsIXULStore");
|
"nsIXULStore");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
|
|
||||||
ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
|
|
||||||
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
AddonManager: "resource://gre/modules/AddonManager.jsm",
|
||||||
|
BookmarksPolicies: "resource:///modules/policies/BookmarksPolicies.jsm",
|
||||||
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
|
CustomizableUI: "resource:///modules/CustomizableUI.jsm",
|
||||||
|
ProxyPolicies: "resource:///modules/policies/ProxyPolicies.jsm",
|
||||||
|
WebsiteFilter: "resource:///modules/policies/WebsiteFilter.jsm",
|
||||||
});
|
});
|
||||||
|
|
||||||
const PREF_LOGLEVEL = "browser.policies.loglevel";
|
const PREF_LOGLEVEL = "browser.policies.loglevel";
|
||||||
@ -579,7 +580,14 @@ var Policies = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
"WebsiteFilter": {
|
||||||
|
onBeforeUIStartup(manager, param) {
|
||||||
|
this.filter = new WebsiteFilter(param.Block || [], param.Exceptions || []);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
108
browser/components/enterprisepolicies/helpers/WebsiteFilter.jsm
Normal file
108
browser/components/enterprisepolicies/helpers/WebsiteFilter.jsm
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -10,4 +10,5 @@ with Files("**"):
|
|||||||
EXTRA_JS_MODULES.policies += [
|
EXTRA_JS_MODULES.policies += [
|
||||||
'BookmarksPolicies.jsm',
|
'BookmarksPolicies.jsm',
|
||||||
'ProxyPolicies.jsm',
|
'ProxyPolicies.jsm',
|
||||||
|
'WebsiteFilter.jsm',
|
||||||
]
|
]
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"policies": {
|
||||||
|
"WebsiteFilter": {
|
||||||
|
"Block": [
|
||||||
|
"*://*.mozilla.org/*",
|
||||||
|
"invalid_pattern"
|
||||||
|
],
|
||||||
|
|
||||||
|
"Exceptions": [
|
||||||
|
"*://*.mozilla.org/*about*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -492,6 +492,29 @@
|
|||||||
"type": "boolean"
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ support-files =
|
|||||||
opensearch.html
|
opensearch.html
|
||||||
opensearchEngine.xml
|
opensearchEngine.xml
|
||||||
policytest.xpi
|
policytest.xpi
|
||||||
|
policy_websitefilter_block.html
|
||||||
|
policy_websitefilter_exception.html
|
||||||
|
|
||||||
[browser_policies_basic_tests.js]
|
[browser_policies_basic_tests.js]
|
||||||
[browser_policies_broken_json.js]
|
[browser_policies_broken_json.js]
|
||||||
@ -45,3 +47,4 @@ support-files =
|
|||||||
[browser_policy_search_engine.js]
|
[browser_policy_search_engine.js]
|
||||||
[browser_policy_searchbar.js]
|
[browser_policy_searchbar.js]
|
||||||
[browser_policy_set_homepage.js]
|
[browser_policy_set_homepage.js]
|
||||||
|
[browser_policy_websitefilter.js]
|
||||||
|
@ -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);
|
||||||
|
});
|
@ -28,6 +28,23 @@ function checkUnlockedPref(prefName, prefValue) {
|
|||||||
EnterprisePolicyTesting.checkPolicyPref(prefName, prefValue, false);
|
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() {
|
add_task(async function policies_headjs_startWithCleanSlate() {
|
||||||
if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
|
if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
|
||||||
await setupPolicyEngineWithJson("");
|
await setupPolicyEngineWithJson("");
|
||||||
|
@ -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>
|
@ -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>
|
@ -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_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_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_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_LOGIN , "FTP error while logging in")
|
||||||
XPC_MSG_DEF(NS_ERROR_FTP_CWD , "FTP error while changing directory")
|
XPC_MSG_DEF(NS_ERROR_FTP_CWD , "FTP error while changing directory")
|
||||||
|
Loading…
Reference in New Issue
Block a user