Removed all references to RequestUtil.js and dependancies.
This commit is contained in:
parent
e959accb70
commit
2f9c5299af
|
@ -58,8 +58,9 @@
|
|||
var ChildPolicies = {
|
||||
async storeTabInfo(tabId, info) {
|
||||
try {
|
||||
let preamble = info ? `${marker} + ${JSON.stringify(JSON.stringify([info]))} + ${marker} + "," + ` : "";
|
||||
await browser.tabs.executeScript(tabId, {
|
||||
code: `window.name = ${marker} + ${JSON.stringify(JSON.stringify([info]))} + ${marker} + "," + window.name;`,
|
||||
code: `window.name = ${preamble}window.name.split(${marker} + ",").pop();`,
|
||||
allFrames: false,
|
||||
matchAboutBlank: true,
|
||||
runAt: "document_start",
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
var ContentScriptOnce = (() => {
|
||||
"use strict";
|
||||
|
||||
let requestMap = new Map();
|
||||
|
||||
{
|
||||
let cleanup = r => {
|
||||
let {requestId} = r;
|
||||
let scripts = requestMap.get(requestId);
|
||||
if (scripts) {
|
||||
window.setTimeout(() => {
|
||||
requestMap.delete(requestId);
|
||||
for (let s of scripts) s.unregister();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
let filter = {
|
||||
urls: ["<all_urls>"],
|
||||
types: ["main_frame", "sub_frame", "object"]
|
||||
};
|
||||
let wr = browser.webRequest;
|
||||
for (let event of ["onCompleted", "onErrorOccurred"]) {
|
||||
wr[event].addListener(cleanup, filter);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
async execute(request, options) {
|
||||
let {requestId, url} = request;
|
||||
let scripts = requestMap.get(requestId);
|
||||
if (!scripts) requestMap.set(requestId, scripts = new Set());
|
||||
try {
|
||||
let urlObj = new URL(url);
|
||||
if (urlObj.port) {
|
||||
urlObj.port = "";
|
||||
url = urlObj.toString();
|
||||
}
|
||||
} catch (e) {}
|
||||
let defOpts = {
|
||||
runAt: "document_start",
|
||||
matchAboutBlank: true,
|
||||
matches: [url],
|
||||
allFrames: true,
|
||||
};
|
||||
|
||||
scripts.add(await browser.contentScripts.register(
|
||||
Object.assign(defOpts, options)
|
||||
));
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -1,169 +0,0 @@
|
|||
'use strict';
|
||||
{
|
||||
let xmlFeedOrImage = /^(?:(?:application|text)\/(?:(?:r(?:ss|df)|atom)\+)xml(;|$))|image\//i;
|
||||
let rawXml = /^(?:application|text)\/xml;/i;
|
||||
let brokenXMLOnLoad;
|
||||
(async () => brokenXMLOnLoad = parseInt((await browser.runtime.getBrowserInfo()).version) < 61)()
|
||||
|
||||
let pendingScripts = new Map();
|
||||
let NOP = () => {};
|
||||
|
||||
let reloadingTabs = new Map();
|
||||
let tabKey = (tabId, url) => `${tabId}:${url}`;
|
||||
|
||||
let cleanup = r => {
|
||||
pendingScripts.delete(r.requestId);
|
||||
let key = tabKey(r.tabId, r.url);
|
||||
if (reloadingTabs.get(key) === false) {
|
||||
reloadingTabs.delete(key);
|
||||
}
|
||||
};
|
||||
|
||||
let executeAll = async request => {
|
||||
let {url, tabId, frameId, requestId, type} = request;
|
||||
let scripts = pendingScripts.get(requestId);
|
||||
if (!scripts) return -1;
|
||||
pendingScripts.delete(requestId);
|
||||
let count = 0;
|
||||
let run = async details => {
|
||||
details = Object.assign({
|
||||
runAt: "document_start",
|
||||
matchAboutBlank: true,
|
||||
frameId
|
||||
}, details);
|
||||
try {
|
||||
let res;
|
||||
for (let attempts = 10; attempts-- > 0;) {
|
||||
try {
|
||||
res = await browser.tabs.executeScript(tabId, details);
|
||||
break;
|
||||
} catch(e) {
|
||||
if (!/No matching message handler/.test(e.message)) throw e;
|
||||
debug("Couldn't inject script into %s: too early? Retrying up to %s times...", url, attempts);
|
||||
}
|
||||
}
|
||||
count++;
|
||||
debug("Execute on start OK, result=%o", res, url, details);
|
||||
} catch (e) {
|
||||
error(e, "Execute on start failed", url, details);
|
||||
}
|
||||
};
|
||||
await Promise.all([...scripts.values()].map(run));
|
||||
return count;
|
||||
};
|
||||
|
||||
{
|
||||
let filter = {
|
||||
urls: ["<all_urls>"],
|
||||
types: ["main_frame", "sub_frame"]
|
||||
};
|
||||
let wr = browser.webRequest;
|
||||
for (let event of ["onCompleted", "onErrorOccurred"]) {
|
||||
wr[event].addListener(cleanup, filter);
|
||||
}
|
||||
|
||||
wr.onResponseStarted.addListener(r => {
|
||||
let scripts = pendingScripts.get(r.requestId);
|
||||
if (scripts) scripts.runAndFlush();
|
||||
}, filter);
|
||||
}
|
||||
|
||||
var RequestUtil = {
|
||||
|
||||
getResponseMetaData(request) {
|
||||
return request.response || (request.response = new ResponseMetaData(request));
|
||||
},
|
||||
|
||||
executeOnStart(request, details) {
|
||||
let {requestId, url, tabId, frameId, statusCode, type} = request;
|
||||
|
||||
if (statusCode >= 300 && statusCode < 400 || type === "object") return;
|
||||
if (frameId === 0) {
|
||||
let key = tabKey(tabId, url);
|
||||
debug("Checking whether %s is a reloading tab...", key);
|
||||
if (reloadingTabs.get(key)) {
|
||||
reloadingTabs.set(key, false); // doom it for removal in cleanup
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let response = this.getResponseMetaData(request);
|
||||
let {contentType, contentDisposition} = response;
|
||||
if (contentDisposition ||
|
||||
xmlFeedOrImage.test(contentType) && !/\/svg\b/i.test(contentType)) {
|
||||
debug("Skipping execute on start of %s %o.", url, response);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Injecting script on start in %s (%o).", url, response);
|
||||
|
||||
let scripts = pendingScripts.get(requestId);
|
||||
let scriptKey = JSON.stringify(details);
|
||||
if (!scripts) {
|
||||
pendingScripts.set(requestId, scripts = new Map());
|
||||
scripts.set(scriptKey, details);
|
||||
} else {
|
||||
scripts.set(scriptKey, details);
|
||||
return;
|
||||
}
|
||||
|
||||
if (/^(?:application|text)\//.test(contentType)
|
||||
&& !/[^;]+\b(html|xml)\b/i.test(contentType)) {
|
||||
debug("Not HTML: defer script to onResponseStarted for %s (%o)", url, response);
|
||||
return;
|
||||
}
|
||||
|
||||
let mustCheckFeed = brokenXMLOnLoad && frameId === 0 && rawXml.test(contentType);
|
||||
debug("mustCheckFeed = %s, brokenXMLOnLoad = %s", mustCheckFeed, brokenXMLOnLoad);
|
||||
let filter = browser.webRequest.filterResponseData(requestId);
|
||||
let buffer = [];
|
||||
let responseCompleted = false;
|
||||
let mustReload = false;
|
||||
scripts.runAndFlush = async () => {
|
||||
scripts.runAndFlush = NOP;
|
||||
if (responseCompleted && buffer && !buffer.length) {
|
||||
filter.disconnect();
|
||||
}
|
||||
let scriptsRan = await executeAll(request);
|
||||
if (mustCheckFeed && !scriptsRan) {
|
||||
mustReload = true;
|
||||
debug(`Marking as "must reload"`, tabId, url);
|
||||
reloadingTabs.set(tabKey(tabId, url), true);
|
||||
}
|
||||
if (buffer) {
|
||||
debug("Flushing %s buffer chunks on %s", buffer.length, url);
|
||||
for (let chunk of buffer) {
|
||||
filter.write(chunk);
|
||||
}
|
||||
buffer = null;
|
||||
}
|
||||
filter.disconnect();
|
||||
if (responseCompleted) {
|
||||
filter.onstop(null);
|
||||
}
|
||||
};
|
||||
|
||||
filter.ondata = event => {
|
||||
scripts.runAndFlush();
|
||||
if (buffer) {
|
||||
debug("buffering", url);
|
||||
buffer.push(event.data);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("ondata", url);
|
||||
filter.write(event.data);
|
||||
filter.disconnect();
|
||||
};
|
||||
|
||||
filter.onstop = async event => {
|
||||
responseCompleted = true;
|
||||
await scripts.runAndFlush();
|
||||
if (mustReload && !buffer) {
|
||||
mustReload = false;
|
||||
browser.tabs.update(tabId, {url});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -90,7 +90,7 @@ var Settings = {
|
|||
|
||||
if (typeof unrestrictedTab === "boolean") {
|
||||
ns.unrestrictedTabs[unrestrictedTab ? "add" : "delete"](tabId);
|
||||
ChildPolicies.storeTabInfo(tabId, {unrestricted: unrestrictedTab});
|
||||
ChildPolicies.storeTabInfo(tabId, unrestrictedTab && {unrestricted: true});
|
||||
}
|
||||
if (reloadAffected) {
|
||||
browser.tabs.reload(tabId);
|
||||
|
|
|
@ -34,16 +34,20 @@ function deferWebTraffic(promiseToWaitFor, next) {
|
|||
if (frameId !== 0) {
|
||||
documentUrl = request.frameAncestors.pop().url;
|
||||
}
|
||||
reloadTab(tabId);
|
||||
if (tabId !== -1) {
|
||||
reloadTab(tabId);
|
||||
} else {
|
||||
debug("No tab to reload for %s %s from %s", type, url, documentUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
debug("Deferring ", url, type);
|
||||
debug("Deferring %s %s from %s", type, url, documentUrl);
|
||||
try {
|
||||
await promiseToWaitFor;
|
||||
} catch (e) {
|
||||
error(e);
|
||||
}
|
||||
debug("Green light to ", url, type);
|
||||
debug("Green light to %s %s from %s", type, url, documentUrl);
|
||||
}
|
||||
|
||||
function spyTabs(request) {
|
||||
|
|
|
@ -36,7 +36,7 @@
|
|||
|
||||
await include("/bg/defaults.js");
|
||||
await ns.defaults;
|
||||
await include(["/bg/RequestGuard.js", "/bg/RequestUtil.js"]);
|
||||
await include("/bg/RequestGuard.js");
|
||||
await RequestGuard.start();
|
||||
await XSS.start(); // we must start it anyway to initialize sub-objects
|
||||
if (!ns.sync.xss) {
|
||||
|
|
|
@ -3,23 +3,30 @@
|
|||
// debug = () => {}; // REL_ONLY
|
||||
{
|
||||
let listenersMap = new Map();
|
||||
let backlog = new Set();
|
||||
var ns = {
|
||||
on(eventName, listener) {
|
||||
let listeners = listenersMap.get(eventName);
|
||||
if (!listeners) listenersMap.set(eventName, listeners = new Set());
|
||||
listeners.add(listener);
|
||||
if (backlog.has(eventName)) this.fire(eventName, listener);
|
||||
},
|
||||
detach(eventName, listener) {
|
||||
let listeners = listenersMap.get(eventName);
|
||||
if (listeners) listeners.delete(listener);
|
||||
},
|
||||
fire(eventName) {
|
||||
fire(eventName, listener = null) {
|
||||
if (listener) {
|
||||
listener({type:eventName, source: this});
|
||||
return;
|
||||
}
|
||||
let listeners = listenersMap.get(eventName);
|
||||
if (listeners) {
|
||||
for (let l of listeners) {
|
||||
l(this);
|
||||
this.fire(eventName, l);
|
||||
}
|
||||
}
|
||||
backlog.add(eventName);
|
||||
},
|
||||
setup(DEFAULT, MARKER) {
|
||||
this.perms.DEFAULT = DEFAULT;
|
||||
|
@ -36,10 +43,10 @@
|
|||
let tabInfoRx = new RegExp(`^${MARKER}\\[([^]*?)\\]${MARKER},`);
|
||||
if (top === window) { // wrap to hide
|
||||
Reflect.defineProperty(top.wrappedJSObject, "name", {
|
||||
get: exportFunction(() => _name.replace(eraseTabInfoRx, ""), top.wrappedJSObject),
|
||||
get: exportFunction(() => top.name.replace(eraseTabInfoRx, ""), top.wrappedJSObject),
|
||||
set: exportFunction(value => {
|
||||
let preamble = _name.match(tabInfoRx);
|
||||
_name = `${preamble && preamble[0] || ""}${value}`;
|
||||
let preamble = top.name.match(tabInfoRx);
|
||||
top.name = `${preamble && preamble[0] || ""}${value}`;
|
||||
return value;
|
||||
}, top.wrappedJSObject)
|
||||
});
|
||||
|
@ -57,14 +64,14 @@
|
|||
}
|
||||
ns.fire("perms");
|
||||
},
|
||||
storeTabInfo(info) {
|
||||
let {MARKER} = this.perms;
|
||||
window.name = `${MARKER}${JSON.stringify([info])}${MARKER},${window.name.split(marker).pop()}`;
|
||||
},
|
||||
perms: { DEFAULT: null, CURRENT: null, tabInfo: {} },
|
||||
perms: { DEFAULT: null, CURRENT: null, tabInfo: {}, MARKER: "" },
|
||||
allows(cap) {
|
||||
let perms = this.perms.CURRENT;
|
||||
return perms && perms.capabilities.includes(cap);
|
||||
},
|
||||
getWindowName() {
|
||||
return top !== window || !this.perms.MARKER ? window.name
|
||||
: window.name.split(this.perms.MARKER + ",").pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ns.on("perms", ns => {
|
||||
ns.on("perms", event => {
|
||||
debug("Media Hook", document.URL, document.documentElement && document.documentElement.innerHTML, ns.perms.CURRENT); // DEV_ONLY
|
||||
let mediaBlocker = !ns.allows("media");
|
||||
let unpatched = new Map();
|
||||
|
@ -54,6 +54,4 @@ ns.on("perms", ns => {
|
|||
|
||||
return unpatched.get(window.MediaSource.prototype).addSourceBuffer.call(ms, mime, ...args);
|
||||
});
|
||||
|
||||
})();
|
||||
document.URL;
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ns.on("perms", ns => {
|
||||
ns.on("perms", event => {
|
||||
debug("WebGL Hook", document.URL, document.documentElement && document.documentElement.innerHTML, ns.perms.CURRENT); // DEV_ONLY
|
||||
if (ns.allows("webgl")) return;
|
||||
let proto = HTMLCanvasElement.prototype;
|
||||
|
@ -26,4 +26,3 @@ ns.on("perms", ns => {
|
|||
return getContext.call(this, type, ...rest);
|
||||
}, proto, {defineAs: "getContext"});
|
||||
});
|
||||
document.URL;
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
class ResponseMetaData {
|
||||
constructor(request) {
|
||||
let {responseHeaders} = request;
|
||||
this.headers = {};
|
||||
this.contentType = this.contentDisposition = null;
|
||||
for (let h of responseHeaders) {
|
||||
if (/^\s*Content-(Type|Disposition)\s*$/i.test(h.name)) {
|
||||
let propertyName = RegExp.$1;
|
||||
propertyName = `content${propertyName.charAt(0).toUpperCase()}${propertyName.substring(1).toLowerCase()}`;
|
||||
this[propertyName] = h.value;
|
||||
this.headers[propertyName] = h;
|
||||
}
|
||||
}
|
||||
this.forcedUTF8 = false;
|
||||
}
|
||||
|
||||
get charset() {
|
||||
let charset = "";
|
||||
if (this.contentType) {
|
||||
let m = this.contentType.match(/;\s*charset\s*=\s*(\S+)/);
|
||||
if (m) {
|
||||
charset = m[1];
|
||||
}
|
||||
}
|
||||
Object.defineProperty(this, "charset", { value: charset, writable: false, configurable: true });
|
||||
return charset;
|
||||
}
|
||||
|
||||
get isUTF8() {
|
||||
return /^utf-?8$/i.test(this.charset);
|
||||
}
|
||||
|
||||
forceUTF8() {
|
||||
if (!(this.forcedUTF8 || this.isUTF8)) {
|
||||
let h = this.headers.contentType;
|
||||
if (h) {
|
||||
h.value = h.value.replace(/;\s*charset\s*=.*|$/, "; charset=utf8");
|
||||
this.forcedUTF8 = true;
|
||||
} // if the header doesn't exist the browser should default to UTF-8 anyway
|
||||
}
|
||||
return this.forcedUTF8;
|
||||
}
|
||||
|
||||
createDecoder() {
|
||||
if (this.charset) {
|
||||
try {
|
||||
return new TextDecoder(this.charset);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
return new TextDecoder("utf-8");
|
||||
}
|
||||
};
|
|
@ -39,7 +39,6 @@
|
|||
"lib/include.js",
|
||||
"lib/punycode.js",
|
||||
"lib/tld.js",
|
||||
"lib/ResponseMetaData.js",
|
||||
"lib/LastListener.js",
|
||||
"common/Policy.js",
|
||||
"common/locale.js",
|
||||
|
|
|
@ -741,7 +741,7 @@ XSS.InjectionChecker = (async () => {
|
|||
checkJS: function(s, unescapedUni) {
|
||||
this.log(s);
|
||||
|
||||
if (/\?name\b[\s\S]*:|[^&?]\bname\b/.test(s)) {
|
||||
if (/[=\(](?:[\s\S]*(?:\?name\b[\s\S]*:|[^&?]\bname\b)|name\b)/.test(s)) {
|
||||
this.nameAssignment = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,8 +61,9 @@ var XSS = (() => {
|
|||
let block = !!(reasons.urlInjection || reasons.postInjection)
|
||||
|
||||
if (reasons.protectName) {
|
||||
RequestUtil.executeOnStart(request, {
|
||||
file: "/xss/sanitizeName.js",
|
||||
await include("bg/COntentScriptOnce.js");
|
||||
await ContentScriptOnce.execute(request, {
|
||||
js: [{file: "/xss/sanitizeName.js"}],
|
||||
});
|
||||
if (!block) return ALLOW;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
if (/[<"'\`(=:]/.test(window.name)) {
|
||||
console.log(`NoScript XSS filter sanitizing suspicious window.name "%s" on %s`, window.name, document.URL);
|
||||
window.name = "";
|
||||
}
|
||||
ns.on("perms", event => {
|
||||
if (ns.allows("script")) {
|
||||
let name = ns.getWindowName();
|
||||
if (/[<"'\`(=:]/.test(name)) {
|
||||
console.log(`NoScript XSS filter sanitizing suspicious window.name "%s" on %s`, name, document.URL);
|
||||
window.name = window.name.substring(0, window.name.length - name.length);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue