Fix header detection, performance improvements in WebExtension driver

main
Elbert Alias 7 years ago
parent bf47697939
commit c9b0ab7aa2

@ -26,15 +26,19 @@ if ( typeof browser !== 'undefined' && typeof document.body !== 'undefined' ) {
const script = document.createElement('script'); const script = document.createElement('script');
script.onload = () => { script.onload = () => {
addEventListener('message', event => { const onMessage = event => {
if ( event.data.id !== 'js' ) { if ( event.data.id !== 'js' ) {
return; return;
} }
document.body.removeChild(script); removeEventListener('message', onMessage);
sendMessage('analyze', { js: event.data.js }); sendMessage('analyze', { js: event.data.js });
}, true);
script.remove();
};
addEventListener('message', onMessage);
sendMessage('get_js_patterns', {}, response => { sendMessage('get_js_patterns', {}, response => {
if ( response ) { if ( response ) {
@ -61,3 +65,7 @@ function sendMessage(id, subject, callback) {
source: 'content.js' source: 'content.js'
}, callback || ( () => {} )); }, callback || ( () => {} ));
} }
// https://stackoverflow.com/a/44774834
// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/executeScript#Return_value
undefined;

@ -8,7 +8,6 @@
const wappalyzer = new Wappalyzer(); const wappalyzer = new Wappalyzer();
var tabCache = {}; var tabCache = {};
var headersCache = {};
var categoryOrder = []; var categoryOrder = [];
var options = {}; var options = {};
var robotsTxtQueue = {}; var robotsTxtQueue = {};
@ -118,56 +117,51 @@ getOption('version')
getOption('dynamicIcon', true); getOption('dynamicIcon', true);
getOption('pinnedCategory'); getOption('pinnedCategory');
// Run content script // Run content script on all tabs
var callback = tabs => { browser.tabs.query({ url: [ 'http://*/*', 'https://*/*' ] })
.then(tabs => {
tabs.forEach(tab => { tabs.forEach(tab => {
if ( tab.url.match(/^https?:\/\//) ) {
browser.tabs.executeScript(tab.id, { browser.tabs.executeScript(tab.id, {
file: 'js/content.js' file: '../js/content.js'
}); });
}
}) })
}; })
browser.tabs.query({})
.then(callback)
.catch(error => wappalyzer.log(error, 'driver', 'error')); .catch(error => wappalyzer.log(error, 'driver', 'error'));
// Capture response headers // Capture response headers
browser.webRequest.onCompleted.addListener(request => { browser.webRequest.onCompleted.addListener(request => {
var responseHeaders = {}; const headers = {};
if ( request.responseHeaders ) { if ( request.responseHeaders ) {
var url = wappalyzer.parseUrl(request.url); const url = wappalyzer.parseUrl(request.url);
request.responseHeaders.forEach(function(header) { browser.tabs.query({ url: [ url.canonical ] })
if ( !responseHeaders[header.name.toLowerCase()] ) { .then(tabs => {
responseHeaders[header.name.toLowerCase()] = [] const tab = tabs[0] || null;
}
responseHeaders[header.name.toLowerCase()].push(header.value || '' + header.binaryValue);
});
if ( headersCache.length > 50 ) { if ( tab ) {
headersCache = {}; request.responseHeaders.forEach(header => {
} const name = header.name.toLowerCase();
if ( /text\/html/.test(responseHeaders['content-type'][0]) ) { headers[name] = headers[name] || [];
if ( headersCache[url.canonical] === undefined ) {
headersCache[url.canonical] = {};
}
Object.keys(responseHeaders).forEach(header => { headers[name].push(header.value || header.binaryValue.toString());
headersCache[url.canonical][header] = responseHeaders[header].slice();
}); });
if ( headers['content-type'] && /\/x?html/.test(headers['content-type'][0]) ) {
wappalyzer.analyze(url, { headers }, { tab });
} }
} }
})
.catch(error => wappalyzer.log(error, 'driver', 'error'));
}
}, { urls: [ 'http://*/*', 'https://*/*' ], types: [ 'main_frame' ] }, [ 'responseHeaders' ]); }, { urls: [ 'http://*/*', 'https://*/*' ], types: [ 'main_frame' ] }, [ 'responseHeaders' ]);
// Listen for messages // Listen for messages
( chrome || browser ).runtime.onMessage.addListener((message, sender, sendResponse) => { ( chrome || browser ).runtime.onMessage.addListener((message, sender, sendResponse) => {
if ( typeof message.id != 'undefined' ) { if ( typeof message.id != 'undefined' ) {
if ( message.id !== 'log' ) { if ( message.id !== 'log' ) {
wappalyzer.log('Message received' + ( message.source ? ' from ' + message.source : '' ) + ': ' + message.id, 'driver'); wappalyzer.log('Message' + ( message.source ? ' from ' + message.source : '' ) + ': ' + message.id, 'driver');
} }
var url = wappalyzer.parseUrl(sender.tab ? sender.tab.url : ''); var url = wappalyzer.parseUrl(sender.tab ? sender.tab.url : '');
@ -175,24 +169,16 @@ browser.webRequest.onCompleted.addListener(request => {
switch ( message.id ) { switch ( message.id ) {
case 'log': case 'log':
wappalyzer.log(message.message, message.source); wappalyzer.log(message.subject, message.source);
break; break;
case 'init': case 'init':
browser.cookies.getAll({ domain: '.' + url.hostname }) browser.cookies.getAll({ domain: '.' + url.hostname })
.then(cookies => wappalyzer.analyze(url, { cookies }, { .then(cookies => wappalyzer.analyze(url, { cookies }, { tab: sender.tab }));
tab: sender.tab
}));
break; break;
case 'analyze': case 'analyze':
if ( headersCache[url.canonical] !== undefined ) { wappalyzer.analyze(url, message.subject, { tab: sender.tab });
message.subject.headers = headersCache[url.canonical];
}
wappalyzer.analyze(url, message.subject, {
tab: sender.tab
});
break; break;
case 'ad_log': case 'ad_log':
@ -242,7 +228,13 @@ wappalyzer.driver.log = (message, source, type) => {
wappalyzer.driver.displayApps = (detected, meta, context) => { wappalyzer.driver.displayApps = (detected, meta, context) => {
var tab = context.tab; var tab = context.tab;
tabCache[tab.id] = tabCache[tab.id] || { detected: [] }; if ( tab === undefined ) {
return;
}
tabCache[tab.id] = tabCache[tab.id] || {
detected: []
};
tabCache[tab.id].detected = detected; tabCache[tab.id].detected = detected;
@ -295,8 +287,6 @@ wappalyzer.driver.displayApps = (detected, meta, context) => {
*/ */
wappalyzer.driver.getRobotsTxt = (host, secure = false) => { wappalyzer.driver.getRobotsTxt = (host, secure = false) => {
if ( robotsTxtQueue.hasOwnProperty(host) ) { if ( robotsTxtQueue.hasOwnProperty(host) ) {
wappalyzer.log('robotTxt fetch already in queue');
return robotsTxtQueue[host]; return robotsTxtQueue[host];
} }

@ -1,10 +1,14 @@
(function() { (() => {
try { try {
addEventListener('message', (event => { addEventListener('message', onMessage);
function onMessage(event) {
if ( event.data.id !== 'patterns' ) { if ( event.data.id !== 'patterns' ) {
return; return;
} }
removeEventListener('message', onMessage);
const patterns = event.data.patterns || {}; const patterns = event.data.patterns || {};
const js = {}; const js = {};
@ -30,13 +34,9 @@
} }
postMessage({ id: 'js', js }, '*'); postMessage({ id: 'js', js }, '*');
}), false);
} catch(e) {
// Fail quietly
} }
function detectJs(chain) { function detectJs(chain) {
try {
const properties = chain.split('.'); const properties = chain.split('.');
var value = properties.length ? window : null; var value = properties.length ? window : null;
@ -54,8 +54,8 @@
} }
return typeof value === 'string' || typeof value === 'number' ? value : !!value; return typeof value === 'string' || typeof value === 'number' ? value : !!value;
}
} catch(e) { } catch(e) {
// Fail quietly // Fail quietly
} }
} })();
}());

@ -42,22 +42,28 @@ class Wappalyzer {
} }
analyze(url, data, context) { analyze(url, data, context) {
const startTime = new Date();
const promises = []; const promises = [];
var apps = {}; var apps = {};
if ( typeof data.html !== 'string' ) {
data.html = '';
}
if ( this.detected[url.canonical] === undefined ) { if ( this.detected[url.canonical] === undefined ) {
this.detected[url.canonical] = {}; this.detected[url.canonical] = {};
} }
// Additional information // Additional information
var language = null;
if ( data.html ) {
if ( typeof data.html !== 'string' ) {
data.html = '';
}
const matches = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); const matches = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
const language = matches && matches.length ? matches[1] : null; language = matches && matches.length ? matches[1] : null;
}
Object.keys(this.apps).forEach(appName => { Object.keys(this.apps).forEach(appName => {
apps[appName] = this.detected[url.canonical] && this.detected[url.canonical][appName] ? this.detected[url.canonical][appName] : new Application(appName, this.apps[appName]); apps[appName] = this.detected[url.canonical] && this.detected[url.canonical][appName] ? this.detected[url.canonical][appName] : new Application(appName, this.apps[appName]);
@ -86,7 +92,7 @@ class Wappalyzer {
if ( data.env ) { if ( data.env ) {
promises.push(this.analyzeEnv(app, data.env)); promises.push(this.analyzeEnv(app, data.env));
} }
}) });
if ( data.js ) { if ( data.js ) {
Object.keys(data.js).forEach(appName => { Object.keys(data.js).forEach(appName => {
@ -111,8 +117,10 @@ class Wappalyzer {
this.cacheDetectedApps(apps, url.canonical); this.cacheDetectedApps(apps, url.canonical);
this.trackDetectedApps(apps, url, language); this.trackDetectedApps(apps, url, language);
this.log('Processing ' + Object.keys(data).join(', ') + ' took ' + (( new Date() - startTime ) / 1000).toFixed(2) + 's (' + url.hostname + ')', 'core');
if ( Object.keys(apps).length ) { if ( Object.keys(apps).length ) {
this.log(Object.keys(apps).length + ' apps detected: ' + Object.keys(apps).join(', ') + ' on ' + url.canonical, 'core'); this.log('Identified ' + Object.keys(apps).join(', ') + ' (' + url.hostname + ')', 'core');
} }
this.driver.displayApps(this.detected[url.canonical], { language }, context); this.driver.displayApps(this.detected[url.canonical], { language }, context);
@ -492,9 +500,9 @@ class Wappalyzer {
const promises = []; const promises = [];
Object.keys(patterns).forEach(headerName => { Object.keys(patterns).forEach(headerName => {
promises.push(this.asyncForEach(patterns[headerName], pattern => {
headerName = headerName.toLowerCase(); headerName = headerName.toLowerCase();
promises.push(this.asyncForEach(patterns[headerName], pattern => {
if ( headerName in headers ) { if ( headerName in headers ) {
headers[headerName].forEach(headerValue => { headers[headerName].forEach(headerValue => {
if ( pattern.regex.test(headerValue) ) { if ( pattern.regex.test(headerValue) ) {

Loading…
Cancel
Save