Skip to content

Commit e74d197

Browse files
committed
Work-around Gecko 77 cached CSP issues (thanks acat for reporting https://trac.torproject.org/projects/tor/ticket/34305)
1 parent d486a96 commit e74d197

3 files changed

Lines changed: 72 additions & 18 deletions

File tree

src/bg/ReportingCSP.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,18 @@ function ReportingCSP(reportURI, reportGroup) {
2727
let needsReportTo = REPORT_TO_SUPPORTED;
2828

2929
let blocker = capabilities && this.buildFromCapabilities(capabilities);
30+
let extras = [];
3031
responseHeaders.forEach((h, index) => {
3132
if (this.isMine(h)) {
3233
header = h;
34+
if (h.value === blocker) {
35+
// make this equivalent but different than the original, otherwise
36+
// it won't be (re)set when deleted, see
37+
// https://dxr.mozilla.org/mozilla-central/rev/882de07e4cbe31a0617d1ae350236123dfdbe17f/toolkit/components/extensions/webrequest/WebRequest.jsm#138
38+
blocker += " ";
39+
} else {
40+
extras.push(...this.unmergeExtras(h));
41+
}
3342
responseHeaders.splice(index, 1);
3443
} else if (needsReportTo &&
3544
h.name === REPORT_TO.name && h.value === REPORT_TO.value) {
@@ -52,6 +61,10 @@ function ReportingCSP(reportURI, reportGroup) {
5261
responseHeaders.push(header);
5362
}
5463

64+
if (extras.length) {
65+
responseHeaders.push(...extras);
66+
}
67+
5568
return header;
5669
}
5770
}

src/bg/RequestGuard.js

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -366,16 +366,17 @@ var RequestGuard = (() => {
366366
return ALLOW;
367367
},
368368
onHeadersReceived(request) {
369-
normalizeRequest(request);
370-
let result = ALLOW;
371-
let promises = [];
372369
// called for main_frame, sub_frame and object
370+
373371
// check for duplicate calls
374-
let headersModified = false;
375372
let pending = pendingRequests.get(request.requestId);
376373
if (pending) {
377374
if (pending.headersProcessed) {
378-
debug("[WARNING] already processed ", request);
375+
if (!request.fromCache) {
376+
debug("Headers already processed, skipping ", request);
377+
return ALLOW;
378+
}
379+
debug("Reprocessing headers for cached request ", request);
379380
} else {
380381
debug("onHeadersReceived", request);
381382
}
@@ -384,6 +385,28 @@ var RequestGuard = (() => {
384385
initPendingRequest(request);
385386
pending = pendingRequests.get(request.requestId);
386387
}
388+
if (request.fromCache && listeners.onHeadersReceived.resetCSP && !pending.resetCachedCSP) {
389+
debug("Resetting CSP Headers");
390+
pending.resetCachedCSP = true;
391+
let {responseHeaders} = request;
392+
let headersCount = responseHeaders.length;
393+
let purged = false;
394+
responseHeaders.forEach((h, index) => {
395+
if (csp.isMine(h)) {
396+
responseHeaders.splice(index, 1);
397+
}
398+
});
399+
if (headersCount > responseHeaders.length) {
400+
debug("Resetting cached NoScript CSP header(s)", request);
401+
return {responseHeaders};
402+
}
403+
}
404+
405+
normalizeRequest(request);
406+
let result = ALLOW;
407+
let promises = [];
408+
let headersModified = false;
409+
387410
pending.headersProcessed = true;
388411
let {url, documentUrl, tabId, responseHeaders, type} = request;
389412
let isMainFrame = type === "main_frame";
@@ -414,6 +437,7 @@ var RequestGuard = (() => {
414437
}
415438
if (headersModified) {
416439
result = {responseHeaders};
440+
debug("Headers changed ", request);
417441
}
418442
} catch (e) {
419443
error(e, "Error in onHeadersReceived", request);
@@ -440,7 +464,7 @@ var RequestGuard = (() => {
440464
if (pending) {
441465
pending.scriptBlocked = scriptBlocked;
442466
if (!(pending.headersProcessed &&
443-
(scriptBlocked || !ns.requestCan(request, "script"))
467+
(scriptBlocked || ns.requestCan(request, "script"))
444468
)) {
445469
debug("[WARNING] onHeadersReceived %s %o", frameId, tabId,
446470
pending.headersProcessed ? "has been overridden on": "could not process",
@@ -508,29 +532,36 @@ var RequestGuard = (() => {
508532
let filterDocs = {urls: allUrls, types: docTypes};
509533
let filterAll = {urls: allUrls};
510534
listen("onBeforeRequest", filterAll, ["blocking"]);
535+
536+
const mergingCSP = parseInt(navigator.userAgent.replace(/.*Firefox\/(\d+).*/, "$1")) >= 77;
537+
if (mergingCSP) {
538+
// In Gecko>=77 (https://bugzilla.mozilla.org/show_bug.cgi?id=1462989)
539+
// we need to cleanup our own cached headers in a dedicated listener :(
540+
// see also https://trac.torproject.org/projects/tor/ticket/34305
541+
wr.onHeadersReceived.addListener(
542+
listeners.onHeadersReceived.resetCSP = request => {
543+
return listeners.onHeadersReceived(request);
544+
}, filterDocs, ["blocking", "responseHeaders"]);
545+
}
511546
listen("onHeadersReceived", filterDocs, ["blocking", "responseHeaders"]);
512-
(listeners.onHeadersReceivedLast = new LastListener(wr.onHeadersReceived, request => {
547+
// Still, other extensions extensions may accidentally delete our CSP
548+
// if called before us, hence we try our best reinjecting in the end
549+
(listeners.onHeadersReceivedLast =
550+
new LastListener(wr.onHeadersReceived, request => {
513551
let {requestId, responseHeaders} = request;
514552
let pending = pendingRequests.get(request.requestId);
515553
if (pending && pending.headersProcessed) {
516554
let {cspHeader} = pending;
517555
if (cspHeader) {
518-
debug("Safety net: injecting again %o in %o", cspHeader, request);
519-
for (let h of responseHeaders) {
520-
if (h.name === cspHeader.name) {
521-
h.value = cspHeader.value;
522-
cspHeader = null;
523-
break;
524-
}
525-
}
526-
if (cspHeader) responseHeaders.push(cspHeader);
556+
responseHeaders.push(cspHeader);
527557
return {responseHeaders};
528558
}
529559
} else {
530560
debug("[WARNING] onHeadersReceived not called (yet?)", request);
531561
}
532-
return null;
562+
return ALLOW;
533563
}, filterDocs, ["blocking", "responseHeaders"])).install();
564+
534565
listen("onResponseStarted", filterDocs, ["responseHeaders"]);
535566
listen("onCompleted", filterAll);
536567
listen("onErrorOccurred", filterAll);
@@ -548,6 +579,9 @@ var RequestGuard = (() => {
548579
}
549580
}
550581
wr.onBeforeRequest.removeListener(onViolationReport);
582+
if (listeners.onHeadersReceived.resetCSP) {
583+
wr.onHeadersReceived.removeListener(listeners.onHeadersReceived.resetCSP);
584+
}
551585
Messages.removeHandler(messageHandler);
552586
}
553587
};

src/lib/NetCSP.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@ class NetCSP extends CSP {
88

99
isMine(header) {
1010
let {name, value} = header;
11-
return name.toLowerCase() === CSP.headerName && value.startsWith(this.start);
11+
return name.toLowerCase() === CSP.headerName &&
12+
value.split(/,\s*/).some(v => v.startsWith(this.start));
13+
}
14+
15+
unmergeExtras(header) {
16+
let {name, value} = header;
17+
return value.split(/,\s*/).filter(v => !v.startsWith(this.start))
18+
.map(value => {name, value});
1219
}
1320

1421
build(...directives) {

0 commit comments

Comments
 (0)