reliably report web worker and inline script presence

pull/2/head
Raymond Hill 8 years ago
parent b870757e94
commit 821e45751a
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

@ -110,7 +110,9 @@ return {
}, },
clearBrowserCacheCycle: 0, clearBrowserCacheCycle: 0,
cspNoWorkerSrc: undefined, cspNoInlineScript: undefined,
cspNoWorker: undefined,
cspReportURI: 'about:blank',
updateAssetsEvery: 11 * oneDay + 1 * oneHour + 1 * oneMinute + 1 * oneSecond, updateAssetsEvery: 11 * oneDay + 1 * oneHour + 1 * oneMinute + 1 * oneSecond,
firstUpdateAfter: 11 * oneMinute, firstUpdateAfter: 11 * oneMinute,
nextUpdateAfter: 11 * oneHour, nextUpdateAfter: 11 * oneHour,

@ -30,16 +30,68 @@
if ( typeof vAPI !== 'object' ) { return; } 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( vAPI.messaging.send(
'contentscript.js', 'contentscript.js',
{ {
what: 'securityPolicyViolation', what: 'securityPolicyViolation',
policy: ev.originalPolicy, directive: directive,
blockedURI: ev.blockedURI, blockedURI: blockedURL,
documentURI: ev.documentURI 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. // Mind "on..." attributes.
(function() { (function() {
vAPI.messaging.send('contentscript.js', { if (
what: 'contentScriptSummary', vAPI.reportedViolations === undefined ||
locationURL: window.location.href, vAPI.reportedViolations.has('script-src') === false
inlineScript: ) {
document.querySelector('script:not([src])') !== null || if ( document.querySelector('script:not([src])') !== null ) {
document.querySelector('a[href^="javascript:"]') !== null || vAPI.messaging.send('contentscript.js', {
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 what: 'securityPolicyViolation',
}); directive: 'script-src',
documentURI: window.location.href
});
if ( vAPI.reportedViolations ) {
vAPI.reportedViolations.add('script-src');
}
}
}
collapser.addMany(document.querySelectorAll('img')); collapser.addMany(document.querySelectorAll('img'));
collapser.addIFrames(document.querySelectorAll('iframe')); collapser.addIFrames(document.querySelectorAll('iframe'));

@ -391,32 +391,23 @@ var µm = µMatrix;
/******************************************************************************/ /******************************************************************************/
var contentScriptSummaryHandler = function(tabId, details) { var contentScriptSummaryHandler = function(tabId, pageStore, 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);
if ( pageStore === null ) { return; } if ( pageStore === null ) { return; }
var pageHostname = pageStore.pageHostname; var pageHostname = pageStore.pageHostname;
var µmuri = µm.URI.set(details.locationURL); var µmuri = µm.URI.set(details.documentURI);
var frameURL = µmuri.normalizedURI(); 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 // https://github.com/gorhill/httpswitchboard/issues/333
// Look-up here whether inline scripting is blocked for the frame. // Look-up here whether inline scripting is blocked for the frame.
var inlineScriptBlocked = µm.mustBlock(pageHostname, frameHostname, 'script');
var url = frameURL + '{inline_script}'; var url = frameURL + '{inline_script}';
pageStore.recordRequest('script', url, inlineScriptBlocked); pageStore.recordRequest('script', url, blocked);
µm.logger.writeOne(tabId, 'net', pageHostname, url, 'script', inlineScriptBlocked); µm.logger.writeOne(tabId, 'net', pageHostname, url, 'script', blocked);
// https://github.com/gorhill/uMatrix/issues/225 // https://github.com/gorhill/uMatrix/issues/225
// A good place to force an update of the page title, as at this point // 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; break;
case 'securityPolicyViolation': case 'securityPolicyViolation':
if ( request.policy !== µm.cspNoWorkerSrc ) { break; } if ( request.directive === 'worker-src' ) {
var url = µm.URI.hostnameFromURI(request.blockedURI) !== '' ? var url = µm.URI.hostnameFromURI(request.blockedURI) !== '' ?
request.blockedURI : request.blockedURI :
request.documentURI; request.documentURI;
if ( pageStore !== null ) { if ( pageStore !== null ) {
pageStore.hasWebWorkers = true; pageStore.hasWebWorkers = true;
pageStore.recordRequest('script', url, true); pageStore.recordRequest('script', url, true);
} }
if ( tabContext !== null ) { if ( tabContext !== null ) {
µm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', true); µm.logger.writeOne(tabId, 'net', rootHostname, url, 'worker', request.blocked);
}
} else if ( request.directive === 'script-src' ) {
contentScriptSummaryHandler(tabId, pageStore, request);
} }
break; break;

@ -299,8 +299,17 @@ var onHeadersReceived = function(details) {
var tabContext = µm.tabContextManager.lookup(tabId); var tabContext = µm.tabContextManager.lookup(tabId);
if ( tabContext === null ) { return; } 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 ( if (
µm.mustAllow( µm.mustAllow(
tabContext.rootHostname, tabContext.rootHostname,
@ -308,39 +317,53 @@ var onHeadersReceived = function(details) {
'script' 'script'
) !== true ) !== true
) { ) {
csp.push("script-src 'unsafe-eval' blob: *"); csp.push(µm.cspNoInlineScript);
} else {
cspReport.push(µm.cspNoInlineScript);
} }
if ( µm.cspNoWorkerSrc === undefined ) { // TODO: Firefox will eventually support `worker-src`:
µm.cspNoWorkerSrc = vAPI.webextFlavor.startsWith('Mozilla-') ? // https://bugzilla.mozilla.org/show_bug.cgi?id=1231788
"child-src 'none'; frame-src data: blob: *" : if ( µm.cspNoWorker === undefined ) {
"worker-src 'none'" ; µ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) ) { if ( µm.tMatrix.evaluateSwitchZ('no-workers', tabContext.rootHostname) ) {
csp.push(µm.cspNoWorkerSrc); csp.push(µm.cspNoWorker);
} else {
cspReport.push(µm.cspNoWorker);
} }
if ( csp.length === 0 ) { return; } var headers = details.responseHeaders,
cspDirectives, i;
// 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(','), if ( csp.length !== 0 ) {
headers = details.responseHeaders, cspDirectives = csp.join(',');
i = headerIndexFromName('content-security-policy', headers); i = headerIndexFromName('content-security-policy', headers);
// A CSP header is already present: just add our own directive as a if ( i !== -1 ) {
// separate disposition (i.e. use comma). headers[i].value += ',' + cspDirectives;
if ( i !== -1 ) { } else {
headers[i].value += ',' + cspDirectives; headers.push({ name: 'Content-Security-Policy', value: cspDirectives });
} else { }
headers.push({ name: 'Content-Security-Policy', value: cspDirectives }); if ( requestType === 'doc' ) {
µm.logger.writeOne(tabId, 'net', '', cspDirectives, 'CSP', false);
}
} }
if ( requestType === 'doc' ) { if ( cspReport.length !== 0 ) {
µm.logger.writeOne(tabId, 'net', '', csp, 'CSP', false); 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 }; return { responseHeaders: headers };

Loading…
Cancel
Save