Created
November 23, 2015 06:23
-
-
Save wshanks/accc083077f6996de7f8 to your computer and use it in GitHub Desktop.
NoScript Integration plugin for Pentadactyl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright ©2010-2014 Kris Maglione <maglione.k at Gmail> | |
* Distributable under the terms of the MIT license. | |
* | |
* Documentation is at the tail of this file. | |
*/ | |
"use strict"; | |
if (!("noscriptOverlay" in window)) { | |
if (!userContext.noscriptIgnoreMissing) | |
dactyl.echoerr("This plugin requires the NoScript add-on."); | |
throw Finished(); | |
} | |
/* | |
* this.globalJS ? !this.alwaysBlockUntrustedContent || !this.untrustedSites.matches(s) | |
* : this.jsPolicySites.matches(s) && !this.untrustedSites.matches(s) && !this.isForbiddenByHttpsStatus(s)); | |
*/ | |
function getSites() { | |
// This logic comes directly from NoScript. To my mind, it's insane. | |
const ns = services.noscript; | |
const global = options["script"]; | |
const groups = { allowed: ns.jsPolicySites, temp: ns.tempSites, untrusted: ns.untrustedSites }; | |
const show = new RealSet(options["noscript-list"]); | |
const sites = window.noscriptOverlay.getSites(); | |
const blockUntrusted = global && ns.alwaysBlockUntrustedContent; | |
let res = []; | |
for (let site of array.iterValues(Array.concat(sites.topSite, sites))) { | |
let ary = []; | |
let untrusted = groups.untrusted.matches(site); | |
let matchingSite = null; | |
if (!untrusted) | |
matchingSite = groups.allowed.matches(site) || blockUntrusted && site; | |
let enabled = Boolean(matchingSite); | |
if (site == sites.topSite && !ns.dom.getDocShellForWindow(content).allowJavascript) | |
enabled = false; | |
let hasPort = /:\d+$/.test(site); | |
if (enabled && !global || untrusted) { | |
if (!enabled || global) | |
matchingSite = untrusted; | |
if (hasPort && ns.ignorePorts) | |
if (site = groups.allowed.matches(site.replace(/:\d+$/, ""))) | |
matchingSite = site; | |
ary.push(matchingSite); | |
} | |
else { | |
if ((!hasPort || ns.ignorePorts) && (show.has("full") || show.has("base"))) { | |
let domain = !ns.isForbiddenByHttpsStatus(site) && ns.getDomain(site); | |
if (domain && ns.isJSEnabled(domain) == enabled) { | |
ary = util.subdomains(domain); | |
if (!show.has("base") && ary.length > 1) | |
ary = ary.slice(1); | |
if (!show.has("full")) | |
ary = ary.slice(0, 1); | |
} | |
} | |
if (show.has("address") || ary.length == 0) { | |
ary.push(site); | |
if (hasPort && ns.ignorePorts) { | |
site = site.replace(/:\d+$/, ""); | |
if (!groups.allowed.matches(site)) | |
ary.push(site); | |
} | |
} | |
} | |
res = res.concat(ary); | |
} | |
let seen = new RealSet(); | |
return res.filter(function (h) { | |
let res = !seen.has(h); | |
seen.add(h); | |
return res; | |
}); | |
} | |
function getObjects() { | |
let sites = noscriptOverlay.getSites(); | |
let general = [], specific = []; | |
for (let group of values(sites.pluginExtras)) | |
for (let obj of array.iterValues(group)) { | |
if (!obj.placeholder && (ns.isAllowedObject(obj.url, obj.mime) || obj.tag)) | |
continue; | |
specific.push(obj.mime + "@" + obj.url); | |
general.push("*@" + obj.url); | |
general.push("*@" + obj.site); | |
} | |
sites = buffer.allFrames().map(function (f) f.location.host); | |
for (let filter of values(options["noscript-objects"])) { | |
let host = util.getHost(util.split(filter, /@/, 2)[1]); | |
if (sites.some(function (s) s == host)) | |
specific.push(filter); | |
} | |
let seen = new RealSet(); | |
return specific.concat(general).filter(function (site) { | |
let res = !seen.has(site); | |
seen.add(site); | |
return res; | |
}); | |
} | |
var onUnload = overlay.overlayObject(gBrowser, { | |
// Extend NoScript's bookmarklet handling hack to the command-line | |
// Modified from NoScript's own wrapper. | |
loadURIWithFlags: function loadURIWithFlags(url) { | |
let args = arguments; | |
function load() loadURIWithFlags.superapply(gBrowser, args); | |
if (!commandline.command || !util.isDactyl(Components.stack.caller)) | |
return load(); | |
try { | |
for (let [cmd, args] of commands.parseCommands(commandline.command)) | |
var origURL = args.literalArg; | |
let isJS = function isJS(url) /^(?:data|javascript):/i.test(url); | |
let allowJS = prefs.get("noscript.allowURLBarJS", true); | |
if (isJS(origURL) && allowJS) { | |
if (services.noscript.executeJSURL(origURL, load)) | |
return; | |
} | |
else if (url != origURL && isJS(url)) { | |
if(services.noscript.handleBookmark(url, load)) | |
return; | |
} | |
} | |
catch (e) { | |
util.reportError(e); | |
} | |
return load(); | |
} | |
}); | |
highlight.loadCSS(literal(/* | |
NoScriptAllowed color: green; | |
NoScriptBlocked color: #444; font-style: italic; | |
NoScriptTemp color: blue; | |
NoScriptUntrusted color: #c00; font-style: italic; | |
*/)); | |
let groupProto = {}; | |
["temp", "jsPolicy", "untrusted"].forEach(function (group) | |
memoize(groupProto, group, function () services.noscript[group + "Sites"].matches(this.site))); | |
let groupDesc = { | |
NoScriptTemp: "Temporarily allowed", | |
NoScriptAllowed: "Allowed permanently", | |
NoScriptUntrusted: "Untrusted", | |
NoScriptBlocked: "Blocked" | |
}; | |
function splitContext(context, list) { | |
for (let [name, title, filter] of values(list)) { | |
let ctxt = context.split(name); | |
ctxt.title = [title]; | |
ctxt.filters.push(filter); | |
} | |
} | |
completion.noscriptObjects = function (context) { | |
let whitelist = options.get("noscript-objects").set; | |
context = context.fork(); | |
context.compare = CompletionContext.Sort.unsorted; | |
context.generate = getObjects; | |
context.keys = { | |
text: util.identity, | |
description: function (key) whitelist.has(key) ? "Allowed" : "Forbidden" | |
}; | |
splitContext(context, getObjects, [ | |
["forbidden", "Forbidden objects", function (item) !whitelist.has(item.item)], | |
["allowed", "Allowed objects", function (item) whitelist.has(item.item)]]); | |
}; | |
completion.noscriptSites = function (context) { | |
context.compare = CompletionContext.Sort.unsorted; | |
context.generate = getSites; | |
context.keys = { | |
text: util.identity, | |
description: function (site) groupDesc[this.highlight] + | |
(this.groups.untrusted && this.highlight != "NoScriptUntrusted" ? " (untrusted)" : ""), | |
highlight: function (site) this.groups.temp ? "NoScriptTemp" : | |
this.groups.jsPolicy ? "NoScriptAllowed" : | |
this.groups.untrusted ? "NoScriptUntrusted" : | |
"NoScriptBlocked", | |
groups: function (site) ({ site: site, __proto__: groupProto }) | |
}; | |
splitContext(context, [ | |
["normal", "Active sites", function (item) item.groups.jsPolicy || !item.groups.untrusted], | |
["untrusted", "Untrusted sites", function (item) !item.groups.jsPolicy && item.groups.untrusted]]); | |
context.maxItems = 100; | |
} | |
services.add("noscript", "@maone.net/noscript-service;1"); | |
var PrefBase = "noscript."; | |
var Pref = Struct("text", "pref", "description"); | |
let prefs = { | |
forbid: [ | |
["bookmarklet", "forbidBookmarklets", "Forbid bookmarklets"], | |
["collapse", "collapseObject", "Collapse forbidden objects"], | |
["flash", "forbidFlash", "Block Adobe® Flash® animations"], | |
["fonts", "forbidFonts", "Forbid remote font loading"], | |
["frame", "forbidFrames", "Block foreign <frame> elements"], | |
["iframe", "forbidIFrames", "Block foreign <iframe> elements"], | |
["java", "forbidJava", "Block Java™ applets"], | |
["media", "forbidMedia", "Block <audio> and <video> elements"], | |
["placeholder", "showPlaceholder", "Replace forbidden objects with a placeholder"], | |
["plugins", "forbidPlugins", "Forbid other plugins"], | |
["refresh", "forbidMetaRefresh", "Block <meta> page directions"], | |
["silverlight", "forbidSilverlight", "Block Microsoft® Silverlight™ objects"], | |
["trusted", "contentBlocker", "Block media and plugins even on trusted sites"], | |
["webbug", "blockNSWB", "Block “web bug” tracking images"], | |
["xslt", "forbidXSLT", "Forbid XSLT stylesheets"] | |
], | |
list: [ | |
["address", "showAddress", "Show the full address (http://www.google.com)"], | |
["base", "showBaseDomain", "Show the base domain (google.com)"], | |
["full", "showDomain", "Show the full domain (www.google.com)"] | |
] | |
}; | |
for (let [k, v] of iter(prefs)) | |
prefs[k] = array(v).map(function (v) [v[0], Pref.fromArray(v.map(UTF8))]).toObject(); | |
function getPref(pref) modules.prefs.get(PrefBase + pref); | |
function setPref(pref, val) modules.prefs.set(PrefBase + pref, val); | |
prefs.complete = function prefsComplete(group) function (context) { | |
context.keys = { text: "text", description: "description" }; | |
context.completions = values(prefs[group]); | |
} | |
prefs.get = function prefsGet(group) [p.text for (p of values(this[group])) if (getPref(p.pref))]; | |
prefs.set = function prefsSet(group, val) { | |
for (let p of values(this[group])) | |
setPref(p.pref, val.indexOf(p.text) >= 0); | |
return val; | |
} | |
prefs.descs = function prefDescs(group) ["dl", {}, | |
template.map(values(this[group]), function (pref) | |
[["dt", {}, pref.text], ["dd", {}, pref.description]])]; | |
function groupParams(group) ( { | |
getter: function () prefs.get(group), | |
completer: prefs.complete(group), | |
setter: function (val) prefs.set(group, val), | |
initialValue: true, | |
persist: false | |
}); | |
group.options.add(["noscript-forbid", "nsf"], | |
"The set of permissions forbidden to untrusted sites", | |
"stringlist", "", | |
groupParams("forbid")); | |
group.options.add(["noscript-list", "nsl"], | |
"The set of domains to show in the menu and completion list", | |
"stringlist", "", | |
groupParams("list")); | |
group.options.add(["script"], | |
"Whether NoScript is enabled", | |
"boolean", false, | |
{ | |
getter: function () services.noscript.jsEnabled, | |
setter: function (val) services.noscript.jsEnabled = val, | |
initialValue: true, | |
persist: false | |
}); | |
[ | |
{ | |
names: ["noscript-sites", "nss"], | |
description: "The list of sites allowed to execute scripts", | |
action: function (add, sites) sites.length && noscriptOverlay.safeAllow(sites, add, false, -1), | |
completer: function (context) completion.noscriptSites(context), | |
has: function (val) hasOwnProperty(services.noscript.jsPolicySites.sitesMap, val) && | |
!hasOwnProperty(services.noscript.tempSites.sitesMap, val), | |
get set() new RealSet(k for (k in services.noscript.jsPolicySites.sitesMap)) | |
.difference(new RealSet(k for (k in services.noscript.tempSites.sitesMap))) | |
}, { | |
names: ["noscript-tempsites", "nst"], | |
description: "The list of sites temporarily allowed to execute scripts", | |
action: function (add, sites) sites.length && noscriptOverlay.safeAllow(sites, add, true, -1), | |
completer: function (context) completion.noscriptSites(context), | |
get set() new RealSet(k for (k in services.noscript.tempSites.sitesMap)) | |
}, { | |
names: ["noscript-untrusted", "nsu"], | |
description: "The list of untrusted sites", | |
action: function (add, sites) sites.length && services.noscript.setUntrusted(sites, add), | |
completer: function (context) completion.noscriptSites(context), | |
get set() new RealSet(k for (k in services.noscript.untrustedSites.sitesMap)) | |
}, { | |
names: ["noscript-objects", "nso"], | |
description: "The list of allowed objects", | |
get set() new RealSet(array.flatten( | |
[Array.concat(v).map(function (v) v + "@" + this, k) | |
for ([k, v] of iter(services.noscript.objectWhitelist))])), | |
action: function (add, patterns) { | |
for (let pattern of values(patterns)) { | |
let [mime, site] = util.split(pattern, /@/, 2); | |
if (add) | |
services.noscript.allowObject(site, mime); | |
else { | |
let list = services.noscript.objectWhitelist[site]; | |
if (list) { | |
if (list == "*") { | |
delete services.noscript.objectWhitelist[site]; | |
services.noscript.objectWhitelistLen--; | |
} | |
else { | |
let types = list.filter(function (type) type != mime); | |
services.noscript.objectWhitelistLen -= list.length - types.length; | |
services.noscript.objectWhitelist[site] = types; | |
if (!types.length) | |
delete services.noscript.objectWhitelist[site]; | |
} | |
} | |
} | |
} | |
if (add) | |
services.noscript.reloadAllowedObjects(config.browser.selectedBrowser); | |
}, | |
completer: function (context) completion.noscriptObjects(context) | |
} | |
].forEach(function (params) | |
group.options.add(params.names, params.description, | |
"stringlist", "", | |
{ | |
completer: function (context) { | |
context.anchored = false; | |
if (params.completer) | |
params.completer(context) | |
}, | |
domains: params.domains || function (values) values, | |
has: params.has || bind("has", params.set), | |
initialValue: true, | |
getter: params.getter || function () [k for (k of params.set)], | |
setter: function (values) { | |
let newset = new RealSet(values); | |
let current = params.set; | |
let value = this.value; | |
params.action(true, values.filter(site => !current.has(site))) | |
params.action(false, value.filter(site => !newset.has(site))); | |
return this.value; | |
}, | |
persist: false, | |
privateData: true, | |
validator: params.validator || function () true | |
})); | |
group.styles.add("noscript-menu-order", ["chrome://browser/content/browser.xul"], | |
literal(/* | |
#noscript-tbb-popup .scrollbox-innerbox { | |
-moz-box-direction: reverse; | |
} | |
*/)); | |
var INFO = | |
["plugin", { name: "noscript", | |
version: "0.9.1", | |
href: "http://dactyl.sf.net/pentadactyl/plugins#noscript-plugin", | |
summary: "NoScript integration", | |
xmlns: "dactyl" }, | |
["author", { email: "[email protected]" }, "Kris Maglione"], | |
["license", { href: "http://opensource.org/licenses/mit-license.php" }, "MIT"], | |
["project", { name: "Pentadactyl", "min-version": "1.0" }], | |
["p", {}, | |
"This plugin provides tight integration with the NoScript add-on. ", | |
"In addition to commands and options to control the behavior of ", | |
"NoScript, this plugin also provides integration with both the ", | |
config.appName, " and ", config.host, " sanitization systems sorely ", | |
"lacking in the add-on itself. Namely, when data for a domain is ", | |
"purged, all of its associated NoScript permissions are purged as ", | |
"well, and temporary permissions are purged along with session ", | |
"data."], | |
["note", {}, | |
"As most options provided by this script directly alter NoScript ", | |
"preferences, which are persistent, their values are automatically ", | |
"preserved across restarts."], | |
["item", {}, | |
["tags", {}, "'script' 'noscript'"], | |
["strut", {}], | |
["spec", {}, "'script'"], | |
["type", {}, "boolean"], | |
["default", {}, "noscript"], | |
["description", {}, | |
["p", {}, | |
"When on, all sites are allowed to execute scripts and ", | |
"load plugins. When off, only specifically allowed sites ", | |
"may do so."]]], | |
["item", {}, | |
["tags", {}, "'nsf' 'noscript-forbid'"], | |
["spec", {}, "'noscript-forbid'"], | |
["type", {}, "stringlist"], | |
["default", {}, ""], | |
["description", {}, | |
["p", {}, | |
"The set of permissions forbidden to untrusted sites."], | |
prefs.descs("forbid"), | |
["p", {}, | |
"See also ", ["o", {}, "noscript-objects"], "."]]], | |
["item", {}, | |
["tags", {}, "'nsl' 'noscript-list'"], | |
["spec", {}, "'noscript-list'"], | |
["type", {}, "stringlist"], | |
["default", {}, ""], | |
["description", {}, | |
["p", {}, | |
"The set of items to show in the main completion list and ", | |
"NoScript menu."], | |
prefs.descs("list")]], | |
["item", {}, | |
["tags", {}, "'nso' 'noscript-objects'"], | |
["spec", {}, "'noscript-objects'"], | |
["type", {}, "stringlist"], | |
["default", {}, ""], | |
["description", {}, | |
["p", {}, | |
"The list of objects which allowed to display. See also ", | |
["o", {}, "noscript-forbid"], "."], | |
["example", {}, | |
["ex", {}, ":map ", ["k", { name: "A-c", link: "false" }]], " ", | |
["ex", {}, ":set nso!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]], | |
["item", {}, | |
["tags", {}, "'nss' 'noscript-sites'"], | |
["spec", {}, "'noscript-sites'"], | |
["type", {}, "stringlist"], | |
["default", {}, ""], | |
["description", {}, | |
["p", {}, | |
"The list of sites which are permanently allowed to execute ", | |
"scripts."], | |
["example", {}, | |
["ex", {}, ":map ", ["k", { name: "A-s", link: "false" }]], " ", | |
["ex", {}, ":set nss!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]], | |
["item", {}, | |
["tags", {}, "'nst' 'noscript-tempsites'"], | |
["spec", {}, "'noscript-tempsites'"], | |
["type", {}, "stringlist"], | |
["default", {}, ""], | |
["description", {}, | |
["p", {}, | |
"The list of sites which are temporarily allowed to execute ", | |
"scripts. The value is not preserved across application ", | |
"restarts."], | |
["example", {}, | |
["ex", {}, ":map ", ["k", { name: "A-S-s", link: "false" }]], " ", | |
["ex", {}, ":set nst!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]], | |
["item", {}, | |
["tags", {}, "'nsu' 'noscript-untrusted'"], | |
["spec", {}, "'noscript-untrusted'"], | |
["type", {}, "stringlist"], | |
["default", {}, ""], | |
["description", {}, | |
["p", {}, | |
"The list of untrusted sites which are not allowed to activate, ", | |
"nor are listed in the main completion lists or NoScript menu."], | |
["example", {}, | |
["ex", {}, ":map ", ["k", { name: "A-C-s", link: "false" }]], " ", | |
["ex", {}, ":set nsu!=", ["k", { name: "A-Tab", link: "c_<A-Tab>" }]]]]]]; | |
/* vim:se sts=4 sw=4 et: */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment