Browse Source

code review of request headers processing code

- drop needless overhead now that legacy support is dropped
- output extra information about the headers which are
  modified and their value before/after modification
pull/2/head
gorhill 7 years ago
parent
commit
c4c0d450fd
No known key found for this signature in database GPG Key ID: 25E1490B761470C2
  1. 128
      platform/chromium/vapi-background.js
  2. 9
      src/js/logger-ui.js
  3. 137
      src/js/traffic.js

128
platform/chromium/vapi-background.js

@ -55,9 +55,7 @@ var noopFunc = function(){};
// https://github.com/gorhill/uMatrix/issues/234 // https://github.com/gorhill/uMatrix/issues/234
// https://developer.chrome.com/extensions/privacy#property-network // https://developer.chrome.com/extensions/privacy#property-network
chrome.privacy.network.networkPredictionEnabled.set({
value: false
});
chrome.privacy.network.networkPredictionEnabled.set({ value: false });
/******************************************************************************/ /******************************************************************************/
@ -125,9 +123,7 @@ vAPI.tabs.registerListeners = function() {
var onCreatedNavigationTarget = function(details) { var onCreatedNavigationTarget = function(details) {
//console.debug('onCreatedNavigationTarget: tab id %d = "%s"', details.tabId, details.url); //console.debug('onCreatedNavigationTarget: tab id %d = "%s"', details.tabId, details.url);
if ( reGoodForWebRequestAPI.test(details.url) ) {
return;
}
if ( reGoodForWebRequestAPI.test(details.url) ) { return; }
details.tabId = details.tabId.toString(); details.tabId = details.tabId.toString();
onNavigationClient(details); onNavigationClient(details);
}; };
@ -563,78 +559,7 @@ vAPI.net = {};
/******************************************************************************/ /******************************************************************************/
vAPI.net.registerListeners = function() { vAPI.net.registerListeners = function() {
var µm = µMatrix,
reNetworkURL = /^(?:https?|wss?):\/\//,
httpRequestHeadersJunkyard = [];
// Abstraction layer to deal with request headers
// >>>>>>>>
var httpRequestHeadersFactory = function(headers) {
var entry = httpRequestHeadersJunkyard.pop();
if ( entry ) {
return entry.init(headers);
}
return new HTTPRequestHeaders(headers);
};
var HTTPRequestHeaders = function(headers) {
this.init(headers);
};
HTTPRequestHeaders.prototype.init = function(headers) {
this.modified = false;
this.headers = headers;
return this;
};
HTTPRequestHeaders.prototype.dispose = function() {
var r = this.modified ? this.headers : null;
this.headers = null;
httpRequestHeadersJunkyard.push(this);
return r;
};
HTTPRequestHeaders.prototype.getHeader = function(target) {
var headers = this.headers;
var header, name;
var i = headers.length;
while ( i-- ) {
header = headers[i];
name = header.name.toLowerCase();
if ( name === target ) {
return header.value;
}
}
return '';
};
HTTPRequestHeaders.prototype.setHeader = function(target, value, create) {
var headers = this.headers;
var header, name;
var i = headers.length;
while ( i-- ) {
header = headers[i];
name = header.name.toLowerCase();
if ( name === target ) {
break;
}
}
if ( i < 0 && !create ) { // Header not found, don't add it
return false;
}
if ( i < 0 ) { // Header not found, add it
headers.push({ name: target, value: value });
} else if ( value === '' ) { // Header found, remove it
headers.splice(i, 1);
} else { // Header found, modify it
header.value = value;
}
this.modified = true;
return true;
};
// <<<<<<<<
// End of: Abstraction layer to deal with request headers
var µm = µMatrix;
// Normalizing request types // Normalizing request types
// >>>>>>>> // >>>>>>>>
@ -647,30 +572,17 @@ vAPI.net.registerListeners = function() {
var normalizeRequestDetails = function(details) { var normalizeRequestDetails = function(details) {
details.tabId = details.tabId.toString(); details.tabId = details.tabId.toString();
var type = details.type;
if ( type === 'imageset' ) {
details.type = 'image';
return;
}
// The rest of the function code is to normalize request type // The rest of the function code is to normalize request type
if ( type !== 'other' ) {
return;
}
if ( details.requestHeaders instanceof HTTPRequestHeaders ) {
if ( details.requestHeaders.getHeader('ping-to') !== '' ) {
details.type = 'ping';
return;
}
}
if ( details.type !== 'other' ) { return; }
// Try to map known "extension" part of URL to request type. // Try to map known "extension" part of URL to request type.
var path = µm.URI.pathFromURI(details.url), var path = µm.URI.pathFromURI(details.url),
pos = path.indexOf('.', path.length - 6); pos = path.indexOf('.', path.length - 6);
if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) {
details.type = type;
if ( pos !== -1 ) {
var type = extToTypeMap.get(path.slice(pos + 1));
if ( type !== undefined ) {
details.type = type;
}
} }
}; };
// <<<<<<<< // <<<<<<<<
@ -681,17 +593,9 @@ vAPI.net.registerListeners = function() {
var onBeforeRequestClient = this.onBeforeRequest.callback; var onBeforeRequestClient = this.onBeforeRequest.callback;
chrome.webRequest.onBeforeRequest.addListener( chrome.webRequest.onBeforeRequest.addListener(
function(details) { function(details) {
if ( reNetworkURL.test(details.url) ) {
normalizeRequestDetails(details);
return onBeforeRequestClient(details);
}
normalizeRequestDetails(details);
return onBeforeRequestClient(details);
}, },
//function(details) {
// quickProfiler.start('onBeforeRequest');
// var r = onBeforeRequest(details);
// quickProfiler.stop();
// return r;
//},
{ {
'urls': this.onBeforeRequest.urls || [ '<all_urls>' ], 'urls': this.onBeforeRequest.urls || [ '<all_urls>' ],
'types': this.onBeforeRequest.types || undefined 'types': this.onBeforeRequest.types || undefined
@ -701,16 +605,8 @@ vAPI.net.registerListeners = function() {
var onBeforeSendHeadersClient = this.onBeforeSendHeaders.callback; var onBeforeSendHeadersClient = this.onBeforeSendHeaders.callback;
var onBeforeSendHeaders = function(details) { var onBeforeSendHeaders = function(details) {
details.requestHeaders = httpRequestHeadersFactory(details.requestHeaders);
normalizeRequestDetails(details); normalizeRequestDetails(details);
var result = onBeforeSendHeadersClient(details);
if ( typeof result === 'object' ) {
return result;
}
var modifiedHeaders = details.requestHeaders.dispose();
if ( modifiedHeaders !== null ) {
return { requestHeaders: modifiedHeaders };
}
return onBeforeSendHeadersClient(details);
}; };
chrome.webRequest.onBeforeSendHeaders.addListener( chrome.webRequest.onBeforeSendHeaders.addListener(
onBeforeSendHeaders, onBeforeSendHeaders,

9
src/js/logger-ui.js

@ -49,6 +49,11 @@ var prettyRequestTypes = {
'xmlhttprequest': 'xhr' 'xmlhttprequest': 'xhr'
}; };
var dontEmphasizeSet = new Set([
'COOKIE',
'REFERER'
]);
/******************************************************************************/ /******************************************************************************/
// Adjust top padding of content table, to match that of toolbar height. // Adjust top padding of content table, to match that of toolbar height.
@ -245,7 +250,9 @@ var renderLogEntry = function(entry) {
tr.cells[fvdc].textContent = ''; tr.cells[fvdc].textContent = '';
} }
tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d2] || entry.d2); tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d2] || entry.d2);
if ( entry.d2 === 'cookie' ) {
if ( dontEmphasizeSet.has(entry.d2) ) {
tr.cells[fvdc+2].textContent = entry.d1;
} else if ( entry.d2 === 'cookie' ) {
tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1)); tr.cells[fvdc+2].appendChild(emphasizeCookie(entry.d1));
} else { } else {
tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1)); tr.cells[fvdc+2].appendChild(emphasizeHostname(entry.d1));

137
src/js/traffic.js

@ -19,8 +19,6 @@
Home: https://github.com/gorhill/uMatrix Home: https://github.com/gorhill/uMatrix
*/ */
/* jshint boss: true */
'use strict'; 'use strict';
/******************************************************************************/ /******************************************************************************/
@ -75,6 +73,13 @@ var onBeforeRootFrameRequestHandler = function(details) {
// Intercept and filter web requests according to white and black lists. // Intercept and filter web requests according to white and black lists.
var onBeforeRequestHandler = function(details) { var onBeforeRequestHandler = function(details) {
var µm = µMatrix,
µmuri = µm.URI,
requestURL = details.url,
requestScheme = µmuri.schemeFromURI(requestURL);
if ( µmuri.isNetworkScheme(requestScheme) === false ) { return; }
var requestType = requestTypeNormalizer[details.type] || 'other'; var requestType = requestTypeNormalizer[details.type] || 'other';
// https://github.com/gorhill/httpswitchboard/issues/303 // https://github.com/gorhill/httpswitchboard/issues/303
@ -84,17 +89,6 @@ var onBeforeRequestHandler = function(details) {
return onBeforeRootFrameRequestHandler(details); return onBeforeRootFrameRequestHandler(details);
} }
var µm = µMatrix,
µmuri = µm.URI,
requestURL = details.url,
requestScheme = µmuri.schemeFromURI(requestURL);
// Ignore non-network schemes
if ( µmuri.isNetworkScheme(requestScheme) === false ) {
µm.logger.writeOne('', 'info', 'request not processed: ' + requestURL);
return;
}
// Re-classify orphan HTTP requests as behind-the-scene requests. There is // Re-classify orphan HTTP requests as behind-the-scene requests. There is
// not much else which can be done, because there are URLs // not much else which can be done, because there are URLs
// which cannot be handled by µMatrix, i.e. `opera://startpage`, // which cannot be handled by µMatrix, i.e. `opera://startpage`,
@ -148,12 +142,13 @@ var onBeforeRequestHandler = function(details) {
// Sanitize outgoing headers as per user settings. // Sanitize outgoing headers as per user settings.
var onBeforeSendHeadersHandler = function(details) { var onBeforeSendHeadersHandler = function(details) {
var µm = µMatrix;
var µm = µMatrix,
µmuri = µm.URI,
requestURL = details.url,
requestScheme = µmuri.schemeFromURI(requestURL);
// Ignore non-network schemes // Ignore non-network schemes
if ( µm.URI.isNetworkScheme(details.url) === false ) {
return;
}
if ( µmuri.isNetworkScheme(requestScheme) === false ) { return; }
// Re-classify orphan HTTP requests as behind-the-scene requests. There is // Re-classify orphan HTTP requests as behind-the-scene requests. There is
// not much else which can be done, because there are URLs // not much else which can be done, because there are URLs
@ -162,8 +157,11 @@ var onBeforeSendHeadersHandler = function(details) {
// to scope on unknown scheme? Etc. // to scope on unknown scheme? Etc.
// https://github.com/gorhill/httpswitchboard/issues/191 // https://github.com/gorhill/httpswitchboard/issues/191
// https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275 // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275
var pageStore = µm.mustPageStoreFromTabId(details.tabId);
var tabId = pageStore.tabId;
var tabId = details.tabId,
pageStore = µm.mustPageStoreFromTabId(tabId),
requestType = requestTypeNormalizer[details.type] || 'other',
requestHeaders = details.requestHeaders,
headerIndex, headerValue;
// https://github.com/gorhill/httpswitchboard/issues/342 // https://github.com/gorhill/httpswitchboard/issues/342
// Is this hyperlink auditing? // Is this hyperlink auditing?
@ -186,13 +184,12 @@ var onBeforeSendHeadersHandler = function(details) {
// With hyperlink-auditing, removing header(s) is pointless, the whole // With hyperlink-auditing, removing header(s) is pointless, the whole
// request must be cancelled. // request must be cancelled.
var requestURL = details.url;
var requestType = requestTypeNormalizer[details.type] || 'other';
if ( requestType === 'ping' ) {
var linkAuditor = details.requestHeaders.getHeader('ping-to');
if ( linkAuditor !== '' ) {
headerIndex = headerIndexFromName('ping-to', requestHeaders);
if ( headerIndex !== -1 ) {
headerValue = requestHeaders[headerIndex].value;
if ( headerValue !== '' ) {
var block = µm.userSettings.processHyperlinkAuditing; var block = µm.userSettings.processHyperlinkAuditing;
pageStore.recordRequest('other', requestURL + '{Ping-To:' + linkAuditor + '}', block);
pageStore.recordRequest('other', requestURL + '{Ping-To:' + headerValue + '}', block);
µm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block); µm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block);
if ( block ) { if ( block ) {
µm.hyperlinkAuditingFoiledCounter += 1; µm.hyperlinkAuditingFoiledCounter += 1;
@ -203,43 +200,31 @@ var onBeforeSendHeadersHandler = function(details) {
// If we reach this point, request is not blocked, so what is left to do // If we reach this point, request is not blocked, so what is left to do
// is to sanitize headers. // is to sanitize headers.
var requestHostname = µm.URI.hostnameFromURI(requestURL);
if ( µm.mustBlock(pageStore.pageHostname, requestHostname, 'cookie') ) {
if ( details.requestHeaders.setHeader('cookie', '') ) {
µm.cookieHeaderFoiledCounter++;
}
}
if ( µm.tMatrix.evaluateSwitchZ('referrer-spoof', pageStore.pageHostname) ) {
foilRefererHeaders(µm, requestHostname, details);
}
};
var rootHostname = pageStore.pageHostname,
requestHostname = µmuri.hostnameFromURI(requestURL),
modified = false;
/******************************************************************************/
// Process `Cookie` header.
var foilRefererHeaders = function(µm, toHostname, details) {
var foiled = false;
var µmuri = µm.URI;
var scheme = '';
var toDomain = '';
var referer = details.requestHeaders.getHeader('referer');
if ( referer !== '' ) {
toDomain = toDomain || µmuri.domainFromHostname(toHostname);
if ( toDomain !== µmuri.domainFromURI(referer) ) {
scheme = scheme || µmuri.schemeFromURI(details.url);
//console.debug('foilRefererHeaders()> foiled referer for "%s"', details.url);
//console.debug('\treferrer "%s"', header.value);
// https://github.com/gorhill/httpswitchboard/issues/222#issuecomment-44828402
details.requestHeaders.setHeader(
'referer',
scheme + '://' + toHostname + '/'
);
foiled = true;
headerIndex = headerIndexFromName('cookie', requestHeaders);
if (
headerIndex !== -1 &&
µm.mustBlock(rootHostname, requestHostname, 'cookie')
) {
headerValue = requestHeaders[headerIndex].value;
requestHeaders.splice(headerIndex, 1);
modified = true;
µm.cookieHeaderFoiledCounter++;
if ( requestType === 'doc' ) {
µm.logger.writeOne(tabId, 'net', '', headerValue, 'COOKIE', true);
} }
} }
// Process `Referer` header.
// https://github.com/gorhill/httpswitchboard/issues/222#issuecomment-44828402
// https://github.com/gorhill/uMatrix/issues/320 // https://github.com/gorhill/uMatrix/issues/320
// http://tools.ietf.org/html/rfc6454#section-7.3 // http://tools.ietf.org/html/rfc6454#section-7.3
// "The user agent MAY include an Origin header field in any HTTP // "The user agent MAY include an Origin header field in any HTTP
@ -253,23 +238,29 @@ var foilRefererHeaders = function(µm, toHostname, details) {
// https://github.com/gorhill/uMatrix/issues/358 // https://github.com/gorhill/uMatrix/issues/358
// Do not spoof `Origin` header for the time being. This will be revisited. // Do not spoof `Origin` header for the time being. This will be revisited.
//var origin = details.requestHeaders.getHeader('origin');
//if ( origin !== '' && origin !== 'null' ) {
// toDomain = toDomain || µmuri.domainFromHostname(toHostname);
// if ( toDomain !== µmuri.domainFromURI(origin) ) {
// scheme = scheme || µmuri.schemeFromURI(details.url);
// //console.debug('foilRefererHeaders()> foiled origin for "%s"', details.url);
// //console.debug('\torigin "%s"', header.value);
// details.requestHeaders.setHeader(
// 'origin',
// scheme + '://' + toHostname
// );
// foiled = true;
// }
//}
if ( foiled ) {
µm.refererHeaderFoiledCounter++;
headerIndex = headerIndexFromName('referer', requestHeaders);
if ( headerIndex !== -1 ) {
headerValue = requestHeaders[headerIndex].value;
if (
headerValue !== '' &&
µm.tMatrix.evaluateSwitchZ('referrer-spoof', rootHostname)
) {
var toDomain = µmuri.domainFromHostname(requestHostname);
if ( toDomain !== '' && toDomain !== µmuri.domainFromURI(headerValue) ) {
var newValue = requestScheme + '://' + requestHostname + '/';
requestHeaders[headerIndex].value = newValue;
modified = true;
µm.refererHeaderFoiledCounter++;
if ( requestType === 'doc' ) {
µm.logger.writeOne(tabId, 'net', '', headerValue, 'REFERER', true);
µm.logger.writeOne(tabId, 'net', '', newValue, 'REFERER', false);
}
}
}
}
if ( modified ) {
return { requestHeaders: requestHeaders };
} }
}; };

Loading…
Cancel
Save