Commit 2d6acb36 authored by Thomas Rientjes's avatar Thomas Rientjes
Browse files

Refactor security preferences module

parent e8a90e2c
/**
* NSA Policy model.
*/
class NsaPolicy {
/**
* Creates an NSA Policy.
*/
constructor () {
this.UNTRUSTED = {};
this.TRUSTED = {
'js': true,
'webgl': true,
'java': true,
'flash': true,
'silverlight': true,
'plugin': true,
'media': true,
'frame': true,
'font': true
};
this.DEFAULT = {
'frame': true
};
this['mozilla.org'] = 1;
this['mozilla.com'] = 1;
this['mozilla.net'] = 1;
this['google.com'] = 1;
this['gstatic.com'] = 1;
this['googleapis.com'] = 1;
this['yahoo.com'] = 1;
this['yimg.com'] = 1;
this['yahooapis.com'] = 1;
this['hotmail.com'] = 1;
this['live.com'] = 1;
this['wlxrs.com'] = 1;
this['securecode.com'] = 1;
this['recaptcha.net'] = 1;
this['noscript.net'] = 1;
this['flashgot.net'] = 1;
this['informaction.com'] = 1;
this['youtube.com'] = 1;
this['ytimg.com'] = 1;
}
};
exports.NsaPolicy = NsaPolicy;
......@@ -13,6 +13,13 @@
*/
var sdkPreferences = require('sdk/preferences/service');
/**
* The NSA policy model.
*
* @var {NsaPolicy} NsaPolicy
*/
var { NsaPolicy } = require('../models/nsa-policy');
/**
* Contains generic utility functions.
*
......@@ -20,6 +27,13 @@ var sdkPreferences = require('sdk/preferences/service');
*/
var utilities = require('./utilities.js');
/**
* Indicates whether or not this module has been initialized.
*
* @var {boolean} initialized
*/
var initialized = false;
/**
* Bindings between preferences and slider levels.
*
......@@ -38,141 +52,221 @@ var preferenceBindings = {
'svg.in-content.enabled': [true, true, false]
};
// The Security Settings prefs in question.
const kSliderPref = 'extensions.tor-browser-settings@torproject.org.security_level';
const kCustomPref = 'extensions.tor-browser-settings@torproject.org.security_custom';
const nsaPolicyPref = 'extensions.nsa.policy';
const nsaStandard = {
'js': true,
'webgl': true,
'java': true,
'flash': true,
'silverlight': true,
'plugin': true,
'media': true,
'frame': true,
'font': true
/**
* Bindings between NSA policy rules and slider levels.
*
* @var {Object} nsaPolicyBindings
*/
var nsaPolicyBindings = {
// NSA policy rule name Standard Safer Safest
'js': [true, false, false],
'webgl': [true, false, false],
'java': [true, false, false],
'flash': [true, false, false],
'silverlight': [true, false, false],
'plugin': [true, false, false],
'media': [true, false, false],
'frame': [true, false, false],
'font': [true, false, false]
};
const nsaSafest = {
'js': false,
'webgl': false,
'java': false,
'flash': false,
'silverlight': false,
'plugin': false,
'media': false,
'frame': false,
'font': false
/**
* The NoScript Anywhere policy preference domain.
*
* @constant NSA_POLICY_DOMAIN
* @type {string}
*/
const NSA_POLICY_DOMAIN = 'extensions.nsa.policy';
/**
* The custom security settings preference domain.
*
* @constant SECURITY_CUSTOM_DOMAIN
* @type {string}
*/
const SECURITY_CUSTOM_DOMAIN = 'extensions.tor-browser-settings@torproject.org.security_custom';
/**
* The security level preference domain.
*
* @constant SECURITY_LEVEL_DOMAIN
* @type {string}
*/
const SECURITY_LEVEL_DOMAIN = 'extensions.tor-browser-settings@torproject.org.security_level';
/**
* Generates a NoScript Anywhere policy string.
*
* @returns {string} NSA policy string
* @private
*/
function generateNsaPolicyString () {
let securityLevel, nsaPolicyString, nsaPolicy;
securityLevel = sdkPreferences.get(SECURITY_LEVEL_DOMAIN);
nsaPolicyString = sdkPreferences.get(NSA_POLICY_DOMAIN);
try {
nsaPolicy = JSON.parse(nsaPolicyString);
} catch (exception) {
nsaPolicy = new NsaPolicy();
}
nsaPolicy.UNTRUSTED = generateNsaRuleset('UNTRUSTED', securityLevel);
nsaPolicy.TRUSTED = generateNsaRuleset('TRUSTED', securityLevel);
nsaPolicy.DEFAULT = generateNsaRuleset('DEFAULT', securityLevel);
return JSON.stringify(nsaPolicy);
};
// ### Prefs
var generate_nsa_policy = function () {
let currentNsaPolicy = sdkPreferences.get(nsaPolicyPref) || '{"UNTRUSTED":{},"TRUSTED":{"js":true,"webgl":true,"java":true,"flash":true,"silverlight":true,"plugin":true,"media":true,"frame":true,"font":true},"DEFAULT":{"frame":true},"mozilla.org":1,"mozilla.com":1,"mozilla.net":1,"google.com":1,"gstatic.com":1,"googleapis.com":1,"yahoo.com":1,"yimg.com":1,"yahooapis.com":1,"hotmail.com":1,"live.com":1,"wlxrs.com":1,"securecode.com":1,"recaptcha.net":1,"noscript.net":1,"flashgot.net":1,"informaction.com":1,"youtube.com":1,"ytimg.com":1}';
let securitySliderValue = sdkPreferences.get(kSliderPref);
currentNsaPolicy = JSON.parse(currentNsaPolicy);
currentNsaPolicy.UNTRUSTED = {};
if (securitySliderValue === 0 || securitySliderValue === '0') {
currentNsaPolicy.TRUSTED = nsaStandard;
currentNsaPolicy.DEFAULT = nsaStandard;
} else {
currentNsaPolicy.TRUSTED = nsaSafest;
currentNsaPolicy.DEFAULT = nsaSafest;
}
return JSON.stringify(currentNsaPolicy);
/**
* Generates a NoScript Anywhere ruleset.
*
* @returns {Object} NSA ruleset
* @private
*/
function generateNsaRuleset (nsaRulesetName, securityLevel) {
let nsaRuleset = {};
if (nsaRulesetName === 'TRUSTED' || nsaRulesetName === 'DEFAULT') {
let nsaRuleNames = Object.keys(nsaPolicyBindings);
nsaRuleNames.forEach(function (nsaRuleName) {
nsaRuleset[nsaRuleName] = nsaPolicyBindings[nsaRuleName][securityLevel];
});
}
return nsaRuleset;
};
// __write_setting_to_prefs(settingIndex)__.
// Take a given setting index and write the appropriate pref values
// to the pref database.
var write_setting_to_prefs = function (settingIndex) {
Object.keys(preferenceBindings).forEach(
prefName => sdkPreferences.set(
prefName, preferenceBindings[prefName][settingIndex]));
/**
* Applies preference bindings for a given security level.
*
* @param {number} securityLevel
* @private
*/
function applyPreferenceBindings (securityLevel) {
let preferenceNames = Object.keys(preferenceBindings);
preferenceNames.forEach(function (preferenceName) {
let preferenceValue = preferenceBindings[preferenceName][securityLevel];
sdkPreferences.set(preferenceName, preferenceValue);
});
};
// __read_setting_from_prefs()__.
// Read the current pref values, and decide if any of our
// security settings matches. Otherwise return null.
var read_setting_from_prefs = function () {
let prefNames = Object.keys(preferenceBindings);
for (let settingIndex of [0, 1, 2]) {
let possibleSetting = true;
// For the given settingIndex, check if all current pref values
// match the setting.
for (let prefName of prefNames) {
if (preferenceBindings[prefName][settingIndex] !==
sdkPreferences.get(prefName)) {
possibleSetting = false;
}
}
if (possibleSetting) {
// We have a match!
return settingIndex;
/**
* Attempts to determine the current security level.
*
* @returns {number|null} Security level or null
* @private
*/
function determineCurrentSecurityLevel () {
let preferenceNames = Object.keys(preferenceBindings);
for (let securityLevel of [0, 1, 2]) {
let perfectMatch = true;
for (let preferenceName of preferenceNames) {
let expectedValue, actualValue;
expectedValue = preferenceBindings[preferenceName][securityLevel];
actualValue = sdkPreferences.get(preferenceName);
if (expectedValue !== actualValue) {
perfectMatch = false;
break;
}
}
if (perfectMatch === true) {
return securityLevel;
}
}
}
// No matching setting; return null.
return null;
return null;
};
// __watch_security_prefs(onSettingChanged)__.
// Whenever a pref bound to the security slider changes, onSettingChanged
// is called with the new security setting value (0,1,2 or null).
// Returns a zero-arg function that ends this binding.
var watch_security_prefs = function (onSettingChanged) {
let prefNames = Object.keys(preferenceBindings);
let unbindFuncs = [];
for (let prefName of prefNames) {
unbindFuncs.push(utilities.bindChangeHandlerToPreference(prefName, () => onSettingChanged(read_setting_from_prefs()), true));
}
// Call all the unbind functions.
return () => unbindFuncs.forEach(unbind => unbind());
/**
* Adds change observers to browser preferences.
*
* @returns {function} Observer unbinder
* @private
*/
function addObserversToPreferences (handler) {
let preferenceNames, unbindFunctions;
preferenceNames = Object.keys(preferenceBindings);
unbindFunctions = [];
for (let preferenceName of preferenceNames) {
let unbindObserver = utilities.addObserverToPreference(preferenceName, function () {
let currentSecurityLevel = determineCurrentSecurityLevel();
return handler(currentSecurityLevel);
}, true);
unbindFunctions.push(unbindObserver);
}
// Can be invoked to unbind all previously registered observers.
return () => unbindFunctions.forEach(unbind => unbind());
};
// __initialized__.
// Have we called initialize() yet?
var initialized = false;
/**
* Defines the behavior of the various preference bindings.
*/
exports.initialize = function () {
if (initialized === true) {
return;
}
// __initialize()__.
// Defines the behavior of "extensions.torbutton.security_custom",
// "extensions.torbutton.security_slider", and the security-sensitive
// prefs declared in preferenceBindings.
var initialize = function () {
// Only run once.
if (initialized) {
return;
}
initialized = true;
sdkPreferences.set(nsaPolicyPref, generate_nsa_policy());
// When security_custom is set to false, apply security_slider setting
// to the security-sensitive prefs.
utilities.bindChangeHandlerToPreference(kCustomPref, function (custom) {
if (custom === false) {
write_setting_to_prefs(sdkPreferences.get(kSliderPref));
// Signal module initialization.
initialized = true;
// Observer for the custom security level preference.
utilities.addObserverToPreference(SECURITY_CUSTOM_DOMAIN, function (customized) {
if (customized === false) {
let securityLevel = sdkPreferences.get(SECURITY_LEVEL_DOMAIN);
sdkPreferences.set(NSA_POLICY_DOMAIN, generateNsaPolicyString());
applyPreferenceBindings(securityLevel);
}
}, true);
// If security_slider is given a new value, then security_custom should
// be set to false.
utilities.bindChangeHandlerToPreference(kSliderPref, function (prefIndex) {
sdkPreferences.set(kCustomPref, false);
sdkPreferences.set(nsaPolicyPref, generate_nsa_policy());
write_setting_to_prefs(prefIndex);
});
// If a security-sensitive pref changes, then decide if the set of pref values
// constitutes a security_slider setting or a custom value.
watch_security_prefs(settingIndex => {
if (settingIndex === null) {
sdkPreferences.set(kCustomPref, true);
} else {
sdkPreferences.set(kSliderPref, settingIndex);
sdkPreferences.set(kCustomPref, false);
}
});
};
// Export initialize() function for external use.
exports.initialize = initialize;
// Observer for the security level preference.
utilities.addObserverToPreference(SECURITY_LEVEL_DOMAIN, function (securityLevel) {
sdkPreferences.set(SECURITY_CUSTOM_DOMAIN, false);
sdkPreferences.set(NSA_POLICY_DOMAIN, generateNsaPolicyString());
applyPreferenceBindings(securityLevel);
});
// Adds observers to related browser preferences.
addObserversToPreferences(function (securityLevel) {
if (securityLevel === null) {
sdkPreferences.set(SECURITY_CUSTOM_DOMAIN, true);
} else {
sdkPreferences.set(SECURITY_LEVEL_DOMAIN, securityLevel);
sdkPreferences.set(SECURITY_CUSTOM_DOMAIN, false);
}
});
};
......@@ -30,7 +30,7 @@ var sdkPreferences = require('sdk/preferences/service');
/**
* Bind a change handler to a specific preference domain.
*/
exports.bindChangeHandlerToPreference = function (domain, handler, initialize = false) {
exports.addObserverToPreference = function (domain, handler, initialize = false) {
let updatePreference, observer;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment