|
|
@ -13,64 +13,64 @@ var wappalyzer = (function() {
|
|
|
|
* Application class
|
|
|
|
* Application class
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
var Application = function(app, detected) {
|
|
|
|
var Application = function(app, detected) {
|
|
|
|
var self = this;
|
|
|
|
this.app = app;
|
|
|
|
|
|
|
|
this.confidence = {};
|
|
|
|
self.app = app;
|
|
|
|
this.confidenceTotal = 0;
|
|
|
|
self.confidence = {};
|
|
|
|
this.detected = Boolean(detected);
|
|
|
|
self.confidenceTotal = 0;
|
|
|
|
this.version = '';
|
|
|
|
self.detected = Boolean(detected);
|
|
|
|
this.versions = [];
|
|
|
|
self.version = '';
|
|
|
|
};
|
|
|
|
self.versions = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Application.prototype = {
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Calculate confidence total
|
|
|
|
* Calculate confidence total
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
self.getConfidence = function() {
|
|
|
|
getConfidence: function() {
|
|
|
|
var total = 0;
|
|
|
|
var total = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for ( id in self.confidence ) {
|
|
|
|
for ( id in this.confidence ) {
|
|
|
|
total += self.confidence[id];
|
|
|
|
total += this.confidence[id];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return self.confidenceTotal = Math.min(total, 100);
|
|
|
|
return this.confidenceTotal = Math.min(total, 100);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Resolve version number (find the longest version number that contains all shorter detected version numbers)
|
|
|
|
* Resolve version number (find the longest version number that contains all shorter detected version numbers)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
self.getVersion = function() {
|
|
|
|
getVersion: function() {
|
|
|
|
var next, resolved;
|
|
|
|
var i, next, resolved;
|
|
|
|
|
|
|
|
|
|
|
|
if ( !self.versions.length ) {
|
|
|
|
if ( !this.versions.length ) {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.versions.sort(function(a, b) {
|
|
|
|
this.versions.sort(function(a, b) {
|
|
|
|
return a.length > b.length ? 1 : ( a.length < b.length ? -1 : 0 );
|
|
|
|
return a.length > b.length ? 1 : ( a.length < b.length ? -1 : 0 );
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
resolved = self.versions[0];
|
|
|
|
resolved = this.versions[0];
|
|
|
|
|
|
|
|
|
|
|
|
for ( i in self.versions ) {
|
|
|
|
for ( i in this.versions ) {
|
|
|
|
next = parseInt(i) + 1;
|
|
|
|
next = parseInt(i) + 1;
|
|
|
|
|
|
|
|
|
|
|
|
if ( next < self.versions.length ) {
|
|
|
|
if ( next < this.versions.length ) {
|
|
|
|
if ( self.versions[next].indexOf(self.versions[i]) !== -1 ) {
|
|
|
|
if ( this.versions[next].indexOf(this.versions[i]) !== -1 ) {
|
|
|
|
resolved = self.versions[next];
|
|
|
|
resolved = this.versions[next];
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return self.version = resolved;
|
|
|
|
return this.version = resolved;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
self.setDetected = function(pattern, type, value, key) {
|
|
|
|
setDetected: function(pattern, type, value, key) {
|
|
|
|
self.detected = true;
|
|
|
|
this.detected = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Set confidence level
|
|
|
|
// Set confidence level
|
|
|
|
self.confidence[type + ' ' + ( key ? key + ' ' : '' ) + pattern.regex] = pattern.confidence ? pattern.confidence : 100;
|
|
|
|
this.confidence[type + ' ' + ( key ? key + ' ' : '' ) + pattern.regex] = pattern.confidence ? pattern.confidence : 100;
|
|
|
|
|
|
|
|
|
|
|
|
// Detect version number
|
|
|
|
// Detect version number
|
|
|
|
if ( pattern.version ) {
|
|
|
|
if ( pattern.version ) {
|
|
|
@ -100,14 +100,43 @@ var wappalyzer = (function() {
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if ( version ) {
|
|
|
|
if ( version ) {
|
|
|
|
self.versions.push(version);
|
|
|
|
this.versions.push(version);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
self.getVersion();
|
|
|
|
this.getVersion();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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.lastTime = new Date().getTime();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.timedOut = this.getDuration() > 1000;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getDuration: function() {
|
|
|
|
|
|
|
|
return new Date().getTime() - this.startTime;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Call driver functions
|
|
|
|
* Call driver functions
|
|
|
@ -224,10 +253,7 @@ var wappalyzer = (function() {
|
|
|
|
analyze: function(hostname, url, data) {
|
|
|
|
analyze: function(hostname, url, data) {
|
|
|
|
var
|
|
|
|
var
|
|
|
|
i, j, app, confidence, type, regexMeta, regexScript, match, content, meta, header, version,
|
|
|
|
i, j, app, confidence, type, regexMeta, regexScript, match, content, meta, header, version,
|
|
|
|
profiler = {
|
|
|
|
profiler = new Profiler(),
|
|
|
|
regexCount: 0,
|
|
|
|
|
|
|
|
startTime: new Date().getTime()
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
apps = {}
|
|
|
|
apps = {}
|
|
|
|
;
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
|
@ -246,13 +272,21 @@ var wappalyzer = (function() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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] ) {
|
|
|
|
switch ( type ) {
|
|
|
|
switch ( type ) {
|
|
|
|
case 'url':
|
|
|
|
case 'url':
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, pattern.regex);
|
|
|
|
|
|
|
|
|
|
|
|
if ( pattern.regex.test(url) ) {
|
|
|
|
if ( pattern.regex.test(url) ) {
|
|
|
|
apps[app].setDetected(pattern, type, url);
|
|
|
|
apps[app].setDetected(pattern, type, url);
|
|
|
@ -266,7 +300,7 @@ var wappalyzer = (function() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, pattern.regex);
|
|
|
|
|
|
|
|
|
|
|
|
if ( pattern.regex.test(data[type]) ) {
|
|
|
|
if ( pattern.regex.test(data[type]) ) {
|
|
|
|
apps[app].setDetected(pattern, type, data[type]);
|
|
|
|
apps[app].setDetected(pattern, type, data[type]);
|
|
|
@ -282,10 +316,10 @@ var wappalyzer = (function() {
|
|
|
|
regexScript = new RegExp('<script[^>]+src=("|\')([^"\']+)', 'ig');
|
|
|
|
regexScript = new RegExp('<script[^>]+src=("|\')([^"\']+)', 'ig');
|
|
|
|
|
|
|
|
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, pattern.regex);
|
|
|
|
|
|
|
|
|
|
|
|
while ( match = regexScript.exec(data.html) ) {
|
|
|
|
while ( match = regexScript.exec(data.html) ) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, pattern.regex);
|
|
|
|
|
|
|
|
|
|
|
|
if ( pattern.regex.test(match[2]) ) {
|
|
|
|
if ( pattern.regex.test(match[2]) ) {
|
|
|
|
apps[app].setDetected(pattern, type, match[2]);
|
|
|
|
apps[app].setDetected(pattern, type, match[2]);
|
|
|
@ -299,19 +333,17 @@ var wappalyzer = (function() {
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
profiler.regexCount ++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
regexMeta = /<meta[^>]+>/ig;
|
|
|
|
regexMeta = /<meta[^>]+>/ig;
|
|
|
|
|
|
|
|
|
|
|
|
while ( match = regexMeta.exec(data.html) ) {
|
|
|
|
while ( match = regexMeta.exec(data.html) ) {
|
|
|
|
for ( meta in w.apps[app][type] ) {
|
|
|
|
for ( meta in w.apps[app][type] ) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, regexMeta);
|
|
|
|
|
|
|
|
|
|
|
|
if ( new RegExp('name=["\']' + meta + '["\']', 'i').test(match) ) {
|
|
|
|
if ( new RegExp('name=["\']' + meta + '["\']', 'i').test(match) ) {
|
|
|
|
content = match.toString().match(/content=("|')([^"']+)("|')/i);
|
|
|
|
content = match.toString().match(/content=("|')([^"']+)("|')/i);
|
|
|
|
|
|
|
|
|
|
|
|
parse(w.apps[app].meta[meta]).map(function(pattern) {
|
|
|
|
parse(w.apps[app].meta[meta]).map(function(pattern) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, pattern.regex);
|
|
|
|
|
|
|
|
|
|
|
|
if ( content && content.length === 4 && pattern.regex.test(content[2]) ) {
|
|
|
|
if ( content && content.length === 4 && pattern.regex.test(content[2]) ) {
|
|
|
|
apps[app].setDetected(pattern, type, content[2], meta);
|
|
|
|
apps[app].setDetected(pattern, type, content[2], meta);
|
|
|
@ -329,7 +361,7 @@ var wappalyzer = (function() {
|
|
|
|
|
|
|
|
|
|
|
|
for ( header in w.apps[app].headers ) {
|
|
|
|
for ( header in w.apps[app].headers ) {
|
|
|
|
parse(w.apps[app][type][header]).map(function(pattern) {
|
|
|
|
parse(w.apps[app][type][header]).map(function(pattern) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, pattern.regex);
|
|
|
|
|
|
|
|
|
|
|
|
if ( typeof data[type][header] === 'string' && pattern.regex.test(data[type][header]) ) {
|
|
|
|
if ( typeof data[type][header] === 'string' && pattern.regex.test(data[type][header]) ) {
|
|
|
|
apps[app].setDetected(pattern, type, data[type][header], header);
|
|
|
|
apps[app].setDetected(pattern, type, data[type][header], header);
|
|
|
@ -345,7 +377,7 @@ var wappalyzer = (function() {
|
|
|
|
|
|
|
|
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
parse(w.apps[app][type]).map(function(pattern) {
|
|
|
|
for ( i in data[type] ) {
|
|
|
|
for ( i in data[type] ) {
|
|
|
|
profiler.regexCount ++;
|
|
|
|
profiler.checkPoint(app, type, pattern.regex);
|
|
|
|
|
|
|
|
|
|
|
|
if ( pattern.regex.test(data[type][i]) ) {
|
|
|
|
if ( pattern.regex.test(data[type][i]) ) {
|
|
|
|
apps[app].setDetected(pattern, type, data[type][i]);
|
|
|
|
apps[app].setDetected(pattern, type, data[type][i]);
|
|
|
@ -358,7 +390,8 @@ var wappalyzer = (function() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
w.log('Tested ' + profiler.regexCount + ' regular expressions in ' + ( ( ( new Date ).getTime() - profiler.startTime ) / 1000 ) + 's');
|
|
|
|
w.log('[ PROFILER ] Tested ' + profiler.regexCount + ' regular expressions in ' + ( profiler.getDuration() / 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 ) {
|
|
|
|