mirror of
https://github.com/gorhill/uMatrix.git
synced 2024-06-14 16:25:03 +12:00
reliably report web worker and inline script presence
This commit is contained in:
parent
b870757e94
commit
821e45751a
|
@ -110,7 +110,9 @@ return {
|
|||
},
|
||||
|
||||
clearBrowserCacheCycle: 0,
|
||||
cspNoWorkerSrc: undefined,
|
||||
cspNoInlineScript: undefined,
|
||||
cspNoWorker: undefined,
|
||||
cspReportURI: 'about:blank',
|
||||
updateAssetsEvery: 11 * oneDay + 1 * oneHour + 1 * oneMinute + 1 * oneSecond,
|
||||
firstUpdateAfter: 11 * oneMinute,
|
||||
nextUpdateAfter: 11 * oneHour,
|
||||
|
|
|
@ -30,16 +30,68 @@
|
|||
|
||||
if ( typeof vAPI !== 'object' ) { return; }
|
||||
|
||||
window.addEventListener('securitypolicyviolation', function(ev) {
|
||||
vAPI.reportedViolations = vAPI.reportedViolations || new Set();
|
||||
|
||||
var cspReportURI = 'about:blank';
|
||||
var reportedViolations = vAPI.reportedViolations;
|
||||
|
||||
var handler = function(ev) {
|
||||
if (
|
||||
ev.isTrusted !== true ||
|
||||
ev.originalPolicy.includes(cspReportURI) === false
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Firefox and Chromium differs in how they fill the
|
||||
// 'effectiveDirective' property. Need to normalize here.
|
||||
var directive = ev.effectiveDirective;
|
||||
if ( directive.startsWith('script-src') ) {
|
||||
directive = 'script-src';
|
||||
} else if ( directive.startsWith('worker-src') ) {
|
||||
directive = 'worker-src';
|
||||
} else if ( directive.startsWith('child-src') ) {
|
||||
directive = 'worker-src';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
var blockedURL;
|
||||
try {
|
||||
blockedURL = new URL(ev.blockedURI);
|
||||
} catch(ex) {
|
||||
}
|
||||
blockedURL = blockedURL !== undefined ? blockedURL.href || '' : '';
|
||||
|
||||
// Avoid reporting same violations repeatedly.
|
||||
var violationKey = (directive + ' ' + blockedURL).trim();
|
||||
if ( reportedViolations.has(violationKey) ) {
|
||||
return true;
|
||||
}
|
||||
reportedViolations.add(violationKey);
|
||||
|
||||
vAPI.messaging.send(
|
||||
'contentscript.js',
|
||||
{
|
||||
what: 'securityPolicyViolation',
|
||||
policy: ev.originalPolicy,
|
||||
blockedURI: ev.blockedURI,
|
||||
documentURI: ev.documentURI
|
||||
directive: directive,
|
||||
blockedURI: blockedURL,
|
||||
documentURI: ev.documentURI,
|
||||
blocked: ev.disposition === 'enforce'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
document.addEventListener(
|
||||
'securitypolicyviolation',
|
||||
function(ev) {
|
||||
if ( !handler(ev) ) { return; }
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
})();
|
||||
|
|
|
@ -407,14 +407,21 @@ var collapser = (function() {
|
|||
// Mind "on..." attributes.
|
||||
|
||||
(function() {
|
||||
vAPI.messaging.send('contentscript.js', {
|
||||
what: 'contentScriptSummary',
|
||||
locationURL: window.location.href,
|
||||
inlineScript:
|
||||
document.querySelector('script:not([src])') !== null ||
|
||||
document.querySelector('a[href^="javascript:"]') !== null ||
|
||||
document.querySelector('[onabort],[onblur],[oncancel],[oncanplay],[oncanplaythrough],[onchange],[onclick],[onclose],[oncontextmenu],[oncuechange],[ondblclick],[ondrag],[ondragend],[ondragenter],[ondragexit],[ondragleave],[ondragover],[ondragstart],[ondrop],[ondurationchange],[onemptied],[onended],[onerror],[onfocus],[oninput],[oninvalid],[onkeydown],[onkeypress],[onkeyup],[onload],[onloadeddata],[onloadedmetadata],[onloadstart],[onmousedown],[onmouseenter],[onmouseleave],[onmousemove],[onmouseout],[onmouseover],[onmouseup],[onwheel],[onpause],[onplay],[onplaying],[onprogress],[onratechange],[onreset],[onresize],[onscroll],[onseeked],[onseeking],[onselect],[onshow],[onstalled],[onsubmit],[onsuspend],[ontimeupdate],[ontoggle],[onvolumechange],[onwaiting],[onafterprint],[onbeforeprint],[onbeforeunload],[onhashchange],[onlanguagechange],[onmessage],[onoffline],[ononline],[onpagehide],[onpageshow],[onrejectionhandled],[onpopstate],[onstorage],[onunhandledrejection],[onunload],[oncopy],[oncut],[onpaste]') !== null
|
||||
});
|
||||
if (
|
||||
vAPI.reportedViolations === undefined ||
|
||||
vAPI.reportedViolations.has('script-src') === false
|
||||
) {
|
||||
if ( document.querySelector('script:not([src])') !== null ) {
|
||||
vAPI.messaging.send('contentscript.js', {
|
||||
what: 'securityPolicyViolation',
|
||||
directive: 'script-src',
|
||||
documentURI: window.location.href
|
||||
});
|
||||
if ( vAPI.reportedViolations ) {
|
||||
vAPI.reportedViolations.add('script-src');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collapser.addMany(document.querySelectorAll('img'));
|
||||
collapser.addIFrames(document.querySelectorAll('iframe'));
|
||||
|
|
|
@ -391,32 +391,23 @@ var µm = µMatrix;
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
var contentScriptSummaryHandler = function(tabId, details) {
|
||||
// TODO: Investigate "Error in response to tabs.executeScript: TypeError:
|
||||
// Cannot read property 'locationURL' of null" (2013-11-12). When can this
|
||||
// happens?
|
||||
if ( !details || !details.locationURL ) { return; }
|
||||
|
||||
// scripts
|
||||
if ( details.inlineScript !== true ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/httpswitchboard/issues/25
|
||||
var pageStore = µm.pageStoreFromTabId(tabId);
|
||||
var contentScriptSummaryHandler = function(tabId, pageStore, details) {
|
||||
if ( pageStore === null ) { return; }
|
||||
|
||||
var pageHostname = pageStore.pageHostname;
|
||||
var µmuri = µm.URI.set(details.locationURL);
|
||||
var µmuri = µm.URI.set(details.documentURI);
|
||||
var frameURL = µmuri.normalizedURI();
|
||||
var frameHostname = µmuri.hostname;
|
||||
|
||||
var blocked = details.blocked;
|
||||
if ( blocked === undefined ) {
|
||||
blocked = µm.mustBlock(pageHostname, µmuri.hostname, 'script');
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/httpswitchboard/issues/333
|
||||
// Look-up here whether inline scripting is blocked for the frame.
|
||||
var inlineScriptBlocked = µm.mustBlock(pageHostname, frameHostname, 'script');
|
||||
var url = frameURL + '{inline_script}';
|
||||
pageStore.recordRequest('script', url, inlineScriptBlocked);
|
||||
µm.logger.writeOne(tabId, 'net', pageHostname, url, 'script', inlineScriptBlocked);
|
||||
pageStore.recordRequest('script', url, blocked);
|
||||
µm.logger.writeOne(tabId, 'net', pageHostname, url, 'script', blocked);
|
||||
|
||||
// https://github.com/gorhill/uMatrix/issues/225
|
||||
// A good place to force an update of the page title, as at this point
|
||||
|
@ -544,16 +535,19 @@ var onMessage = function(request, sender, callback) {
|
|||
break;
|
||||
|
||||
case 'securityPolicyViolation':
|
||||
if ( request.policy !== µm.cspNoWorkerSrc ) { break; }
|
||||
var url = µm.URI.hostnameFromURI(request.blockedURI) !== '' ?
|
||||
request.blockedURI :
|
||||
request.documentURI;
|
||||
if ( pageStore !== null ) {
|
||||
pageStore.hasWebWorkers = true;
|
||||
pageStore.recordRequest('script', url, true);
|
||||
}
|
||||
if ( tabContext !== null ) {
|
||||
µm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', true);
|
||||
if ( request.directive === 'worker-src' ) {
|
||||
var url = µm.URI.hostnameFromURI(request.blockedURI) !== '' ?
|
||||
request.blockedURI :
|
||||
request.documentURI;
|
||||
if ( pageStore !== null ) {
|
||||
pageStore.hasWebWorkers = true;
|
||||
pageStore.recordRequest('script', url, true);
|
||||
}
|
||||
if ( tabContext !== null ) {
|
||||
µm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', request.blocked);
|
||||
}
|
||||
} else if ( request.directive === 'script-src' ) {
|
||||
contentScriptSummaryHandler(tabId, pageStore, request);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -299,8 +299,17 @@ var onHeadersReceived = function(details) {
|
|||
var tabContext = µm.tabContextManager.lookup(tabId);
|
||||
if ( tabContext === null ) { return; }
|
||||
|
||||
var csp = [];
|
||||
var csp = [],
|
||||
cspReport = [];
|
||||
|
||||
// If javascript is not allowed, say so through a `Content-Security-Policy`
|
||||
// directive.
|
||||
// We block only inline-script tags, all the external javascript will be
|
||||
// blocked by our request handler.
|
||||
if ( µm.cspNoInlineScript === undefined ) {
|
||||
µm.cspNoInlineScript =
|
||||
"script-src 'unsafe-eval' blob: *;report-uri " + µm.cspReportURI;
|
||||
}
|
||||
if (
|
||||
µm.mustAllow(
|
||||
tabContext.rootHostname,
|
||||
|
@ -308,39 +317,53 @@ var onHeadersReceived = function(details) {
|
|||
'script'
|
||||
) !== true
|
||||
) {
|
||||
csp.push("script-src 'unsafe-eval' blob: *");
|
||||
csp.push(µm.cspNoInlineScript);
|
||||
} else {
|
||||
cspReport.push(µm.cspNoInlineScript);
|
||||
}
|
||||
|
||||
if ( µm.cspNoWorkerSrc === undefined ) {
|
||||
µm.cspNoWorkerSrc = vAPI.webextFlavor.startsWith('Mozilla-') ?
|
||||
"child-src 'none'; frame-src data: blob: *" :
|
||||
"worker-src 'none'" ;
|
||||
// TODO: Firefox will eventually support `worker-src`:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1231788
|
||||
if ( µm.cspNoWorker === undefined ) {
|
||||
µm.cspNoWorker = vAPI.webextFlavor.startsWith('Mozilla-') ?
|
||||
"child-src 'none'; frame-src data: blob: *;report-uri " :
|
||||
"worker-src 'none';report-uri " ;
|
||||
µm.cspNoWorker += µm.cspReportURI;
|
||||
}
|
||||
|
||||
if ( µm.tMatrix.evaluateSwitchZ('no-workers', tabContext.rootHostname) ) {
|
||||
csp.push(µm.cspNoWorkerSrc);
|
||||
}
|
||||
|
||||
if ( csp.length === 0 ) { return; }
|
||||
|
||||
// If javascript is not allowed, say so through a `Content-Security-Policy`
|
||||
// directive.
|
||||
// We block only inline-script tags, all the external javascript will be
|
||||
// blocked by our request handler.
|
||||
|
||||
var cspDirectives = csp.join(','),
|
||||
headers = details.responseHeaders,
|
||||
i = headerIndexFromName('content-security-policy', headers);
|
||||
// A CSP header is already present: just add our own directive as a
|
||||
// separate disposition (i.e. use comma).
|
||||
if ( i !== -1 ) {
|
||||
headers[i].value += ',' + cspDirectives;
|
||||
csp.push(µm.cspNoWorker);
|
||||
} else {
|
||||
headers.push({ name: 'Content-Security-Policy', value: cspDirectives });
|
||||
cspReport.push(µm.cspNoWorker);
|
||||
}
|
||||
|
||||
if ( requestType === 'doc' ) {
|
||||
µm.logger.writeOne(tabId, 'net', '', csp, 'CSP', false);
|
||||
var headers = details.responseHeaders,
|
||||
cspDirectives, i;
|
||||
|
||||
if ( csp.length !== 0 ) {
|
||||
cspDirectives = csp.join(',');
|
||||
i = headerIndexFromName('content-security-policy', headers);
|
||||
if ( i !== -1 ) {
|
||||
headers[i].value += ',' + cspDirectives;
|
||||
} else {
|
||||
headers.push({ name: 'Content-Security-Policy', value: cspDirectives });
|
||||
}
|
||||
if ( requestType === 'doc' ) {
|
||||
µm.logger.writeOne(tabId, 'net', '', cspDirectives, 'CSP', false);
|
||||
}
|
||||
}
|
||||
|
||||
if ( cspReport.length !== 0 ) {
|
||||
cspDirectives = cspReport.join(',');
|
||||
i = headerIndexFromName('content-security-policy-report-only', headers);
|
||||
if ( i !== -1 ) {
|
||||
headers[i].value += ',' + cspDirectives;
|
||||
} else {
|
||||
headers.push({
|
||||
name: 'Content-Security-Policy-Report-Only',
|
||||
value: cspDirectives
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { responseHeaders: headers };
|
||||
|
|
Loading…
Reference in a new issue