commit 3e1c144a729f373b3ee70fd8d2a16646b2b6ad12 Author: Arthur Edelstein arthuredelstein@gmail.com Date: Wed Jun 6 21:54:58 2018 -0700
Bug 26128: Adapt security slider to WebExtensions version of NoScript
We try to maintain the same security slider behavior as for the legacy version of NoScript.
This patch uses a few tricks:
1. Using a LegacyExtensionContext (defined in LegacyExtensionsUtils.jsm) to send JSON objects to NoScript via sendMessage. 2. Taking advantage of an existing invocation of browser.runtime.onMessage.addListener(...) in NoScript's code that accepts a JSON object for updating NoScript's settings. 3. Providing NoScript with settings for a "site" whose "domain" is "http:", which causes NoScript to match non-https sites.
(Thanks to Sukhbir Singh for help.) --- src/chrome/content/torbutton.js | 2 + src/modules/noscript-control.js | 126 ++++++++++++++++++++++++++++++++++++++++ src/modules/security-prefs.js | 8 +-- 3 files changed, 131 insertions(+), 5 deletions(-)
diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js index 7f750bc..11548d4 100644 --- a/src/chrome/content/torbutton.js +++ b/src/chrome/content/torbutton.js @@ -11,6 +11,7 @@ let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); let { showDialog } = Cu.import("resource://torbutton/modules/utils.js", {}); let { getLocale, unescapeTorString } = Cu.import("resource://torbutton/modules/utils.js", {}); let SecurityPrefs = Cu.import("resource://torbutton/modules/security-prefs.js", {}); +let NoScriptControl = Cu.import("resource://torbutton/modules/noscript-control.js", {}); let { bindPrefAndInit, observe } = Cu.import("resource://torbutton/modules/utils.js", {});
Cu.importGlobalProperties(["XMLHttpRequest"]); @@ -242,6 +243,7 @@ function torbutton_init() { torbutton_log(3, 'called init()');
SecurityPrefs.initialize(); + NoScriptControl.initialize();
if (m_tb_wasinited) { return; diff --git a/src/modules/noscript-control.js b/src/modules/noscript-control.js new file mode 100644 index 0000000..6270efe --- /dev/null +++ b/src/modules/noscript-control.js @@ -0,0 +1,126 @@ +// # NoScript settings control (for binding to Security Slider) + +/* jshint esversion:6 */ + +// ## Utilities + +const { utils: Cu } = Components; +const { LegacyExtensionContext } = + Cu.import("resource://gre/modules/LegacyExtensionsUtils.jsm", {}); +const { bindPrefAndInit } = + Cu.import("resource://torbutton/modules/utils.js", {}); + +// ## NoScript settings + +// Minimum and maximum capability states as controlled by NoScript. +const max_caps = ["fetch", "font", "frame", "media", "other", "script", "webgl"]; +const min_caps = ["frame", "other"]; + +// Untrusted capabilities for [Standard, Safer, Safest] safety levels. +const untrusted_caps = [ + max_caps, // standard safety: neither http nor https + ["frame", "font", "other"], // safer: http + min_caps, // safest: neither http nor https +]; + +// Default capabilities for [Standard, Safer, Safest] safety levels. +const default_caps = [ + max_caps, // standard: both http and https + ["fetch", "font", "frame", "other", "script", "webgl"], // safer: https only + min_caps, // safest: both http and https +]; + +// __noscriptSettings(safetyLevel)__. +// Produces NoScript settings with policy according to +// the safetyLevel which can be: +// 0 = Standard, 1 = Safer, 2 = Safest +// +// At the "Standard" safety level, we leave all sites at +// default with maximal capabilities. Essentially no content +// is blocked. +// +// At "Safer", we set all http sites to untrusted, +// and all https sites to default. Scripts are only permitted +// on https sites. Neither type of site is supposed to allow +// media, but both allow fonts (as we used in legacy NoScript). +// +// At "Safest", all sites are at default with minimal +// capabilities. Most things are blocked. +let noscriptSettings = safetyLevel => ( + { + "type": "NoScript.updateSettings", + "policy": { + "DEFAULT": { + "capabilities": default_caps[safetyLevel], + "temp": false + }, + "TRUSTED": { + "capabilities": max_caps, + "temp": false + }, + "UNTRUSTED": { + "capabilities": untrusted_caps[safetyLevel], + "temp": false + }, + "sites": { + "trusted": [], + "untrusted": [[], ["http:"], []][safetyLevel], + "custom": {}, + "temp": [] + }, + "enforced": true, + "autoAllowTop": false + }, + "tabId": -1 + }); + +// ## Communications + +// The extension ID for NoScript (WebExtension) +const noscriptID = "{73a6fe31-595d-460b-a920-fcc0f8843232}"; + +// A mock extension object that can communicate with another extension +// via the WebExtensions sendMessage/onMessage mechanism. +let extensionContext = new LegacyExtensionContext({ id : noscriptID }); + +// The component that handles WebExtensions' sendMessage. +let messageManager = extensionContext.messenger.messageManagers[0]; + +// __setNoScriptSettings(settings)__. +// NoScript listens for internal settings with onMessage. We can send +// a new settings JSON object according to NoScript's +// protocol and these are accepted! See the use of +// `browser.runtime.onMessage.addListener(...)` in NoScript's bg/main.js. +let sendNoScriptSettings = settings => + extensionContext.messenger.sendMessage(messageManager, settings, noscriptID); + +// __setNoScriptSafetyLevel(safetyLevel)__. +// Set NoScript settings according to a particular safety level +// (security slider level): 0 = Standard, 1 = Safer, 2 = Safest +let setNoScriptSafetyLevel = safetyLevel => + sendNoScriptSettings(noscriptSettings(safetyLevel)); + +// ## Slider binding + +// __securitySliderToSafetyLevel(sliderState)__. +// Converts the "extensions.torbutton.security_slider" pref value +// to a "safety level" value: 0 = Standard, 1 = Safer, 2 = Safest +let securitySliderToSafetyLevel = sliderState => [undefined, 2, 1, 1, 0][sliderState]; + +// Ensure binding only occurs once. +let initialized = false; + +// __initialize()__. +// The main function that binds the NoScript settings to the security +// slider pref state. +var initialize = () => { + if (initialized) { + return; + } + bindPrefAndInit( + "extensions.torbutton.security_slider", + sliderState => setNoScriptSafetyLevel(securitySliderToSafetyLevel(sliderState))); +}; + +// Export initialize() function for external use. +let EXPORTED_SYMBOLS = ["initialize"]; diff --git a/src/modules/security-prefs.js b/src/modules/security-prefs.js index 3d3630b..d269a1f 100644 --- a/src/modules/security-prefs.js +++ b/src/modules/security-prefs.js @@ -16,19 +16,17 @@ let log = (level, msg) => logger.log(level, msg); // __kSecuritySettings__. // A table of all prefs bound to the security slider, and the value // for each security setting. Note that 2-m and 3-m are identical, -// corresponding to the old 2-medium-high setting. +// corresponding to the old 2-medium-high setting. We also separately +// bind NoScript settings to the extensions.torbutton.security_slider +// (see noscript-control.js). const kSecuritySettings = { // Preference name : [0, 1-high 2-m 3-m 4-low] "javascript.options.ion" : [, false, false, false, true ], "javascript.options.baselinejit" : [, false, false, false, true ], "javascript.options.native_regexp" : [, false, false, false, true ], - "noscript.forbidMedia" : [, true, true, true, false], "media.webaudio.enabled" : [, false, false, false, true ], "mathml.disabled" : [, true, true, true, false], "gfx.font_rendering.opentype_svg.enabled" : [, false, false, false, true ], - "noscript.global" : [, false, false, false, true ], - "noscript.globalHttpsWhitelist" : [, false, true, true, false], - "noscript.forbidFonts" : [, true, false, false, false], "svg.in-content.enabled" : [, false, true, true, true ], };