diff options
-rw-r--r-- | js/traffic.js | 484 |
1 files changed, 243 insertions, 241 deletions
diff --git a/js/traffic.js b/js/traffic.js index 0c71f85..aae4070 100644 --- a/js/traffic.js +++ b/js/traffic.js @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. - Home: https://libregit.spks.xyz/heckyel/ematrix + Home: https://notabug.org/heckyel/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -31,255 +31,257 @@ // Intercept and filter web requests according to white and black lists. var onBeforeRootFrameRequestHandler = function (details) { - let ηm = ηMatrix; - let requestURL = details.url; - let requestHostname = UriTools.hostnameFromURI(requestURL); - let tabId = details.tabId; + let ηm = ηMatrix; + let requestURL = details.url; + let requestHostname = UriTools.hostnameFromURI(requestURL); + let tabId = details.tabId; - ηm.tabContextManager.push(tabId, requestURL); + ηm.tabContextManager.push(tabId, requestURL); - let tabContext = ηm.tabContextManager.mustLookup(tabId); - let rootHostname = tabContext.rootHostname; + let tabContext = ηm.tabContextManager.mustLookup(tabId); + let rootHostname = tabContext.rootHostname; - // Disallow request as per matrix? - let block = ηm.mustBlock(rootHostname, requestHostname, 'doc'); + // Disallow request as per matrix? + let block = ηm.mustBlock(rootHostname, requestHostname, 'doc'); - let pageStore = ηm.pageStoreFromTabId(tabId); - pageStore.recordRequest('doc', requestURL, block); - ηm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block); + let pageStore = ηm.pageStoreFromTabId(tabId); + pageStore.recordRequest('doc', requestURL, block); + ηm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block); - // Not blocked - if (!block) { + // Not blocked + if (!block) { // rhill 2013-11-07: Senseless to do this for // behind-the-scene requests. ηm.cookieHunter.recordPageCookies(pageStore); return; - } + } - // Blocked - var query = btoa(JSON.stringify({ + // Blocked + var query = btoa(JSON.stringify({ url: requestURL, hn: requestHostname, why: '?' - })); + })); - vAPI.tabs.replace(tabId, - vAPI.getURL('main-blocked.html?details=') + query); + vAPI.tabs.replace(tabId, + vAPI.getURL('main-blocked.html?details=') + query); - return { - cancel: true - }; + return { + cancel: true + }; }; // Intercept and filter web requests according to white and black lists. var onBeforeRequestHandler = function (details) { - let ηm = ηMatrix; + let ηm = ηMatrix; let requestURL = details.url; let requestScheme = UriTools.schemeFromURI(requestURL); - if (UriTools.isNetworkScheme(requestScheme) === false) { - return; - } + if (UriTools.isNetworkScheme(requestScheme) === false) { + return; + } - var requestType = requestTypeNormalizer[details.type] || 'other'; + var requestType = requestTypeNormalizer[details.type] || 'other'; - // https://github.com/gorhill/httpswitchboard/issues/303 - // Wherever the main doc comes from, create a receiver page - // URL: synthetize one if needed. - if (requestType === 'doc' && details.parentFrameId === -1) { + // https://github.com/gorhill/httpswitchboard/issues/303 + // Wherever the main doc comes from, create a receiver page + // URL: synthetize one if needed. + if (requestType === 'doc' && details.parentFrameId === -1) { return onBeforeRootFrameRequestHandler(details); - } - - // Re-classify orphan HTTP requests as behind-the-scene - // requests. There is not much else which can be done, because - // there are URLs which cannot be handled by ηMatrix, - // i.e. `opera://startpage`, as this would lead to - // complications with no obvious solution, like how to scope - // on unknown scheme? Etc. - // https://github.com/gorhill/httpswitchboard/issues/191 - // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275 - let tabContext = ηm.tabContextManager.mustLookup(details.tabId); + } + + // Re-classify orphan HTTP requests as behind-the-scene + // requests. There is not much else which can be done, because + // there are URLs which cannot be handled by ηMatrix, + // i.e. `opera://startpage`, as this would lead to + // complications with no obvious solution, like how to scope + // on unknown scheme? Etc. + // https://github.com/gorhill/httpswitchboard/issues/191 + // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275 + let tabContext = ηm.tabContextManager.mustLookup(details.tabId); let tabId = tabContext.tabId; let rootHostname = tabContext.rootHostname; let specificity = 0; - // Filter through matrix - let block = ηm.tMatrix.mustBlock(rootHostname, - UriTools.hostnameFromURI(requestURL), - requestType); - if (block) { + // Filter through matrix + let block = ηm.tMatrix.mustBlock(rootHostname, + UriTools.hostnameFromURI(requestURL), + requestType); + if (block) { specificity = ηm.tMatrix.specificityRegister; - } - - // Record request. - // https://github.com/gorhill/httpswitchboard/issues/342 - // The way requests are handled now, it may happen at this - // point some processing has already been performed, and that - // a synthetic URL has been constructed for logging - // purpose. Use this synthetic URL if it is available. - let pageStore = ηm.mustPageStoreFromTabId(tabId); - - // Enforce strict secure connection? - if (tabContext.secure - && UriTools.isSecureScheme(requestScheme) === false) { + } + + // Record request. + // https://github.com/gorhill/httpswitchboard/issues/342 + // The way requests are handled now, it may happen at this + // point some processing has already been performed, and that + // a synthetic URL has been constructed for logging + // purpose. Use this synthetic URL if it is available. + let pageStore = ηm.mustPageStoreFromTabId(tabId); + + // Enforce strict secure connection? + if (tabContext.secure + && UriTools.isSecureScheme(requestScheme) === false) { pageStore.hasMixedContent = true; if (block === false) { - block = ηm.tMatrix.evaluateSwitchZ('https-strict', rootHostname); + block = ηm.tMatrix.evaluateSwitchZ('https-strict', rootHostname); } - } + } - pageStore.recordRequest(requestType, requestURL, block); - ηm.logger.writeOne(tabId, 'net', rootHostname, - requestURL, details.type, block); + pageStore.recordRequest(requestType, requestURL, block); + ηm.logger.writeOne(tabId, 'net', rootHostname, + requestURL, details.type, block); - if (block) { + if (block) { pageStore.cacheBlockedCollapsible(requestType, - requestURL, specificity); + requestURL, specificity); return { - 'cancel': true - }; - } + 'cancel': true + }; + } }; // Sanitize outgoing headers as per user settings. var onBeforeSendHeadersHandler = function (details) { - let ηm = ηMatrix; + let ηm = ηMatrix; let requestURL = details.url; let requestScheme = UriTools.schemeFromURI(requestURL); - // Ignore non-network schemes - if (UriTools.isNetworkScheme(requestScheme) === false) { - return; - } - - // Re-classify orphan HTTP requests as behind-the-scene - // requests. There is not much else which can be done, because - // there are URLs which cannot be handled by HTTP Switchboard, - // i.e. `opera://startpage`, as this would lead to - // complications with no obvious solution, like how to scope - // on unknown scheme? Etc. - // https://github.com/gorhill/httpswitchboard/issues/191 - // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275 - let tabId = details.tabId; + // Ignore non-network schemes + if (UriTools.isNetworkScheme(requestScheme) === false) { + return; + } + + // Re-classify orphan HTTP requests as behind-the-scene + // requests. There is not much else which can be done, because + // there are URLs which cannot be handled by HTTP Switchboard, + // i.e. `opera://startpage`, as this would lead to + // complications with no obvious solution, like how to scope + // on unknown scheme? Etc. + // https://github.com/gorhill/httpswitchboard/issues/191 + // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275 + let tabId = details.tabId; let pageStore = ηm.mustPageStoreFromTabId(tabId); let requestType = requestTypeNormalizer[details.type] || 'other'; let requestHeaders = details.requestHeaders; let headerIndex, headerValue; - // https://github.com/gorhill/httpswitchboard/issues/342 - // Is this hyperlink auditing? If yes, create a synthetic URL - // for reporting hyperlink auditing in request log. This way - // the user is better informed of what went on. - - // https://html.spec.whatwg.org/multipage/links.html#hyperlink-auditing - // - // Target URL = the href of the link - // Doc URL = URL of the document containing the target URL - // Ping URLs = servers which will be told that user clicked target URL - // - // `Content-Type` = `text/ping` (always present) - // `Ping-To` = target URL (always present) - // `Ping-From` = doc URL - // `Referer` = doc URL - // request URL = URL which will receive the information - // - // With hyperlink-auditing, removing header(s) is pointless, the whole - // request must be cancelled. - headerIndex = headerIndexFromName('ping-to', requestHeaders); - if (headerIndex !== -1) { + // https://github.com/gorhill/httpswitchboard/issues/342 + // Is this hyperlink auditing? If yes, create a synthetic URL + // for reporting hyperlink auditing in request log. This way + // the user is better informed of what went on. + + // https://html.spec.whatwg.org/multipage/links.html#hyperlink-auditing + // + // Target URL = the href of the link + // Doc URL = URL of the document containing the target URL + // Ping URLs = servers which will be told that user clicked target URL + // + // `Content-Type` = `text/ping` (always present) + // `Ping-To` = target URL (always present) + // `Ping-From` = doc URL + // `Referer` = doc URL + // request URL = URL which will receive the information + // + // With hyperlink-auditing, removing header(s) is pointless, the whole + // request must be cancelled. + headerIndex = headerIndexFromName('ping-to', requestHeaders); + if (headerIndex !== -1) { headerValue = requestHeaders[headerIndex].value; if (headerValue !== '') { - let block = ηm.userSettings.processHyperlinkAuditing; - pageStore.recordRequest('other', - requestURL - + '{Ping-To:' - + headerValue - + '}', block); - ηm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block); - if (block) { + let block = ηm.userSettings.processHyperlinkAuditing; + pageStore.recordRequest('other', + requestURL + + '{Ping-To:' + + headerValue + + '}', block); + ηm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block); + if (block) { ηm.hyperlinkAuditingFoiledCounter += 1; return { - 'cancel': true - }; - } + 'cancel': true + }; + } } - } + } - // If we reach this point, request is not blocked, so what is - // left to do is to sanitize headers. - let rootHostname = pageStore.pageHostname; + // If we reach this point, request is not blocked, so what is + // left to do is to sanitize headers. + let rootHostname = pageStore.pageHostname; let requestHostname = UriTools.hostnameFromURI(requestURL); let modified = false; - // Process `Cookie` header. - headerIndex = headerIndexFromName('cookie', requestHeaders); - if (headerIndex !== -1 - && ηm.mustBlock(rootHostname, requestHostname, 'cookie')) { + // Process `Cookie` header. + headerIndex = headerIndexFromName('cookie', requestHeaders); + if (headerIndex !== -1 + && ηm.mustBlock(rootHostname, requestHostname, 'cookie')) { modified = true; headerValue = requestHeaders[headerIndex].value; requestHeaders.splice(headerIndex, 1); ηm.cookieHeaderFoiledCounter++; if (requestType === 'doc') { - ηm.logger.writeOne(tabId, 'net', '', headerValue, 'COOKIE', true); + pageStore.perLoadBlockedRequestCount++; + ηm.logger.writeOne(tabId, 'net', '', headerValue, 'COOKIE', true); } - } + } - // Process `Referer` header. - // https://github.com/gorhill/httpswitchboard/issues/222#issuecomment-44828402 + // Process `Referer` header. + // https://github.com/gorhill/httpswitchboard/issues/222#issuecomment-44828402 - // https://github.com/gorhill/uMatrix/issues/320 - // http://tools.ietf.org/html/rfc6454#section-7.3 - // "The user agent MAY include an Origin header field in any HTTP - // "request. - // "The user agent MUST NOT include more than one Origin header field in - // "any HTTP request. - // "Whenever a user agent issues an HTTP request from a "privacy- - // "sensitive" context, the user agent MUST send the value "null" in the - // "Origin header field." + // https://github.com/gorhill/uMatrix/issues/320 + // http://tools.ietf.org/html/rfc6454#section-7.3 + // "The user agent MAY include an Origin header field in any HTTP + // "request. + // "The user agent MUST NOT include more than one Origin header field in + // "any HTTP request. + // "Whenever a user agent issues an HTTP request from a "privacy- + // "sensitive" context, the user agent MUST send the value "null" in the + // "Origin header field." - // https://github.com/gorhill/uMatrix/issues/358 - // Do not spoof `Origin` header for the time being. + // https://github.com/gorhill/uMatrix/issues/358 + // Do not spoof `Origin` header for the time being. - // https://github.com/gorhill/uMatrix/issues/773 - // For non-GET requests, remove `Referer` header instead of spoofing it. + // https://github.com/gorhill/uMatrix/issues/773 + // For non-GET requests, remove `Referer` header instead of spoofing it. - headerIndex = headerIndexFromName('referer', requestHeaders); - if (headerIndex !== -1) { + headerIndex = headerIndexFromName('referer', requestHeaders); + if (headerIndex !== -1) { headerValue = requestHeaders[headerIndex].value; if (headerValue !== '') { - let toDomain = UriTools.domainFromHostname(requestHostname); - if (toDomain !== '' - && toDomain !== UriTools.domainFromURI(headerValue)) { + let toDomain = UriTools.domainFromHostname(requestHostname); + if (toDomain !== '' + && toDomain !== UriTools.domainFromURI(headerValue)) { pageStore.has3pReferrer = true; if (ηm.tMatrix.evaluateSwitchZ('referrer-spoof', - rootHostname)) { - modified = true; - let newValue; - if (details.method === 'GET') { + rootHostname)) { + modified = true; + let newValue; + if (details.method === 'GET') { newValue = requestHeaders[headerIndex].value = - requestScheme + '://' + requestHostname + '/'; - } else { + requestScheme + '://' + requestHostname + '/'; + } else { requestHeaders.splice(headerIndex, 1); - } - ηm.refererHeaderFoiledCounter++; - if (requestType === 'doc') { + } + ηm.refererHeaderFoiledCounter++; + if (requestType === 'doc') { + pageStore.perLoadBlockedRequestCount++; ηm.logger.writeOne(tabId, 'net', '', - headerValue, 'REFERER', true); + headerValue, 'REFERER', true); if (newValue !== undefined) { - ηm.logger.writeOne(tabId, 'net', '', - newValue, 'REFERER', false); + ηm.logger.writeOne(tabId, 'net', '', + newValue, 'REFERER', false); } - } + } } - } + } } - } + } - if (modified) { + if (modified) { return { - requestHeaders: requestHeaders - }; - } + requestHeaders: requestHeaders + }; + } }; // To prevent inline javascript from being executed. @@ -290,144 +292,144 @@ // This fixes: // https://github.com/gorhill/httpswitchboard/issues/35 var onHeadersReceived = function (details) { - // Ignore schemes other than 'http...' - let ηm = ηMatrix; + // Ignore schemes other than 'http...' + let ηm = ηMatrix; let tabId = details.tabId; let requestURL = details.url; let requestType = requestTypeNormalizer[details.type] || 'other'; - // https://github.com/gorhill/uMatrix/issues/145 - // Check if the main_frame is a download - if (requestType === 'doc') { + // https://github.com/gorhill/uMatrix/issues/145 + // Check if the main_frame is a download + if (requestType === 'doc') { ηm.tabContextManager.push(tabId, requestURL); - } + } - let tabContext = ηm.tabContextManager.lookup(tabId); - if (tabContext === null) { - return; - } + let tabContext = ηm.tabContextManager.lookup(tabId); + if (tabContext === null) { + return; + } - let csp = []; + let csp = []; let cspReport = []; let rootHostname = tabContext.rootHostname; let requestHostname = UriTools.hostnameFromURI(requestURL); - // Inline script tags. - if (ηm.mustAllow(rootHostname, requestHostname, 'script') !== true) { + // Inline script tags. + if (ηm.mustAllow(rootHostname, requestHostname, 'script') !== true) { csp.push(ηm.cspNoInlineScript); - } + } - // Inline style tags. - if (ηm.mustAllow(rootHostname, requestHostname, 'css') !== true) { + // Inline style tags. + if (ηm.mustAllow(rootHostname, requestHostname, 'css') !== true) { csp.push(ηm.cspNoInlineStyle); - } + } - // https://bugzilla.mozilla.org/show_bug.cgi?id=1302667 - let cspNoWorker = ηm.cspNoWorker; - if (cspNoWorker === undefined) { + // https://bugzilla.mozilla.org/show_bug.cgi?id=1302667 + let cspNoWorker = ηm.cspNoWorker; + if (cspNoWorker === undefined) { cspNoWorker = cspNoWorkerInit(); - } + } - if (ηm.tMatrix.evaluateSwitchZ('no-workers', rootHostname)) { + if (ηm.tMatrix.evaluateSwitchZ('no-workers', rootHostname)) { csp.push(cspNoWorker); - } else if (ηm.rawSettings.disableCSPReportInjection === false) { + } else if (ηm.rawSettings.disableCSPReportInjection === false) { cspReport.push(cspNoWorker); - } + } - let headers = details.responseHeaders; + let headers = details.responseHeaders; let cspDirectives, i; - if (csp.length !== 0) { + if (csp.length !== 0) { cspDirectives = csp.join(','); i = headerIndexFromName('content-security-policy', headers); if (i !== -1) { - headers[i].value += ',' + cspDirectives; + headers[i].value += ',' + cspDirectives; } else { - headers.push({ + headers.push({ name: 'Content-Security-Policy', value: cspDirectives - }); + }); } if (requestType === 'doc') { - ηm.logger.writeOne(tabId, 'net', '', cspDirectives, 'CSP', false); + ηm.logger.writeOne(tabId, 'net', '', cspDirectives, 'CSP', false); } - } + } - if (cspReport.length !== 0) { + if (cspReport.length !== 0) { cspDirectives = cspReport.join(','); i = headerIndexFromName('content-security-policy-report-only', - headers); + headers); if (i !== -1) { - headers[i].value += ',' + cspDirectives; + headers[i].value += ',' + cspDirectives; } else { - headers.push({ + headers.push({ name: 'Content-Security-Policy-Report-Only', value: cspDirectives - }); + }); } - } + } - return { - responseHeaders: headers - }; + return { + responseHeaders: headers + }; }; let cspNoWorkerInit = function () { - if (ηMatrix.cspNoWorker === undefined) { - ηMatrix.cspNoWorker = "worker-src 'none'; " - +"frame-src data: blob: *; " - +"report-uri about:blank"; - } + if (ηMatrix.cspNoWorker === undefined) { + ηMatrix.cspNoWorker = "worker-src 'none'; " + +"frame-src data: blob: *; " + +"report-uri about:blank"; + } - return ηMatrix.cspNoWorker; + return ηMatrix.cspNoWorker; }; // Caller must ensure headerName is normalized to lower case. let headerIndexFromName = function (headerName, headers) { - for (let i=0; i<headers.length; ++i) { + for (let i=0; i<headers.length; ++i) { if ( headers[i].name.toLowerCase() === headerName ) { - return i; + return i; } - } + } - return -1; + return -1; }; let requestTypeNormalizer = { - 'font' : 'css', - 'image' : 'image', - 'imageset' : 'image', - 'main_frame' : 'doc', - 'media' : 'media', - 'object' : 'media', - 'other' : 'other', - 'script' : 'script', - 'stylesheet' : 'css', - 'sub_frame' : 'frame', - 'websocket' : 'xhr', - 'xmlhttprequest': 'xhr' + 'font' : 'css', + 'image' : 'image', + 'imageset' : 'image', + 'main_frame' : 'doc', + 'media' : 'media', + 'object' : 'media', + 'other' : 'other', + 'script' : 'script', + 'stylesheet' : 'css', + 'sub_frame' : 'frame', + 'websocket' : 'xhr', + 'xmlhttprequest': 'xhr' }; vAPI.net.onBeforeRequest = { - extra: [ 'blocking' ], - callback: onBeforeRequestHandler + extra: [ 'blocking' ], + callback: onBeforeRequestHandler }; vAPI.net.onBeforeSendHeaders = { - extra: [ 'blocking', 'requestHeaders' ], - callback: onBeforeSendHeadersHandler + extra: [ 'blocking', 'requestHeaders' ], + callback: onBeforeSendHeadersHandler }; vAPI.net.onHeadersReceived = { - urls: [ 'http://*/*', 'https://*/*' ], - types: [ 'main_frame', 'sub_frame' ], - extra: [ 'blocking', 'responseHeaders' ], - callback: onHeadersReceived + urls: [ 'http://*/*', 'https://*/*' ], + types: [ 'main_frame', 'sub_frame' ], + extra: [ 'blocking', 'responseHeaders' ], + callback: onHeadersReceived }; return { - start: function () { - vAPI.net.registerListeners(); - }, + start: function () { + vAPI.net.registerListeners(); + }, }; })(); |