Work-around Gecko 77 cached CSP issues (thanks acat for reporting https://trac.torproject.org/projects/tor/ticket/34305)
This commit is contained in:
parent
d486a96254
commit
e74d197ccb
|
@ -27,9 +27,18 @@ function ReportingCSP(reportURI, reportGroup) {
|
|||
let needsReportTo = REPORT_TO_SUPPORTED;
|
||||
|
||||
let blocker = capabilities && this.buildFromCapabilities(capabilities);
|
||||
let extras = [];
|
||||
responseHeaders.forEach((h, index) => {
|
||||
if (this.isMine(h)) {
|
||||
header = h;
|
||||
if (h.value === blocker) {
|
||||
// make this equivalent but different than the original, otherwise
|
||||
// it won't be (re)set when deleted, see
|
||||
// https://dxr.mozilla.org/mozilla-central/rev/882de07e4cbe31a0617d1ae350236123dfdbe17f/toolkit/components/extensions/webrequest/WebRequest.jsm#138
|
||||
blocker += " ";
|
||||
} else {
|
||||
extras.push(...this.unmergeExtras(h));
|
||||
}
|
||||
responseHeaders.splice(index, 1);
|
||||
} else if (needsReportTo &&
|
||||
h.name === REPORT_TO.name && h.value === REPORT_TO.value) {
|
||||
|
@ -52,6 +61,10 @@ function ReportingCSP(reportURI, reportGroup) {
|
|||
responseHeaders.push(header);
|
||||
}
|
||||
|
||||
if (extras.length) {
|
||||
responseHeaders.push(...extras);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -366,16 +366,17 @@ var RequestGuard = (() => {
|
|||
return ALLOW;
|
||||
},
|
||||
onHeadersReceived(request) {
|
||||
normalizeRequest(request);
|
||||
let result = ALLOW;
|
||||
let promises = [];
|
||||
// called for main_frame, sub_frame and object
|
||||
|
||||
// check for duplicate calls
|
||||
let headersModified = false;
|
||||
let pending = pendingRequests.get(request.requestId);
|
||||
if (pending) {
|
||||
if (pending.headersProcessed) {
|
||||
debug("[WARNING] already processed ", request);
|
||||
if (!request.fromCache) {
|
||||
debug("Headers already processed, skipping ", request);
|
||||
return ALLOW;
|
||||
}
|
||||
debug("Reprocessing headers for cached request ", request);
|
||||
} else {
|
||||
debug("onHeadersReceived", request);
|
||||
}
|
||||
|
@ -384,6 +385,28 @@ var RequestGuard = (() => {
|
|||
initPendingRequest(request);
|
||||
pending = pendingRequests.get(request.requestId);
|
||||
}
|
||||
if (request.fromCache && listeners.onHeadersReceived.resetCSP && !pending.resetCachedCSP) {
|
||||
debug("Resetting CSP Headers");
|
||||
pending.resetCachedCSP = true;
|
||||
let {responseHeaders} = request;
|
||||
let headersCount = responseHeaders.length;
|
||||
let purged = false;
|
||||
responseHeaders.forEach((h, index) => {
|
||||
if (csp.isMine(h)) {
|
||||
responseHeaders.splice(index, 1);
|
||||
}
|
||||
});
|
||||
if (headersCount > responseHeaders.length) {
|
||||
debug("Resetting cached NoScript CSP header(s)", request);
|
||||
return {responseHeaders};
|
||||
}
|
||||
}
|
||||
|
||||
normalizeRequest(request);
|
||||
let result = ALLOW;
|
||||
let promises = [];
|
||||
let headersModified = false;
|
||||
|
||||
pending.headersProcessed = true;
|
||||
let {url, documentUrl, tabId, responseHeaders, type} = request;
|
||||
let isMainFrame = type === "main_frame";
|
||||
|
@ -414,6 +437,7 @@ var RequestGuard = (() => {
|
|||
}
|
||||
if (headersModified) {
|
||||
result = {responseHeaders};
|
||||
debug("Headers changed ", request);
|
||||
}
|
||||
} catch (e) {
|
||||
error(e, "Error in onHeadersReceived", request);
|
||||
|
@ -440,7 +464,7 @@ var RequestGuard = (() => {
|
|||
if (pending) {
|
||||
pending.scriptBlocked = scriptBlocked;
|
||||
if (!(pending.headersProcessed &&
|
||||
(scriptBlocked || !ns.requestCan(request, "script"))
|
||||
(scriptBlocked || ns.requestCan(request, "script"))
|
||||
)) {
|
||||
debug("[WARNING] onHeadersReceived %s %o", frameId, tabId,
|
||||
pending.headersProcessed ? "has been overridden on": "could not process",
|
||||
|
@ -508,29 +532,36 @@ var RequestGuard = (() => {
|
|||
let filterDocs = {urls: allUrls, types: docTypes};
|
||||
let filterAll = {urls: allUrls};
|
||||
listen("onBeforeRequest", filterAll, ["blocking"]);
|
||||
|
||||
const mergingCSP = parseInt(navigator.userAgent.replace(/.*Firefox\/(\d+).*/, "$1")) >= 77;
|
||||
if (mergingCSP) {
|
||||
// In Gecko>=77 (https://bugzilla.mozilla.org/show_bug.cgi?id=1462989)
|
||||
// we need to cleanup our own cached headers in a dedicated listener :(
|
||||
// see also https://trac.torproject.org/projects/tor/ticket/34305
|
||||
wr.onHeadersReceived.addListener(
|
||||
listeners.onHeadersReceived.resetCSP = request => {
|
||||
return listeners.onHeadersReceived(request);
|
||||
}, filterDocs, ["blocking", "responseHeaders"]);
|
||||
}
|
||||
listen("onHeadersReceived", filterDocs, ["blocking", "responseHeaders"]);
|
||||
(listeners.onHeadersReceivedLast = new LastListener(wr.onHeadersReceived, request => {
|
||||
// Still, other extensions extensions may accidentally delete our CSP
|
||||
// if called before us, hence we try our best reinjecting in the end
|
||||
(listeners.onHeadersReceivedLast =
|
||||
new LastListener(wr.onHeadersReceived, request => {
|
||||
let {requestId, responseHeaders} = request;
|
||||
let pending = pendingRequests.get(request.requestId);
|
||||
if (pending && pending.headersProcessed) {
|
||||
let {cspHeader} = pending;
|
||||
if (cspHeader) {
|
||||
debug("Safety net: injecting again %o in %o", cspHeader, request);
|
||||
for (let h of responseHeaders) {
|
||||
if (h.name === cspHeader.name) {
|
||||
h.value = cspHeader.value;
|
||||
cspHeader = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cspHeader) responseHeaders.push(cspHeader);
|
||||
responseHeaders.push(cspHeader);
|
||||
return {responseHeaders};
|
||||
}
|
||||
} else {
|
||||
debug("[WARNING] onHeadersReceived not called (yet?)", request);
|
||||
}
|
||||
return null;
|
||||
return ALLOW;
|
||||
}, filterDocs, ["blocking", "responseHeaders"])).install();
|
||||
|
||||
listen("onResponseStarted", filterDocs, ["responseHeaders"]);
|
||||
listen("onCompleted", filterAll);
|
||||
listen("onErrorOccurred", filterAll);
|
||||
|
@ -548,6 +579,9 @@ var RequestGuard = (() => {
|
|||
}
|
||||
}
|
||||
wr.onBeforeRequest.removeListener(onViolationReport);
|
||||
if (listeners.onHeadersReceived.resetCSP) {
|
||||
wr.onHeadersReceived.removeListener(listeners.onHeadersReceived.resetCSP);
|
||||
}
|
||||
Messages.removeHandler(messageHandler);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,7 +8,14 @@ class NetCSP extends CSP {
|
|||
|
||||
isMine(header) {
|
||||
let {name, value} = header;
|
||||
return name.toLowerCase() === CSP.headerName && value.startsWith(this.start);
|
||||
return name.toLowerCase() === CSP.headerName &&
|
||||
value.split(/,\s*/).some(v => v.startsWith(this.start));
|
||||
}
|
||||
|
||||
unmergeExtras(header) {
|
||||
let {name, value} = header;
|
||||
return value.split(/,\s*/).filter(v => !v.startsWith(this.start))
|
||||
.map(value => {name, value});
|
||||
}
|
||||
|
||||
build(...directives) {
|
||||
|
|
Loading…
Reference in New Issue