[Pkg-mozext-commits] [no-resource-uri-leak] 06/09: Added a redirect checker (based on Torbutton code by Yawning); UI improvement; Refactored to allow policy reloading.
Hema Prathaban
hemaprathaban-guest at moszumanska.debian.org
Tue Jul 4 17:19:13 UTC 2017
This is an automated email from the git hooks/post-receive script.
hemaprathaban-guest pushed a commit to branch upstream
in repository no-resource-uri-leak.
commit a12d0867d3719a6476243e6dfdd2d7af505cc730
Author: nord-stream <nord-stream at ochaken.jp.eu.org>
Date: Sun Jul 24 16:05:16 2016 +0000
Added a redirect checker (based on Torbutton code by Yawning); UI improvement; Refactored to allow policy reloading.
---
preferences.json | 54 ++++++++++-
src/main.js | 36 ++++++--
src/resource-filter/content-policy.js | 95 --------------------
src/resource-filter/init.js | 33 ++++---
src/resource-filter/process/content-policy.js | 61 +++++++++++++
src/resource-filter/process/filter.js | 124 ++++++++++++++++++++++++++
version_info | 2 +-
7 files changed, 285 insertions(+), 120 deletions(-)
diff --git a/preferences.json b/preferences.json
index 989f9ed..eb000bd 100644
--- a/preferences.json
+++ b/preferences.json
@@ -1,9 +1,57 @@
[
{
- "name": "blockChromeURIs"
+ "name": "uri.resource.enableBlocking"
,"type": "bool"
- ,"value": false
+ ,"value": true
+ ,"title": "Block access to resource:// URIs from Web"
+ ,"description": "This is the extesion's main feature."
+ }
+ ,{
+ "name": "uri.chrome.enableBlocking"
+ ,"type": "bool"
+ ,"value": true
,"title": "Block Web-exposed subset of chrome:// URIs"
- ,"description": "Enabling it may break certain extensions or badly designed Web sites. (Requires a restart)"
+ ,"description": "RECOMMENDED for privacy. Enabling it may break certain extensions or badly designed Web sites."
+ }
+ ,{
+ "name": "redirect.enableMasking"
+ ,"type": "bool"
+ ,"value": true
+ ,"title": "Uniformly filter disallowed redirects"
+ ,"description": "This eliminates one known source of an information leak"
+ }
+ ,{
+ "name": "uri.resource.exposedList"
+ ,"type": "string"
+ ,"value": ""
+ ,"title": "Exposed resource:// domains"
+ ,"description": "This may harm your privacy: Only for debugging and as temporary measures (Separated with spaces and/or commas)"
+ }
+ ,{
+ "name": "uri.chrome.exposedList"
+ ,"type": "string"
+ ,"value": ""
+ ,"title": "Exposed chrome:// domains"
+ ,"description": "This may harm your privacy: Only for debugging and as temporary measures (Separated with spaces and/or commas)"
+ }
+ ,{
+ "name": "uri.about.restricted"
+ ,"type": "bool"
+ ,"value": false
+ ,"title": "Restrict about: pages by default (for paranoids)"
+ ,"description": "This may break certain add-ons or the browser's internal pages"
+ }
+ ,{
+ "name": "debug.enabled"
+ ,"type": "bool"
+ ,"value": false
+ ,"title": "Enable debugging messages (for hackers)"
+ }
+ ,{
+ "name": "control.update"
+ ,"type": "control"
+ ,"label": "Update"
+ ,"title": "Update the policy"
+ ,"description": "Push this button for changes to take effect."
}
]
diff --git a/src/main.js b/src/main.js
index 414e80d..6692acf 100644
--- a/src/main.js
+++ b/src/main.js
@@ -25,11 +25,33 @@ vim: ts=4 noet ai */
'use strict';
-// Stop all access attempts to resource:// URIs from the Web
-const filteredDomain = void 0; // everything
-const blockChromeURIs = !!require ('sdk/simple-prefs').prefs.blockChromeURIs;
-
-console.log (require ('sdk/simple-prefs').prefs);
-// The core code is under MPL-2.0
-require ('./resource-filter/init').addFilter (filteredDomain, blockChromeURIs);
+// We are using a library under MPL-2.0 here
+const {enablePolicy} = require ('./resource-filter/init');
+
+
+/* Preferences keys */
+const PREF_REDIRECT_MASKED = 'redirect.enableMasking';
+const PREF_URI_RESOURCE_BLOCKED = 'uri.resource.enableBlocking';
+const PREF_URI_CHROME_BLOCKED = 'uri.chrome.enableBlocking';
+const PREF_URI_CHROME_WHITELIST = 'uri.chrome.exposedList';
+const PREF_URI_RESOURCE_WHITELIST = 'uri.resource.exposedList';
+const PREF_RESTRICT_ABOUT = 'uri.about.restricted';
+const PREF_DEBUG_ENABLED = 'debug.enabled';
+
+const _$prefs = require ('sdk/simple-prefs').prefs;
+
+const update = $prefs => enablePolicy ({__proto__: null
+ ,enableDebug: !!$prefs[PREF_DEBUG_ENABLED]
+ ,blockResourceURIs: !!$prefs[PREF_URI_RESOURCE_BLOCKED]
+ ,blockChromeURIs: !!$prefs[PREF_URI_CHROME_BLOCKED]
+ ,enableRedirectMasking: !!$prefs[PREF_REDIRECT_MASKED]
+ ,restrictAboutPages: !!$prefs[PREF_RESTRICT_ABOUT]
+ ,exposedResourceDomains:
+ String ($prefs[PREF_URI_RESOURCE_WHITELIST]).split (/[,\s]+/)
+ ,exposedChromeDomains:
+ String ($prefs[PREF_URI_CHROME_WHITELIST]).split (/[,\s]+/)
+});
+
+update (_$prefs);
+require ('sdk/simple-prefs').on ('control.update', () => void update (_$prefs));
diff --git a/src/resource-filter/content-policy.js b/src/resource-filter/content-policy.js
deleted file mode 100644
index dd47683..0000000
--- a/src/resource-filter/content-policy.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
- * 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/. */
-
-/* ResourceFilter: A direct workaround for https://bugzil.la/863246 */
-
-/* Internal script: Loaded into every process (parent or content) */
-
-const {components, Cc, Ci, Cm, Cr} = require ('chrome');
-const unload = require ('sdk/system/unload');
-const {XPCOMUtils} = require ('resource://gre/modules/XPCOMUtils.jsm');
-
-const domains = new Set; // disallowed domains: any if empty
-const isDenied = domain => 1 > domains.size || domains.has ('' + domain);
-let allowChromeURIs = true;
-
-const policy = {__proto__: null
- /* nsISupports */
- ,QueryInterface: XPCOMUtils.generateQI (['nsIContentPolicy', 'nsIFactory'])
-
- /* nsIFactory */
- ,createInstance (outer, id) {
- if (outer) {
- throw Cr.NS_ERROR_NO_AGGREGATION;
- }
- return this.QueryInterface (id);
- }
-
- /* nsIContentPolicy */
- ,shouldLoad (typeCode, uri, originUri, node, expectedMime, extra, principal) {
- // Note: view-source: scheme is no longer accessible from content (thus no leaks)
- if (!uri || !uri.schemeIs ('resource') || !originUri
- || originUri.schemeIs ('chrome') || originUri.schemeIs ('resource')
- || originUri.schemeIs ('view-source')) {
-
- if (allowChromeURIs || !uri.schemeIs ('chrome')) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
- }
-
- // Non-matching domain or a resource directly loaded into a tab
- if (!isDenied (uri.host) || Ci.nsIContentPolicy.TYPE_DOCUMENT === typeCode) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- // Whitelist about:addons (Add-ons compatibility)
- if (originUri.schemeIs ('about') && 'addons' === originUri.path) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-
- return Ci.nsIContentPolicy.REJECT_REQUEST;
- }
- ,shouldProcess (typeCode, uri, originUri, node, expectedMime, extra) {
- return Ci.nsIContentPolicy.ACCEPT;
- }
-};
-
-const contractId = '@addons.mozilla.org/resource-masking-policy;1';
-const classId = components.ID ('{ee3ec15e-6743-47a4-96a7-b551935c93c6}');
-const description = 'Masks resource URIs against content';
-const category = 'content-policy';
-
-const init = (... args) => {
- const registrar = Cm.QueryInterface (Ci.nsIComponentRegistrar);
- const categoryManager = Cc['@mozilla.org/categorymanager;1']
- .getService (Ci.nsICategoryManager);
-
- const {resourceDomain, blockChromeURIs} = args.pop ();
- try {
- if ('string' === typeof resourceDomain) throw void 0;
- [... resourceDomain].forEach (domain => domains.add ('' + domain));
- } catch (e) {
- resourceDomain && domains.add ('' + resourceDomain);
- }
-
- if (blockChromeURIs) {
- allowChromeURIs = false;
- }
-
- registrar.registerFactory (classId, description, contractId, policy);
- categoryManager.addCategoryEntry (category, contractId, contractId, false, true);
-
- unload.when (() => {
- categoryManager.deleteCategoryEntry (category, contractId, false);
- registrar.unregisterFactory (classId, policy);
- });
-};
-
-try {
- require ('sdk/remote/child').process.port.on ('init', init);
-} catch (e) {
- // Not multiprocess
- exports.init = init;
-}
diff --git a/src/resource-filter/init.js b/src/resource-filter/init.js
index 74ca34c..62895dc 100644
--- a/src/resource-filter/init.js
+++ b/src/resource-filter/init.js
@@ -1,25 +1,30 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+/* -*- indent-tabs-mode: nil; js-indent-level: 2; tab-width: 2 -*-
* 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/. */
-/* ResourceFilter: A direct workaround for https://bugzil.la/863246 */
-
-/**
- Prevents content from loading resource:// URIs without breaking add-ons.
- @param resourceDomain (optional) e.g. 'gre' for resource://gre/
- @param blockChromeURIs (optional) set to block chrome:// resources
+/*
+ ResourceFilter: A direct workaround for https://bugzil.la/863246
+ API revision: 2
*/
-exports.addFilter = (resourceDomain, blockChromeURIs) => {
+
+const setPolicy = (() => {
try {
const {processes, remoteRequire} = require ('sdk/remote/parent');
- remoteRequire ('./content-policy', module);
-
+ remoteRequire ('./process/filter', module);
// For every current and future process
- processes.forEvery (process => void process.port.emit ('init'
- , {resourceDomain, blockChromeURIs}));
+ return options =>
+ processes.forEvery (process => void process.port.emit ('setPolicy', options));
} catch (e) {
// Not multiprocess
- require ('./content-policy').init ({resourceDomain, blockChromeURIs});
+ return require ('./process/filter').setPolicy;
}
-};
+}) ();
+
+
+/**
+ Prevents content from loading resource:// URIs without breaking add-ons.
+ Can be called multiple times to update the policy.
+*/
+exports.enablePolicy = options => void setPolicy (options || {});
+
diff --git a/src/resource-filter/process/content-policy.js b/src/resource-filter/process/content-policy.js
new file mode 100644
index 0000000..bcf921d
--- /dev/null
+++ b/src/resource-filter/process/content-policy.js
@@ -0,0 +1,61 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2; tab-width: 2 -*-
+ * 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/. */
+
+/* ResourceFilter: A direct workaround for https://bugzil.la/863246 */
+
+const unload = require ('sdk/system/unload');
+const {components, Cc, Ci, Cm, Cr} = require ('chrome');
+const {XPCOMUtils} = require ('resource://gre/modules/XPCOMUtils.jsm');
+
+const registrar = Cm.QueryInterface (Ci.nsIComponentRegistrar);
+const categoryManager = Cc['@mozilla.org/categorymanager;1']
+ .getService (Ci.nsICategoryManager);
+
+/**
+ The 'evaluate' callback must not perform any blocking processing.
+*/
+exports.registerContentPolicy = ({evaluate, contractId, uuid, description}) => {
+ if ('function' != typeof evaluate) {
+ throw new TypeError ('evaluate must be a function');
+ }
+
+ const policy = {__proto__: null
+ /* nsISupports */
+ ,QueryInterface: XPCOMUtils.generateQI (['nsIContentPolicy', 'nsIFactory'])
+
+ /* nsIFactory */
+ ,createInstance (outer, id) {
+ if (outer) {
+ throw Cr.NS_ERROR_NO_AGGREGATION;
+ }
+ return this.QueryInterface (id);
+ }
+
+ /* nsIContentPolicy */
+ ,shouldLoad (... args) {
+ try {
+ if (evaluate (... args)) {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+ } catch (e) {
+ console.exception (e);
+ }
+ return Ci.nsIContentPolicy.REJECT_REQUEST;
+ }
+ ,shouldProcess () {
+ return Ci.nsIContentPolicy.ACCEPT;
+ }
+ };
+
+ const classId = components.ID (uuid);
+ const category = 'content-policy';
+ registrar.registerFactory (classId, description, contractId, policy);
+ categoryManager.addCategoryEntry (category, contractId, contractId, false, true);
+
+ unload.when (() => {
+ categoryManager.deleteCategoryEntry (category, contractId, false);
+ registrar.unregisterFactory (classId, policy);
+ });
+};
diff --git a/src/resource-filter/process/filter.js b/src/resource-filter/process/filter.js
new file mode 100644
index 0000000..f2f295a
--- /dev/null
+++ b/src/resource-filter/process/filter.js
@@ -0,0 +1,124 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2; tab-width: 2 -*-
+ * 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/. */
+
+/* ResourceFilter: A direct workaround for https://bugzil.la/863246 */
+
+/* Internal script: Loaded into every process (parent or content) */
+
+const {Ci, Cr} = require ('chrome');
+const unload = require ('sdk/system/unload');
+const {io: ioService, obs: observerService}
+ = require ('resource://gre/modules/Services.jsm').Services;
+
+const {registerContentPolicy} = require ('./content-policy');
+
+// Default values
+const policyState = {__proto__: null
+ ,debug: false
+ ,exposedResourceDomains: new Set
+ ,exposedChromeDomains: new Set
+ ,blockResourceUris: true
+ ,blockChromeUris: true
+ ,filterRedirects: true
+ ,whitelistAboutUris: false
+ ,secureAboutUris: new Set (['addons', 'home', 'preferences', 'support', 'newtab', 'debugging', 'config', 'downloads', 'profiles', 'sessionrestore', 'privatebrowsing', 'plugins'])
+ ,veryInsecureAboutUris: new Set (['blank', 'srcdoc'])
+};
+
+
+// Note: view-source: scheme is no longer accessible from content (thus no leaks)
+const isWhitelistedOrigin = u => (!u)
+ || u.schemeIs ('chrome') || u.schemeIs ('resource') || u.schemeIs ('view-source')
+ || u.schemeIs ('about') && (!policyState.veryInsecureAboutUris.has (u.path))
+ && (policyState.secureAboutUris.has (u.path) || policyState.whitelistAboutUris);
+
+const shouldBeBlocked = u => (!u)
+ || policyState.blockResourceUris
+ && u.schemeIs ('resource') && (!policyState.exposedResourceDomains.has (u.host))
+ || policyState.blockChromeUris
+ && u.schemeIs ('chrome') && (!policyState.exposedChromeDomains.has (u.host));
+
+registerContentPolicy ({__proto__: null
+ ,contractId: '@addons.mozilla.org/resource-masking-policy;1'
+ ,uuid: '{ee3ec15e-6743-47a4-96a7-b551935c93c6}'
+ ,description: 'Masks resource URIs against content'
+ ,evaluate (typeCode, uri, originUri, node, expectedMime, extra, principal) {
+ if (!shouldBeBlocked (uri) || isWhitelistedOrigin (originUri)) {
+ return true;
+ }
+
+ // Allow documents directly loaded into a tab
+ if (Ci.nsIContentPolicy.TYPE_DOCUMENT === typeCode) {
+ return true;
+ }
+
+ policyState.debug && console.warn ('ResourceFilter: Rejected'
+ , uri.spec, originUri.spec, node, principal);
+ return false;
+ }
+});
+
+/*
+ Based on TorButton code, by Yawning Angel
+ From https://git.schwanenlied.me/yawning/torbutton/src/fa67687df5fc72c0b6085d9941331277d32319f3/src/components/content-policy.js
+*/
+
+// Install a HTTP response handler to check for redirects to URLs with schemes
+// that should be internal to the browser. There's various safeguards and
+// checks that cause the body to be unavailable, but the `onLoad()` behavior
+// is inconsistent, which results in leaking information about the specific
+// user agent instance (eg: what addons are installed).
+const requestObserver = {__proto__: null
+ ,observe (aSubject, aTopic, aData) {
+ const aChannel = aSubject.QueryInterface (Ci.nsIHttpChannel);
+ const aStatus = aChannel.responseStatus;
+
+ try {
+ // If this is a redirect...
+ //
+ // Note: Technically `304 Not Modifed` isn't a redirect, but receiving that
+ // to the proscribed schemes is nonsensical.
+ if (aStatus >= 300 && aStatus < 400) {
+ const location = aChannel.getResponseHeader ('Location');
+ const aUri = ioService.newURI (location, null, null);
+ // And it's redirecting into the browser or addon's internal URLs...
+ if (shouldBeBlocked (aUri) || aUri.schemeIs ('about')) {
+ // Cancel the request.
+ policyState.debug && console.warn ('ResourceFilter: Cancelled redirect'
+ , aUri.spec, aChannel.owner);
+ aSubject.cancel (Cr.NS_BINDING_ABORTED);
+ }
+ }
+ } catch (e) {
+ console.exception (e);
+ }
+ }
+};
+
+try {
+ observerService.addObserver (requestObserver, 'http-on-examine-response', false);
+ unload.when (() =>
+ observerService.removeObserver (requestObserver, 'http-on-examine-response'));
+} catch (e) {}
+
+const setPolicy = ({enableRedirectMasking, blockChromeURIs, blockResourceURIs
+ , enableDebug, restrictAboutPages, exposedResourceDomains, exposedChromeDomains}) =>
+{
+ policyState.filterRedirects = !!enableRedirectMasking;
+ policyState.blockChromeUris = !!blockChromeURIs;
+ policyState.blockResourceUris = !!blockResourceURIs;
+ policyState.debug = !!enableDebug;
+ policyState.whitelistAboutUris = !restrictAboutPages;
+ policyState.exposedResourceDomains = new Set (exposedResourceDomains || []);
+ policyState.exposedChromeDomains = new Set (exposedChromeDomains || []);
+};
+
+try {
+ require ('sdk/remote/child').process.port.on ('setPolicy'
+ , (_, options) => setPolicy (options));
+} catch (e) {
+ // Not multiprocess
+ exports.setPolicy = setPolicy;
+}
diff --git a/version_info b/version_info
index eaa0efa..5b2b92d 100644
--- a/version_info
+++ b/version_info
@@ -22,7 +22,7 @@
addon_id="no-resource-uri-leak"
# Canonical version of the addon (may be converted into different formats on build)
-addon_version="0.2.1"
+addon_version="1.0.0"
# Alpha versions (may not be feature complete): x.y.z~a1, x.y.z~a2, ...
# Beta versions (feature-frozen): x.y.z~b1, x.y.z~b2, ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/no-resource-uri-leak.git
More information about the Pkg-mozext-commits
mailing list