Simplified CSP HTTP header injection, avoiding report-to until actually supported by browsers.

This commit is contained in:
hackademix 2018-10-06 17:05:14 +02:00
parent c9c7b7aefe
commit 209d50b0c1
2 changed files with 25 additions and 29 deletions

View File

@ -1,6 +1,13 @@
"use strict"; "use strict";
function ReportingCSP(reportURI, reportGroup) { function ReportingCSP(reportURI, reportGroup) {
const REPORT_TO_SUPPORTED = false;
// TODO: figure out if we're running on a browser supporting the report-to
// CSP directive, breaking report-uri, see
// 1. https://www.w3.org/TR/CSP3/#directive-report-uri
// 2. https://bugs.chromium.org/p/chromium/issues/detail?id=726634
// 3. https://bugzilla.mozilla.org/show_bug.cgi?id=1391243
const REPORT_TO = { const REPORT_TO = {
name: "Report-To", name: "Report-To",
value: JSON.stringify({ "url": reportURI, value: JSON.stringify({ "url": reportURI,
@ -9,31 +16,32 @@ function ReportingCSP(reportURI, reportGroup) {
}; };
return Object.assign( return Object.assign(
new CapsCSP(new NetCSP( new CapsCSP(new NetCSP(
`report-uri ${reportURI};`, REPORT_TO_SUPPORTED ? `;report-to ${reportGroup};`
`;report-to ${reportGroup};` : `report-uri ${reportURI};`
)), )),
{ {
reportURI, reportURI,
reportGroup, reportGroup,
patchHeaders(responseHeaders, capabilities) { patchHeaders(responseHeaders, capabilities) {
let header = null; let header = null;
let hasReportTo = false; let needsReportTo = REPORT_TO_SUPPORTED;
for (let h of responseHeaders) { for (let h of responseHeaders) {
if (this.isMine(h)) { if (this.isMine(h)) {
header = h; header = h;
h.value = this.inject(h.value, ""); h.value = "";
} else if (h.name === REPORT_TO.name && h.value === REPORT_TO.value) { } else if (needsReportTo &&
hasReportTo = true; h.name === REPORT_TO.name && h.value === REPORT_TO.value) {
needsReportTo = false;
} }
} }
let blocker = capabilities && this.buildFromCapabilities(capabilities); let blocker = capabilities && this.buildFromCapabilities(capabilities);
if (blocker) { if (blocker) {
if (!hasReportTo) { if (needsReportTo) {
responseHeaders.push(REPORT_TO); responseHeaders.push(REPORT_TO);
} }
if (header) { if (header) {
header.value = this.inject(header.value, blocker); header.value = blocker;
} else { } else {
header = this.asHeader(blocker); header = this.asHeader(blocker);
responseHeaders.push(header); responseHeaders.push(header);

View File

@ -1,30 +1,18 @@
"use strict"; "use strict";
class NetCSP extends CSP { class NetCSP extends CSP {
constructor(start, end) { constructor(start) {
super(); super();
this.start = start; this.start = start;
this.end = end;
} }
isMine(header) { isMine(header) {
let {name, value} = header; let {name, value} = header;
if (name.toLowerCase() !== CSP.headerName) return false; return name.toLowerCase() === CSP.headerName && value.startsWith(this.start);
let startIdx = value.indexOf(this.start);
return startIdx > -1 && startIdx < value.lastIndexOf(this.end);
}
inject(headerValue, mine) {
let startIdx = headerValue.indexOf(this.start);
if (startIdx < 0) return `${headerValue};${mine}`;
let endIdx = headerValue.lastIndexOf(this.end);
let retValue = `${headerValue.substring(0, startIdx)}${mine}`;
return endIdx < 0 ? retValue : `${retValue}${headerValue.substring(endIdx + this.end.length + 1)}`;
} }
build(...directives) { build(...directives) {
return `${this.start}${super.build(...directives)}${this.end}`; return `${this.start}${super.build(...directives)}`;
} }
cleanup(headers) { cleanup(headers) {