Refactoring

main
Elbert Alias 8 years ago
parent bf8202d8b9
commit 24e335f018

@ -102,33 +102,6 @@ var wappalyzer = (function() {
} }
}; };
var Profiler = function() {
this.regexCount = 0;
this.startTime = new Date().getTime();
this.lastTime = new Date().getTime();
this.slowest = { duration: null, app: '', type: '', pattern: '' };
this.timedOut = false;
};
Profiler.prototype = {
checkPoint: function(app, type, regex) {
var duration = new Date().getTime() - this.lastTime;
if ( !this.slowest.duration || duration > this.slowest.duration ) {
this.slowest.duration = duration;
this.slowest.app = app;
this.slowest.type = type;
this.slowest.regex = regex;
}
this.regexCount++;
this.lastTime = new Date().getTime();
this.timedOut = this.lastTime - this.startTime > w.driver.timeout;
}
};
/** /**
* Call driver functions * Call driver functions
*/ */
@ -149,42 +122,61 @@ var wappalyzer = (function() {
/** /**
* Parse apps.json patterns * Parse apps.json patterns
*/ */
var parse = function(patterns) { var parsePatterns = function(patterns) {
var var
attrs, key,
parsed = []; parsed = {};
// Convert single patterns to an array // Convert array to object containing array
if ( patterns instanceof Array ) {
patterns = { main: patterns }
}
// Convert string to object containing array containing string
if ( typeof patterns === 'string' ) { if ( typeof patterns === 'string' ) {
patterns = [ patterns ]; patterns = { main: [ patterns ] };
} }
patterns.forEach(function(pattern) { for ( key in patterns ) {
attrs = {}; parsed[key] = [];
pattern.split('\\;').forEach(function(attr, i) { // Convert string to array containing string
if ( i ) { if ( typeof patterns[key] === 'string' ) {
// Key value pairs patterns[key] = [ patterns[key] ];
attr = attr.split(':'); }
if ( attr.length > 1 ) { patterns[key].forEach(function(pattern) {
attrs[attr.shift()] = attr.join(':'); var attrs = {};
}
} else {
attrs.string = attr;
try { pattern.split('\\;').forEach(function(attr, i) {
attrs.regex = new RegExp(attr.replace('/', '\/'), 'i'); // Escape slashes in regular expression if ( i ) {
} catch (e) { // Key value pairs
attrs.regex = new RegExp(); attr = attr.split(':');
w.log(e + ': ' + attr, 'error'); if ( attr.length > 1 ) {
attrs[attr.shift()] = attr.join(':');
}
} else {
attrs.string = attr;
try {
attrs.regex = new RegExp(attr.replace('/', '\/'), 'i'); // Escape slashes in regular expression
} catch (e) {
attrs.regex = new RegExp();
w.log(e + ': ' + attr, 'error');
}
} }
} });
parsed[key].push(attrs);
}); });
}
parsed.push(attrs); // Convert back to array if the original pattern list was an array (or string)
}); if ( parsed.hasOwnProperty('main') ) {
parsed = parsed.main;
}
return parsed; return parsed;
}; };
@ -242,157 +234,87 @@ var wappalyzer = (function() {
*/ */
analyze: function(hostname, url, data) { analyze: function(hostname, url, data) {
var var
i, app, confidence, type, regexMeta, regexScript, match, content, meta, header, version, id, confirmMatch,
profiler = new Profiler(),
apps = {}, apps = {},
excludes = [], excludes = [],
checkImplies = true; checkImplies = true,
patterns = [];
w.log('w.analyze'); w.log('w.analyze');
// Remove hash from URL
data.url = url = url.split('#')[0];
if ( w.apps === undefined || w.categories === undefined ) { if ( w.apps === undefined || w.categories === undefined ) {
w.log('apps.json not loaded, check for syntax errors'); w.log('apps.json not loaded, check for syntax errors');
return; return;
} }
// Remove hash from URL
data.url = url = url.split('#')[0];
if ( typeof data.html !== 'string' ) {
data.html = '';
}
if ( w.detected[url] === undefined ) { if ( w.detected[url] === undefined ) {
w.detected[url] = {}; w.detected[url] = {};
} }
for ( app in w.apps ) { for ( app in w.apps ) {
// Exit loop after one second to prevent CPU hogging
// Remaining patterns will not be evaluated
if ( profiler.timedOut ) {
w.log('Timeout, exiting loop');
break;
}
apps[app] = w.detected[url] && w.detected[url][app] ? w.detected[url][app] : new Application(app); apps[app] = w.detected[url] && w.detected[url][app] ? w.detected[url][app] : new Application(app);
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
patterns = parsePatterns(w.apps[app][type]);
if ( !patterns ) {
continue;
}
confirmMatch = function(pattern, value, key) {
apps[app].setDetected(pattern, type, value, key);
}
switch ( type ) { switch ( type ) {
case 'url': case 'url':
parse(w.apps[app][type]).forEach(function(pattern) { if ( url ) {
if ( pattern.regex.test(url) ) { w.analyzeUrl(patterns, url, confirmMatch);
apps[app].setDetected(pattern, type, url); }
}
profiler.checkPoint(app, type, pattern.regex);
});
break; break;
case 'html': case 'html':
if ( typeof data[type] !== 'string' || !data.html ) { if ( data.html ) {
break; w.analyzeHtml(patterns, data.html, confirmMatch);
} }
parse(w.apps[app][type]).forEach(function(pattern) {
if ( pattern.regex.test(data[type]) ) {
apps[app].setDetected(pattern, type, data[type]);
}
profiler.checkPoint(app, type, pattern.regex);
});
break; break;
case 'script': case 'script':
if ( typeof data.html !== 'string' || !data.html ) { if ( data.html ) {
break; w.analyzeScript(patterns, data.html, confirmMatch);
} }
regexScript = new RegExp('<script[^>]+src=("|\')([^"\']+)', 'ig');
parse(w.apps[app][type]).forEach(function(pattern) {
while ( match = regexScript.exec(data.html) ) {
if ( pattern.regex.test(match[2]) ) {
apps[app].setDetected(pattern, type, match[2]);
}
}
profiler.checkPoint(app, type, pattern.regex);
});
break; break;
case 'meta': case 'meta':
if ( typeof data.html !== 'string' || !data.html ) { if ( data.html ) {
break; w.analyzeMeta(patterns, data.html, confirmMatch);
}
regexMeta = /<meta[^>]+>/ig;
while ( match = regexMeta.exec(data.html) ) {
for ( meta in w.apps[app][type] ) {
profiler.checkPoint(app, type, regexMeta);
if ( new RegExp('(name|property)=["\']' + meta + '["\']', 'i').test(match) ) {
content = match.toString().match(/content=("|')([^"']+)("|')/i);
parse(w.apps[app].meta[meta]).forEach(function(pattern) {
if ( content && content.length === 4 && pattern.regex.test(content[2]) ) {
apps[app].setDetected(pattern, type, content[2], meta);
}
profiler.checkPoint(app, type, pattern.regex);
});
}
}
} }
break; break;
case 'headers': case 'headers':
if ( typeof data[type] !== 'object' || !data[type] ) { if ( data.hasOwnProperty('headers') && data.headers ) {
break; w.analyzeHeaders(patterns, data.headers, confirmMatch);
}
for ( header in w.apps[app].headers ) {
parse(w.apps[app][type][header]).forEach(function(pattern) {
if ( data[type][header.toLowerCase()] instanceof Array ) {
data[type][header.toLowerCase()].forEach(function(el) {
if ( typeof el === 'string' && pattern.regex.test(el) ) {
apps[app].setDetected(pattern, type, data[type][header.toLowerCase()], header);
}
});
} else {
if ( typeof data[type][header.toLowerCase()] === 'string' && pattern.regex.test(data[type][header.toLowerCase()]) ) {
apps[app].setDetected(pattern, type, data[type][header.toLowerCase()], header);
}
}
profiler.checkPoint(app, type, pattern.regex);
});
} }
break; break;
case 'env': case 'env':
if ( typeof data[type] !== 'object' || !data[type] ) { if ( data.hasOwnProperty('env') && data.env ) {
break; w.analyzeEnv(patterns, data.env, confirmMatch);
} }
parse(w.apps[app][type]).forEach(function(pattern) {
for ( i in data[type] ) {
if ( pattern.regex.test(data[type][i]) ) {
apps[app].setDetected(pattern, type, data[type][i]);
}
}
profiler.checkPoint(app, type, pattern.regex);
});
break; break;
default: default:
} }
} }
} }
w.log('[ profiler ] Tested ' + profiler.regexCount + ' regular expressions in ' + ( (new Date().getTime() - profiler.startTime) / 1000 ) + 's');
w.log('[ profiler ] Slowest pattern took ' + ( profiler.slowest.duration / 1000 ) + 's: ' + profiler.slowest.app + ' | ' + profiler.slowest.type + ' | ' + profiler.slowest.regex);
for ( app in apps ) { for ( app in apps ) {
if ( !apps[app].detected ) { if ( !apps[app].detected ) {
delete apps[app]; delete apps[app];
@ -434,7 +356,7 @@ var wappalyzer = (function() {
} }
w.apps[app].implies.forEach(function(implied) { w.apps[app].implies.forEach(function(implied) {
implied = parse(implied)[0]; implied = parsePatterns(implied)[0];
if ( !w.apps[implied.string] ) { if ( !w.apps[implied.string] ) {
w.log('Implied application ' + implied.string + ' does not exist', 'warn'); w.log('Implied application ' + implied.string + ' does not exist', 'warn');
@ -475,11 +397,16 @@ var wappalyzer = (function() {
// Per hostname // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|ing)?|test(ing)?|demo(shop)?|admin|google|cache)\.|\/admin|\.local)/.test(url) ) { if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|ing)?|test(ing)?|demo(shop)?|admin|google|cache)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) { if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; w.ping.hostnames[hostname] = {
applications: {},
meta: {}
};
} }
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) { if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = { hits: 0 }; w.ping.hostnames[hostname].applications[app] = {
hits: 0
};
} }
w.ping.hostnames[hostname].applications[app].hits ++; w.ping.hostnames[hostname].applications[app].hits ++;
@ -509,6 +436,98 @@ var wappalyzer = (function() {
} }
driver('displayApps'); driver('displayApps');
},
/**
* Analyze URL
*/
analyzeUrl: function(patterns, url, confirmMatch) {
patterns.forEach(function(pattern) {
if ( pattern.regex.test(url) ) {
confirmMatch(pattern, url);
}
});
},
/**
* Analyze HTML
*/
analyzeHtml: function(patterns, html, confirmMatch) {
patterns.forEach(function(pattern) {
if ( pattern.regex.test(html) ) {
confirmMatch(pattern, html);
}
});
},
/**
* Analyze script tag
*/
analyzeScript: function(patterns, html, confirmMatch) {
var regex = new RegExp('<script[^>]+src=("|\')([^"\']+)', 'ig');
patterns.forEach(function(pattern) {
var match;
while ( match = regex.exec(html) ) {
if ( pattern.regex.test(match[2]) ) {
confirmMatch(pattern, match[2]);
}
}
});
},
/**
* Analyze meta tag
*/
analyzeMeta: function(patterns, html, confirmMatch) {
var
meta,
regex = /<meta[^>]+>/ig;
while ( match = regex.exec(html) ) {
for ( meta in patterns ) {
if ( new RegExp('(name|property)=["\']' + meta + '["\']', 'i').test(match) ) {
content = match.toString().match(/content=("|')([^"']+)("|')/i);
patterns[meta].forEach(function(pattern) {
if ( content && content.length === 4 && pattern.regex.test(content[2]) ) {
confirmMatch(pattern, content[2], meta);
}
});
}
}
}
},
/**
* analyze response headers
*/
analyzeHeaders: function(patterns, headers, confirmMatch) {
for ( header in patterns ) {
patterns[header].forEach(function(pattern) {
header = header.toLowerCase();
if ( headers.hasOwnProperty(header) && pattern.regex.test(headers[header]) ) {
confirmMatch(pattern, headers[header], header);
}
});
}
},
/**
* Analyze environment variables
*/
analyzeEnv: function(patterns, envs, confirmMatch) {
var env;
patterns.forEach(function(pattern) {
for ( env in envs ) {
if ( pattern.regex.test(envs[env]) ) {
confirmMatch(pattern, envs[env]);
}
}
});
} }
}; };

Loading…
Cancel
Save