lots of JSlint based cleanup
This commit is contained in:
parent
463b9a10b4
commit
b8d2eaeca0
14 changed files with 138 additions and 95 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -2,4 +2,5 @@ node_modules/
|
|||
.DS_Store
|
||||
*.swp
|
||||
|
||||
content_scripts/dist/*.js
|
||||
content_scripts/dist/*.js
|
||||
web-ext-artifacts/
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
Web API Manager
|
||||
===
|
||||
|
||||
|
||||
Overview
|
||||
---
|
||||
This extension allows users to selectively allow different hosts on the web
|
||||
|
@ -9,6 +10,7 @@ security and privacy sensitive web users to limit the attack surface presented
|
|||
to websites, and to limit websites to the functionality they actually need
|
||||
to carry out user-serving purposes.
|
||||
|
||||
|
||||
Background
|
||||
---
|
||||
Web browsers gain staggering numbers of new features, without their users
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
/*jslint es6: true*/
|
||||
/*global chrome, browser, window, URI*/
|
||||
/*global window*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const {packingLib, standards, storageLib} = window.WEB_API_MANAGER;
|
||||
const {packingLib, standards, storageLib, domainMatcherLib} = window.WEB_API_MANAGER;
|
||||
const rootObject = window.browser || window.chrome;
|
||||
const defaultKey = "(default)";
|
||||
|
||||
|
@ -11,6 +12,9 @@
|
|||
// that should be blocked on matching domains.
|
||||
let domainRules;
|
||||
|
||||
// The extension depends on this fetch happening before the DOM on any
|
||||
// pages is loaded. The Chrome and Firefox docs *do not* promise this,
|
||||
// but in testing this is always the case.
|
||||
storageLib.get(function (loadedDomainRules) {
|
||||
domainRules = loadedDomainRules;
|
||||
});
|
||||
|
@ -18,7 +22,7 @@
|
|||
// Manage the state of the browser activity, by displaying the number
|
||||
// of origins / frames
|
||||
const updateBrowserActionBadge = function (activeInfo) {
|
||||
const {tabId, windowId} = activeInfo;
|
||||
const tabId = activeInfo.tabId;
|
||||
rootObject.tabs.executeScript(
|
||||
tabId,
|
||||
{
|
||||
|
@ -50,39 +54,8 @@
|
|||
rootObject.tabs.onActivated.addListener(updateBrowserActionBadge);
|
||||
rootObject.windows.onFocusChanged.addListener(updateBrowserActionBadge);
|
||||
|
||||
// Inject the blocking settings for each visited domain / frame.
|
||||
const extractHostFromUrl = function (url) {
|
||||
const uri = URI(url);
|
||||
return uri.hostname();
|
||||
};
|
||||
|
||||
const matchingUrlReduceFunction = function (domain, prev, next) {
|
||||
if (prev) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
const domainRegex = new RegExp(next);
|
||||
if (domainRegex.test(domain)) {
|
||||
return next;
|
||||
}
|
||||
|
||||
return prev;
|
||||
};
|
||||
|
||||
const whichDomainRuleMatches = function (hostName) {
|
||||
// of the URL being requested.
|
||||
const matchingUrlReduceFunctionBound = matchingUrlReduceFunction.bind(undefined, hostName);
|
||||
const matchingPattern = Object
|
||||
.keys(domainRules)
|
||||
.filter((aRule) => aRule !== defaultKey)
|
||||
.sort()
|
||||
.reduce(matchingUrlReduceFunctionBound, undefined);
|
||||
|
||||
return matchingPattern || defaultKey;
|
||||
};
|
||||
|
||||
// Listen for updates to the domain rules from the config page.
|
||||
rootObject.runtime.onMessage.addListener(function (request, sender, sendResponse) {
|
||||
rootObject.runtime.onMessage.addListener(function (request, ignore, sendResponse) {
|
||||
const [label, data] = request;
|
||||
if (label === "rulesUpdate") {
|
||||
domainRules = data;
|
||||
|
@ -90,12 +63,16 @@
|
|||
}
|
||||
|
||||
if (label === "rulesForDomains") {
|
||||
const ruleForDomain = data.map(whichDomainRuleMatches);
|
||||
const mapping = {};
|
||||
for (let i = 0; i < ruleForDomain.length; i += 1) {
|
||||
mapping[data[i]] = ruleForDomain[i];
|
||||
}
|
||||
sendResponse(mapping);
|
||||
|
||||
const matchHostNameBound = domainMatcherLib.matchHostName.bind(undefined, Object.keys(domainRules));
|
||||
const rulesForDomains = data.map(matchHostNameBound);
|
||||
const domainToRuleMapping = {};
|
||||
|
||||
data.forEach(function (aHostName, index) {
|
||||
domainToRuleMapping[aHostName] = rulesForDomains[index] || defaultKey;
|
||||
});
|
||||
|
||||
sendResponse(domainToRuleMapping);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
@ -106,15 +83,15 @@
|
|||
};
|
||||
const requestOptions = ["blocking", "responseHeaders"];
|
||||
|
||||
chrome.webRequest.onHeadersReceived.addListener(function (details) {
|
||||
// Inject the blocking settings for each visited domain / frame.
|
||||
rootObject.webRequest.onHeadersReceived.addListener(function (details) {
|
||||
|
||||
const url = details.url;
|
||||
const hostName = extractHostFromUrl(url);
|
||||
|
||||
// Decide which set of blocking rules to use, depending on the host
|
||||
// of the URL being requested.
|
||||
const matchingDomainKey = whichDomainRuleMatches(hostName);
|
||||
const standardsToBlock = domainRules[matchingDomainKey];
|
||||
const matchingDomainRule = domainMatcherLib.matchUrl(Object.keys(domainRules), url);
|
||||
const standardsToBlock = domainRules[matchingDomainRule || defaultKey];
|
||||
|
||||
const options = Object.keys(standards);
|
||||
const packedValues = packingLib.pack(options, standardsToBlock);
|
||||
|
@ -129,4 +106,4 @@
|
|||
};
|
||||
|
||||
}, requestFilter, requestOptions);
|
||||
}());
|
||||
}());
|
||||
|
|
|
@ -34,9 +34,9 @@
|
|||
</div>
|
||||
</section>
|
||||
<script src="../lib/init.js"></script>
|
||||
<script src="../lib/storage.js"></script>
|
||||
<script src="../content_scripts/dist/defaults.js"></script>
|
||||
<script src="../content_scripts/dist/standards.js"></script>
|
||||
<script src="../lib/storage.js"></script>
|
||||
<script src="js/lib/vue.js"></script>
|
||||
<script src="js/state.js"></script>
|
||||
<script src="js/components/domain-rules.vue.js"></script>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*jslint es6: true*/
|
||||
/*global window, browser, Vue*/
|
||||
/*jslint es6: true, this: true*/
|
||||
/*global window, Vue*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
|
@ -65,6 +65,6 @@
|
|||
isDefault: function (domainName) {
|
||||
return domainName === "(default)";
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}());
|
|
@ -1,6 +1,7 @@
|
|||
/*jslint es6: true*/
|
||||
/*global window, browser, Vue*/
|
||||
/*jslint es6: true, this: true*/
|
||||
/*global window, Vue*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const standardsDefaults = window.WEB_API_MANAGER.defaults;
|
||||
|
||||
|
|
|
@ -6,25 +6,15 @@
|
|||
const rootObject = (window.browser || window.chrome);
|
||||
const doc = window.document;
|
||||
const standards = window.WEB_API_MANAGER.standards;
|
||||
const defaultConservativeRules = window.WEB_API_MANAGER.defaults.conservative;
|
||||
const {storageLib, stateLib} = window.WEB_API_MANAGER;
|
||||
const defaultDomain = "(default)";
|
||||
|
||||
const state = stateLib.generateStateObject(defaultDomain, standards);
|
||||
|
||||
const onSettingsLoaded = function (settingsResults) {
|
||||
|
||||
let loadedDomainRules;
|
||||
|
||||
if (Object.keys(settingsResults).length !== 0) {
|
||||
loadedDomainRules = settingsResults;
|
||||
} else {
|
||||
loadedDomainRules = Object.create(null);
|
||||
loadedDomainRules[defaultDomain] = defaultConservativeRules;
|
||||
}
|
||||
const onSettingsLoaded = function (loadedDomainRules) {
|
||||
|
||||
state.setDomainRules(loadedDomainRules);
|
||||
|
||||
|
||||
const vm = new Vue({
|
||||
el: doc.body,
|
||||
data: state
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*jslint es6: true*/
|
||||
/*jslint es6: true, this: true*/
|
||||
/*global window, browser, Vue*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
window.WEB_API_MANAGER.stateLib = {};
|
||||
|
||||
|
@ -12,27 +13,27 @@
|
|||
domainRules: {},
|
||||
domainNames: [],
|
||||
selectedStandards: [],
|
||||
|
||||
|
||||
setDomainRules: function (newDomainRules) {
|
||||
this.domainRules = newDomainRules;
|
||||
this.domainNames = Object.keys(newDomainRules);
|
||||
this.selectedStandards = this.domainRules[this.selectedDomain];
|
||||
},
|
||||
|
||||
|
||||
setSelectedDomain: function (newDomain) {
|
||||
this.selectedDomain = newDomain;
|
||||
this.selectedStandards = this.domainRules[newDomain];
|
||||
},
|
||||
|
||||
|
||||
setSelectedStandards: function (selectedStandards) {
|
||||
this.selectedStandards = selectedStandards;
|
||||
this.domainRules[this.selectedDomain] = selectedStandards;
|
||||
},
|
||||
|
||||
|
||||
deleteDomainRule: function (domainToDelete) {
|
||||
delete this.domainRules[domainToDelete];
|
||||
this.domainNames = Object.keys(this.domainRules);
|
||||
|
||||
|
||||
// If we're deleted the domain thats currently selected, then
|
||||
// select the default domain.
|
||||
if (this.selectedDomain === domainToDelete) {
|
||||
|
|
46
lib/domainmatcher.js
Normal file
46
lib/domainmatcher.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*jslint es6: true*/
|
||||
/*global window*/
|
||||
(function () {
|
||||
"use strict";
|
||||
const defaultKey = "(default)";
|
||||
|
||||
const extractHostNameFromUrl = function (url) {
|
||||
const uri = window.URI(url);
|
||||
return uri.hostname();
|
||||
};
|
||||
|
||||
const matchingUrlReduceFunction = function (hostName, prev, next) {
|
||||
if (prev) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
const domainRegex = new RegExp(next);
|
||||
if (domainRegex.test(hostName)) {
|
||||
return next;
|
||||
}
|
||||
|
||||
return prev;
|
||||
};
|
||||
|
||||
const matchHostName = function (domainRegExes, hostName) {
|
||||
// of the URL being requested.
|
||||
const matchingUrlReduceFunctionBound = matchingUrlReduceFunction.bind(undefined, hostName);
|
||||
const matchingPattern = domainRegExes
|
||||
.filter((aRule) => aRule !== defaultKey)
|
||||
.sort()
|
||||
.reduce(matchingUrlReduceFunctionBound, undefined);
|
||||
|
||||
return matchingPattern || undefined;
|
||||
};
|
||||
|
||||
const matchUrl = function (domainRegExes, url) {
|
||||
const hostName = extractHostNameFromUrl(url);
|
||||
return matchHostName(domainRegExes, hostName);
|
||||
};
|
||||
|
||||
window.WEB_API_MANAGER.domainMatcherLib = {
|
||||
matchHostName,
|
||||
matchUrl
|
||||
};
|
||||
|
||||
}());
|
|
@ -1,3 +1,7 @@
|
|||
/*global window*/
|
||||
// Initial content script for the Web API manager extension, that creates
|
||||
// the "namespace" we'll use for all the content scripts in the extension.
|
||||
window.WEB_API_MANAGER = {};
|
||||
(function () {
|
||||
"use strict";
|
||||
window.WEB_API_MANAGER = {};
|
||||
}());
|
||||
|
|
21
lib/pack.js
21
lib/pack.js
|
@ -1,8 +1,9 @@
|
|||
/*jslint es6: true*/
|
||||
/*jslint es6: true, for: true, bitwise: true*/
|
||||
/*global window*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const {btoa, atob} = window;
|
||||
const bucketSize = 8;
|
||||
|
||||
const bufferToBase64 = function (buf) {
|
||||
|
@ -16,7 +17,7 @@
|
|||
const binstr = atob(base64);
|
||||
const buf = new Uint8Array(binstr.length);
|
||||
Array.prototype.forEach.call(binstr, function (ch, i) {
|
||||
buf[i] = ch.charCodeAt(0);
|
||||
buf[i] = ch.charCodeAt(0);
|
||||
});
|
||||
return buf;
|
||||
};
|
||||
|
@ -47,11 +48,13 @@
|
|||
const binnedOptions = options.reduce(binToBucketSizeFunc, []);
|
||||
const bitFields = new Uint8Array(numBuckets);
|
||||
|
||||
for (let i = 0; i < numBuckets; i += 1) {
|
||||
let i, j;
|
||||
|
||||
for (i = 0; i < numBuckets; i += 1) {
|
||||
let bitfield = 0;
|
||||
let currentBucket = binnedOptions[i];
|
||||
|
||||
for (let j = 0; j < currentBucket.length; j += 1) {
|
||||
for (j = 0; j < currentBucket.length; j += 1) {
|
||||
|
||||
let currentOption = currentBucket[j];
|
||||
if (selected.indexOf(currentOption) !== -1) {
|
||||
|
@ -68,7 +71,6 @@
|
|||
|
||||
const unpack = function (options, data) {
|
||||
|
||||
const numBuckets = Math.ceil(options.length / bucketSize);
|
||||
const binToBucketSizeFunc = binOptionsReduceFunction.bind(undefined, bucketSize);
|
||||
options.sort();
|
||||
|
||||
|
@ -77,11 +79,13 @@
|
|||
|
||||
const result = [];
|
||||
|
||||
for (let i = 0; i < bitFields.length; i += 1) {
|
||||
let i, j;
|
||||
|
||||
for (i = 0; i < bitFields.length; i += 1) {
|
||||
let currentBitField = bitFields[i];
|
||||
let currentOptionsBin = binnedOptions[i];
|
||||
|
||||
for (let j = 0; j < bucketSize; j += 1) {
|
||||
for (j = 0; j < bucketSize; j += 1) {
|
||||
if (currentBitField & (1 << j)) {
|
||||
let currentOption = currentOptionsBin[j];
|
||||
result.push(currentOption);
|
||||
|
@ -93,6 +97,7 @@
|
|||
};
|
||||
|
||||
window.WEB_API_MANAGER.packingLib = {
|
||||
pack, unpack
|
||||
pack,
|
||||
unpack
|
||||
};
|
||||
}());
|
|
@ -3,13 +3,25 @@
|
|||
(function () {
|
||||
"use strict";
|
||||
|
||||
const browserObj = window.browser || window.chrome;
|
||||
const rootObject = window.browser || window.chrome;
|
||||
const defaultConservativeRules = window.WEB_API_MANAGER.defaults.conservative;
|
||||
const webApiManagerKeySettingsKey = "webApiManagerDomainRules";
|
||||
const storageObject = browserObj.storage;
|
||||
const storageObject = rootObject.storage;
|
||||
|
||||
const get = function (callback) {
|
||||
storageObject.local.get(webApiManagerKeySettingsKey, function (results) {
|
||||
callback(results && results[webApiManagerKeySettingsKey]);
|
||||
|
||||
let loadedDomainRules = results && results[webApiManagerKeySettingsKey];
|
||||
|
||||
// If there are no currently saved domain rules, then create
|
||||
// a stubbed out one, using the conservative blocking rule set.
|
||||
if (!loadedDomainRules || Object.keys(loadedDomainRules).length === 0) {
|
||||
loadedDomainRules = {
|
||||
"(default)": defaultConservativeRules
|
||||
};
|
||||
}
|
||||
|
||||
callback(loadedDomainRules);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"manifest_version": 2,
|
||||
"name": "WebAPI Manager",
|
||||
"version": "0.5",
|
||||
"version": "0.7",
|
||||
"description": "Improves browser security by restricting page access to parts of the Web API.",
|
||||
"icons": {
|
||||
"48": "images/uic-48.png",
|
||||
|
@ -37,20 +37,21 @@
|
|||
"lib/init.js",
|
||||
"lib/js.cookie.js",
|
||||
"lib/storage.js",
|
||||
"lib/URI.js"
|
||||
"lib/URI.js",
|
||||
"content_scripts/dist/defaults.js"
|
||||
],
|
||||
"background": {
|
||||
"scripts": [
|
||||
"lib/init.js",
|
||||
"lib/pack.js",
|
||||
"lib/storage.js",
|
||||
"lib/URI.js",
|
||||
"content_scripts/dist/standards.js",
|
||||
"content_scripts/dist/defaults.js",
|
||||
"lib/storage.js",
|
||||
"background_scripts/background.js"
|
||||
]
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "config/index.html",
|
||||
"chrome_style": true
|
||||
"page": "config/index.html"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
/*jslint es6: true*/
|
||||
/*global window*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
const rootObject = window.browser || window.chrome;
|
||||
const configureButton = window.document.getElementById("config-page-link");
|
||||
const doc = window.document;
|
||||
const configureButton = doc.getElementById("config-page-link");
|
||||
|
||||
configureButton.addEventListener("click", function (event) {
|
||||
rootObject.runtime.openOptionsPage();
|
||||
|
@ -21,26 +24,26 @@
|
|||
const message = ["rulesForDomains", uniqueDomains];
|
||||
rootObject.runtime.sendMessage(message, function (response) {
|
||||
|
||||
const listGroupElm = document.querySelector("ul.list-group");
|
||||
const listGroupElm = doc.querySelector("ul.list-group");
|
||||
const domainNames = Object.keys(response);
|
||||
|
||||
domainNames.forEach(function (aDomain) {
|
||||
const domainRule = response[aDomain];
|
||||
|
||||
const liElm = document.createElement("li");
|
||||
|
||||
const liElm = doc.createElement("li");
|
||||
liElm.className = "list-group-item";
|
||||
if (domainRule !== "(default)") {
|
||||
liElm.className += " list-group-item-success";
|
||||
}
|
||||
|
||||
const spanElm = document.createElement("span");
|
||||
const spanElm = doc.createElement("span");
|
||||
spanElm.className = "badge";
|
||||
|
||||
const badgeText = document.createTextNode(domainRule);
|
||||
const badgeText = doc.createTextNode(domainRule);
|
||||
spanElm.appendChild(badgeText);
|
||||
liElm.appendChild(spanElm);
|
||||
|
||||
const textElm = document.createTextNode(aDomain);
|
||||
const textElm = doc.createTextNode(aDomain);
|
||||
liElm.appendChild(textElm);
|
||||
listGroupElm.appendChild(liElm);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue