webNavigation.onCommitted + tabs.executeScript to deliver DOM policies earlier whenever possible.
This commit is contained in:
parent
6a07c055e0
commit
9954bc1ec8
|
@ -549,6 +549,40 @@ var RequestGuard = (() => {
|
|||
}
|
||||
return ABORT;
|
||||
}
|
||||
|
||||
async function onNavCommitted(details) {
|
||||
debug("onNavCommitted", details);
|
||||
let {url, tabId, frameId} = details;
|
||||
try {
|
||||
let policy = ns.computeChildPolicy({url}, {tab: {tabId}, frameId});
|
||||
policy.navigationURL = url;
|
||||
let ret = await browser.tabs.executeScript(details.tabId, {
|
||||
code:
|
||||
`{
|
||||
let domPolicy = ${JSON.stringify(policy)};
|
||||
if (this.ns) {
|
||||
ns.domPolicy = domPolicy;
|
||||
if (ns.setup) {
|
||||
if (ns.syncSetup) ns.syncSetup(domPolicy);
|
||||
else if (!ns.pendingSyncFetchPolicy) {
|
||||
ns.setup(domPolicy);
|
||||
}
|
||||
} ;
|
||||
} else {
|
||||
ns = {domPolicy}
|
||||
}
|
||||
console.debug("domPolicy", domPolicy);
|
||||
}
|
||||
ns;`,
|
||||
runAt: "document_start",
|
||||
frameId,
|
||||
});
|
||||
debug("onNavCommitted return: ", ret);
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
const RequestGuard = {
|
||||
async start() {
|
||||
Messages.addHandler(messageHandler);
|
||||
|
@ -600,9 +634,11 @@ var RequestGuard = (() => {
|
|||
wr.onBeforeRequest.addListener(onViolationReport,
|
||||
{urls: [csp.reportURI], types: ["csp_report"]}, ["blocking", "requestBody"]);
|
||||
}
|
||||
browser.webNavigation.onCommitted.addListener(onNavCommitted);
|
||||
TabStatus.probe();
|
||||
},
|
||||
stop() {
|
||||
browser.webNavigation.onCommitted.removeListener(onNavCommitted);
|
||||
let wr = browser.webRequest;
|
||||
for (let [name, listener] of Object.entries(listeners)) {
|
||||
if (typeof listener === "function") {
|
||||
|
|
|
@ -158,40 +158,7 @@
|
|||
async fetchChildPolicy({url, contextUrl}, sender) {
|
||||
await ns.initializing;
|
||||
return (messageHandler.fetchChildPolicy =
|
||||
messageHandler.fetchChildPolicySync)(...arguments);
|
||||
},
|
||||
fetchChildPolicySync({url, contextUrl}, sender) {
|
||||
let {tab, frameId} = sender;
|
||||
let policy = ns.policy;
|
||||
if (!policy) {
|
||||
console.log("Policy is null, initializing: %o, sending fallback.", ns.initializing);
|
||||
return {
|
||||
permissions: new Permissions(Permissions.DEFAULT).dry(),
|
||||
unrestricted: false,
|
||||
cascaded: false,
|
||||
fallback: true
|
||||
};
|
||||
}
|
||||
let topUrl = frameId === 0 ? contextUrl : tab && (tab.url || TabCache.get(tab.id));
|
||||
if (Sites.isInternal(url) || !ns.isEnforced(tab ? tab.id : -1)) {
|
||||
policy = null;
|
||||
}
|
||||
|
||||
let permissions, unrestricted, cascaded;
|
||||
if (policy) {
|
||||
let perms = policy.get(url, contextUrl).perms;
|
||||
cascaded = topUrl && ns.sync.cascadeRestrictions;
|
||||
if (cascaded) {
|
||||
perms = policy.cascadeRestrictions(perms, topUrl);
|
||||
}
|
||||
permissions = perms.dry();
|
||||
} else {
|
||||
// otherwise either internal URL or unrestricted
|
||||
permissions = new Permissions(Permissions.ALL).dry();
|
||||
unrestricted = true;
|
||||
cascaded = false;
|
||||
}
|
||||
return {permissions, unrestricted, cascaded};
|
||||
ns.computeChildPolicy)(...arguments);
|
||||
},
|
||||
|
||||
async openStandalonePopup() {
|
||||
|
@ -239,6 +206,40 @@
|
|||
return !this.isEnforced(request.tabId) || this.policy.can(request.url, capability, request.documentURL);
|
||||
},
|
||||
|
||||
computeChildPolicy({url, contextUrl}, sender) {
|
||||
let {tab, frameId} = sender;
|
||||
let policy = ns.policy;
|
||||
if (!policy) {
|
||||
console.log("Policy is null, initializing: %o, sending fallback.", ns.initializing);
|
||||
return {
|
||||
permissions: new Permissions(Permissions.DEFAULT).dry(),
|
||||
unrestricted: false,
|
||||
cascaded: false,
|
||||
fallback: true
|
||||
};
|
||||
}
|
||||
let topUrl = frameId === 0 ? contextUrl : tab && (tab.url || TabCache.get(tab.id));
|
||||
if (Sites.isInternal(url) || !ns.isEnforced(tab ? tab.id : -1)) {
|
||||
policy = null;
|
||||
}
|
||||
|
||||
let permissions, unrestricted, cascaded;
|
||||
if (policy) {
|
||||
let perms = policy.get(url, contextUrl).perms;
|
||||
cascaded = topUrl && ns.sync.cascadeRestrictions;
|
||||
if (cascaded) {
|
||||
perms = policy.cascadeRestrictions(perms, topUrl);
|
||||
}
|
||||
permissions = perms.dry();
|
||||
} else {
|
||||
// otherwise either internal URL or unrestricted
|
||||
permissions = new Permissions(Permissions.ALL).dry();
|
||||
unrestricted = true;
|
||||
cascaded = false;
|
||||
}
|
||||
return {permissions, unrestricted, cascaded};
|
||||
},
|
||||
|
||||
start() {
|
||||
if (this.running) return;
|
||||
this.running = true;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
},
|
||||
|
||||
fetchPolicy() {
|
||||
if (this.policy) return;
|
||||
let url = document.URL;
|
||||
|
||||
debug(`Fetching policy from document %s, readyState %s`,
|
||||
|
@ -41,6 +42,7 @@
|
|||
//, document.domain, document.baseURI, window.isSecureContext // DEV_ONLY
|
||||
);
|
||||
|
||||
|
||||
if (/^(ftp|file):/.test(url)) { // ftp: or file: - no CSP headers yet
|
||||
if (this.syncFetchPolicy) {
|
||||
this.syncFetchPolicy();
|
||||
|
@ -50,6 +52,15 @@
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
if (this.domPolicy) {
|
||||
debug("File policy set in webNavigation found!");
|
||||
try {
|
||||
this.setup(this.domPolicy);
|
||||
return;
|
||||
} catch(e) {
|
||||
error(e);
|
||||
}
|
||||
}
|
||||
// CSP headers have been already provided by webRequest, we are not in a hurry...
|
||||
if (/^(javascript|about):/.test(url)) {
|
||||
url = document.readyState === "loading"
|
||||
|
@ -77,7 +88,8 @@
|
|||
},
|
||||
|
||||
setup(policy) {
|
||||
debug("%s, %s, %o", document.URL, document.readyState, policy);
|
||||
if (this.policy) return false;
|
||||
debug("%s, %s, fetched %o", document.URL, document.readyState, policy);
|
||||
if (!policy) {
|
||||
policy = {permissions: {capabilities: []}, localFallback: true};
|
||||
}
|
||||
|
@ -97,6 +109,7 @@
|
|||
}
|
||||
this.canScript = this.allows("script");
|
||||
this.fire("capabilities");
|
||||
return true;
|
||||
},
|
||||
|
||||
policy: null,
|
||||
|
@ -105,6 +118,6 @@
|
|||
return this.capabilities && this.capabilities.has(cap);
|
||||
},
|
||||
};
|
||||
|
||||
this.ns = this.ns ? Object.assign(this.ns, ns) : ns;
|
||||
this.ns = this.ns ? Object.assign(ns, this.ns) : ns;
|
||||
debug("StaticNS", JSON.stringify(this.ns)); // DEV_ONLY
|
||||
}
|
||||
|
|
|
@ -6,19 +6,43 @@
|
|||
|
||||
// Here we've got no CSP header yet (file: or ftp: URL), we need one
|
||||
// injected in the DOM as soon as possible.
|
||||
debug("No CSP yet for non-HTTP document load: fetching policy synchronously...");
|
||||
debug("No CSP yet for non-HTTP document load: fetching policy synchronously...", ns);
|
||||
|
||||
ns.syncSetup = ns.setup.bind(ns);
|
||||
|
||||
if (top !== window) {
|
||||
if (top.wrappedJSObject._noScriptPolicy) {
|
||||
try {
|
||||
ns.setup(JSON.parse(top.wrappedJSObject._noScriptPolicy));
|
||||
} catch(e) {
|
||||
error(e);
|
||||
if (window.wrappedJSObject) {
|
||||
if (top === window) {
|
||||
ns.syncSetup = policy => {
|
||||
if (!ns.setup(policy)) return;
|
||||
if (top === window && window.wrappedJSObject) {
|
||||
let persistentPolicy = JSON.stringify(policy);
|
||||
Object.freeze(persistentPolicy);
|
||||
try {
|
||||
Object.defineProperty(window.wrappedJSObject, "_noScriptPolicy", {value: cloneInto(persistentPolicy, window)});
|
||||
} catch(e) {
|
||||
error(e);
|
||||
}
|
||||
}
|
||||
ns.syncSetup = () => {};
|
||||
};
|
||||
} else try {
|
||||
if (top.wrappedJSObject._noScriptPolicy) {
|
||||
debug("Policy set in parent frame found!")
|
||||
try {
|
||||
ns.setup(JSON.parse(top.wrappedJSObject._noScriptPolicy));
|
||||
return;
|
||||
} catch(e) {
|
||||
error(e);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (e) {
|
||||
// cross-origin accesss violation, ignore
|
||||
}
|
||||
}
|
||||
if (ns.domPolicy) {
|
||||
ns.syncSetup(ns.domPolicy);
|
||||
return;
|
||||
}
|
||||
let syncFetch = callback => {
|
||||
browser.runtime.sendSyncMessage(
|
||||
{id: "fetchPolicy", url, contextUrl: url},
|
||||
|
@ -175,23 +199,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
let setup = policy => {
|
||||
debug("Fetched %o, readyState %s", policy, document.readyState); // DEV_ONLY
|
||||
if (top === window) {
|
||||
let persistentPolicy = JSON.stringify(policy);
|
||||
Object.freeze(persistentPolicy);
|
||||
try {
|
||||
Object.defineProperty(window.wrappedJSObject, "_noScriptPolicy", {value: cloneInto(persistentPolicy, window)});
|
||||
} catch(e) {
|
||||
error(e);
|
||||
}
|
||||
}
|
||||
ns.setup(policy);
|
||||
}
|
||||
|
||||
for (let attempts = 3; attempts-- > 0;) {
|
||||
try {
|
||||
syncFetch(setup);
|
||||
if (ns.policy) break;
|
||||
syncFetch(ns.syncSetup);
|
||||
break;
|
||||
} catch (e) {
|
||||
if (!Messages.isMissingEndpoint(e) || document.readyState === "complete") {
|
||||
|
|
Loading…
Reference in New Issue