Force CSP inheritance for redirections to data: URIs on Gecko pre-69.

This commit is contained in:
hackademix 2020-03-13 22:31:08 +01:00
parent c44ab6f8aa
commit d8332adc4e
3 changed files with 41 additions and 8 deletions

View File

@ -25,6 +25,8 @@ function ReportingCSP(reportURI, reportGroup) {
patchHeaders(responseHeaders, capabilities) { patchHeaders(responseHeaders, capabilities) {
let header = null; let header = null;
let needsReportTo = REPORT_TO_SUPPORTED; let needsReportTo = REPORT_TO_SUPPORTED;
let blocker = capabilities && this.buildFromCapabilities(capabilities);
for (let h of responseHeaders) { for (let h of responseHeaders) {
if (this.isMine(h)) { if (this.isMine(h)) {
header = h; header = h;
@ -32,10 +34,16 @@ function ReportingCSP(reportURI, reportGroup) {
} else if (needsReportTo && } else if (needsReportTo &&
h.name === REPORT_TO.name && h.value === REPORT_TO.value) { h.name === REPORT_TO.name && h.value === REPORT_TO.value) {
needsReportTo = false; needsReportTo = false;
} else if (blocker && /^(Location|Refresh)$/i.test(h.name)) {
let url = /^R/i.test(h.name)
? h.value.replace(/^[^,;]*[,;]url[^\w=]*=\s*/i, "") : h.value;
let patched = CSP.patchDataURI(url, blocker);
if (patched !== url) {
h.value = h.value.slice(0, -url.length) + patched;
}
} }
} }
let blocker = capabilities && this.buildFromCapabilities(capabilities);
if (blocker) { if (blocker) {
if (needsReportTo) { if (needsReportTo) {
responseHeaders.push(REPORT_TO); responseHeaders.push(REPORT_TO);

View File

@ -305,9 +305,10 @@ var RequestGuard = (() => {
normalizeRequest(request); normalizeRequest(request);
try { try {
let redirected = initPendingRequest(request); let redirected = initPendingRequest(request);
let {policy} = ns; let {policy} = ns
let policyType = policyTypesMap[request.type]; let {type} = request;
if (policyType) { if (type in policyTypesMap) {
let policyType = policyTypesMap[type];
let {url, originUrl, documentUrl, tabId} = request; let {url, originUrl, documentUrl, tabId} = request;
let isFetch = "fetch" === policyType; let isFetch = "fetch" === policyType;
@ -327,7 +328,7 @@ var RequestGuard = (() => {
if (/^(?:data|blob):/.test(url)) { if (/^(?:data|blob):/.test(url)) {
request._dataUrl = url; request._dataUrl = url;
request.url = url = documentUrl; request.url = url = documentUrl || originUrl;
} }
let allowed = Sites.isInternal(url); let allowed = Sites.isInternal(url);
@ -340,10 +341,19 @@ var RequestGuard = (() => {
allowed = !ns.isEnforced(tabId); allowed = !ns.isEnforced(tabId);
} }
if (!allowed) { if (!allowed) {
allowed = intersectCapabilities( let capabilities = intersectCapabilities(
policy.get(url, documentUrl).perms, policy.get(url, documentUrl).perms,
request request);
).has(policyType); allowed = !policyType || capabilities.has(policyType);
if (allowed && request._dataUrl && type.endsWith("frame")) {
let blocker = csp.buildFromCapabilities(capabilities);
if (blocker) {
let redirectUrl = CSP.patchDataURI(request._dataUrl, blocker);
if (redirectUrl !== request._dataUrl) {
return {redirectUrl};
}
}
}
} }
} }
Content.reportTo(request, allowed, policyType); Content.reportTo(request, allowed, policyType);

View File

@ -21,3 +21,18 @@ class CSP {
CSP.isEmbedType = type => /\b(?:application|video|audio)\b/.test(type) && type !== "application/xhtml+xml"; CSP.isEmbedType = type => /\b(?:application|video|audio)\b/.test(type) && type !== "application/xhtml+xml";
CSP.headerName = "content-security-policy"; CSP.headerName = "content-security-policy";
CSP.patchDataURI = (uri, blocker) => {
let parts = /^data:(?:[^,;]*ml)(;[^,]*)?,/i.exec(uri);
if (!(blocker && parts)) {
// not an interesting data: URI, return as it is
return uri;
}
if (parts[1]) {
// extra encoding info, let's bailout (better safe than sorry)
return "data:";
}
// It's a HTML/XML page, let's prepend our CSP blocker to the document
let patch = parts[0] + encodeURIComponent(
`<meta http-equiv="${CSP.headerName}" content="${blocker}">`);
return uri.startsWith(patch) ? uri : patch + uri.substring(parts[0].length);
}