Added confidence level

main
Elbert Alias 12 years ago
parent ecf1d1c7b6
commit 7877da87c1

@ -30,8 +30,8 @@ var wappalyzer = (function() {
var w = { var w = {
apps: null, apps: null,
cats: null, cats: null,
ping: {}, ping: { hostnames: {} },
detected: [], detected: {},
config: { config: {
environment: 'dev', // dev | live environment: 'dev', // dev | live
@ -103,11 +103,11 @@ var wappalyzer = (function() {
} }
if ( typeof w.detected[url] === 'undefined' ) { if ( typeof w.detected[url] === 'undefined' ) {
w.detected[url] = []; w.detected[url] = {};
} }
var var
i, app, type, regex, regexMeta, regexScript, match, content, meta, header, i, app, confidence, type, regex, regexMeta, regexScript, match, content, meta, header,
profiler = { profiler = {
regexCount: 0, regexCount: 0,
startTime: new Date().getTime() startTime: new Date().getTime()
@ -118,11 +118,15 @@ var wappalyzer = (function() {
appLoop: appLoop:
for ( app in w.apps ) { for ( app in w.apps ) {
// Skip if the app has already been detected // Skip if the app has already been detected
if ( w.detected[url].indexOf(app) !== -1 || apps.indexOf(app) !== -1 ) { if ( w.detected[url].hasOwnProperty(app) || apps.indexOf(app) !== -1 ) {
continue; continue;
} }
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
confidence = {};
confidence[type] = w.apps[app].hasOwnProperty('confidence') && w.apps[app].confidence.hasOwnProperty(type) ? w.apps[app].confidence[type] : 100;
switch ( type ) { switch ( type ) {
case 'url': case 'url':
regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i'); regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i');
@ -130,7 +134,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(url) ) { if ( regex.test(url) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -146,7 +150,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type]) ) { if ( regex.test(data[type]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -166,7 +170,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(match[2]) ) { if ( regex.test(match[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -194,7 +198,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( content && content.length === 4 && regex.test(content[2]) ) { if ( content && content.length === 4 && regex.test(content[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -214,7 +218,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) { if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -232,7 +236,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type][i]) ) { if ( regex.test(data[type][i]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -250,9 +254,11 @@ var wappalyzer = (function() {
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
for ( j in apps ) { for ( j in apps ) {
if ( w.apps[apps[j]] && w.apps[apps[j]].implies ) { app = apps[j].app;
for ( k in w.apps[apps[j]].implies ) {
implied = w.apps[apps[j]].implies[k]; if ( w.apps[app] && w.apps[app].implies ) {
for ( k in w.apps[app].implies ) {
implied = w.apps[app].implies[k];
if ( !w.apps[implied] ) { if ( !w.apps[implied] ) {
w.log('Implied application ' + implied + ' does not exist'); w.log('Implied application ' + implied + ' does not exist');
@ -260,45 +266,72 @@ var wappalyzer = (function() {
continue; continue;
} }
if ( w.detected[url].indexOf(implied) === -1 && apps.indexOf(implied) === -1 ) { if ( !w.detected[url].hasOwnProperty(implied) && apps.indexOf(implied) === -1 ) {
apps.push(implied); apps.push({ app: implied, confidence: apps[j].confidence });
} }
} }
} }
} }
} }
w.log(apps.length + ' apps detected: ' + apps.join(', ') + ' on ' + url); w.log(apps.reduce(function(reduced, app) {
var i;
for ( i in app.confidence ) {
return app.app + ' (' + app.confidence[i] + '%) ';
}
}, Object.keys(apps).length + ' apps detected: ') + 'on ' + url);
// Keep history of detected apps // Keep history of detected apps
var i, app, regex, regexMeta, match; var i, app, regex, regexMeta, match;
for ( i in apps ) { for ( i in apps ) {
app = apps[i]; app = apps[i].app;
// Per hostname confidence = apps[i].confidence;
if ( /^[a-z0-9._\-]+\.[a-z]+/.test(hostname) && !/((local|dev|development|stage|staging|test|testing|demo|admin)\.|\/admin|\.local)/.test(url) ) {
if ( typeof w.ping.hostnames === 'undefined' ) {
w.ping.hostnames = {};
}
if ( typeof w.ping.hostnames[hostname] === 'undefined' ) { // Per URL
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; if ( !w.detected[url].hasOwnProperty(app)) {
} w.detected[url][app] = {};
}
if ( typeof w.ping.hostnames[hostname].applications[app] === 'undefined' ) { for ( type in confidence ) {
w.ping.hostnames[hostname].applications[app] = 1; w.detected[url][app][type] = confidence[type];
} }
w.ping.hostnames[hostname].applications[app] ++; // Calculate confidence total
w.detected[url][app].total = 0;
for ( type in w.detected[url][app] ) {
if ( type !== 'total' ) {
w.detected[url][app].total += w.detected[url][app][type];
w.detected[url][app].total = Math.min(w.detected[url][app].total, 100);
}
} }
// Per URL if ( w.detected[url][app].total >= 100 ) {
if ( w.detected[url].indexOf(app) === -1 ) { w.detected[url].push(app); } // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|staging)?|test(ing)?|demo(shop)?|admin)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} };
}
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = 1;
}
w.ping.hostnames[hostname].applications[app] ++;
} else {
w.log('Ignoring hostname "' + hostname + '"');
}
}
} }
w.log(JSON.stringify(w.detected));
// Additional information // Additional information
if ( typeof w.ping.hostnames !== 'undefined' && typeof w.ping.hostnames[hostname] !== 'undefined' ) { if ( w.ping.hostnames.hasOwnProperty(hostname) ) {
if ( typeof data.html === 'string' && data.html ) { if ( typeof data.html === 'string' && data.html ) {
match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
@ -322,7 +355,7 @@ var wappalyzer = (function() {
w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname])); w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname]));
} }
if ( typeof w.ping.hostnames === 'object' && Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); } if ( Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); }
apps = null; apps = null;
data = null; data = null;

@ -1380,8 +1380,9 @@
"cats": [ 18 ], "cats": [ 18 ],
"script": "/assets/application-[a-z\\d]{32}/\\.js", "script": "/assets/application-[a-z\\d]{32}/\\.js",
"meta": { "csrf-param": "authenticity_token" }, "meta": { "csrf-param": "authenticity_token" },
"headers": { "Server": "(mod_rails|mod_rack|Phusion\\.Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion\\.Passenger)" }, "headers": { "Server": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)" },
"implies": [ "Ruby" ] "implies": [ "Ruby" ],
"confidence": { "script": 50, "meta": 50, "headers": 50 }
}, },
"S.Builder": { "S.Builder": {
"cats": [ 1 ], "cats": [ 1 ],
@ -1581,7 +1582,7 @@
"Tumblr": { "Tumblr": {
"cats": [ 11 ], "cats": [ 11 ],
"html": "<iframe src=\\\"[^>]+tumblr\\.com", "html": "<iframe src=\\\"[^>]+tumblr\\.com",
"url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/" "url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/",
"headers": { "X-Tumblr-Usec": ".*" } "headers": { "X-Tumblr-Usec": ".*" }
}, },
"Twilight CMS": { "Twilight CMS": {

@ -1380,8 +1380,9 @@
"cats": [ 18 ], "cats": [ 18 ],
"script": "/assets/application-[a-z\\d]{32}/\\.js", "script": "/assets/application-[a-z\\d]{32}/\\.js",
"meta": { "csrf-param": "authenticity_token" }, "meta": { "csrf-param": "authenticity_token" },
"headers": { "Server": "(mod_rails|mod_rack|Phusion\\.Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion\\.Passenger)" }, "headers": { "Server": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)" },
"implies": [ "Ruby" ] "implies": [ "Ruby" ],
"confidence": { "script": 50, "meta": 50, "headers": 50 }
}, },
"S.Builder": { "S.Builder": {
"cats": [ 1 ], "cats": [ 1 ],
@ -1581,7 +1582,7 @@
"Tumblr": { "Tumblr": {
"cats": [ 11 ], "cats": [ 11 ],
"html": "<iframe src=\\\"[^>]+tumblr\\.com", "html": "<iframe src=\\\"[^>]+tumblr\\.com",
"url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/" "url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/",
"headers": { "X-Tumblr-Usec": ".*" } "headers": { "X-Tumblr-Usec": ".*" }
}, },
"Twilight CMS": { "Twilight CMS": {

@ -125,7 +125,7 @@
* Display apps * Display apps
*/ */
displayApps: function() { displayApps: function() {
var count = w.detected[tab.url].length.toString(); var count = Object.keys(w.detected[tab.url]).length.toString();
if ( tabCache[tab.id] == null ) { if ( tabCache[tab.id] == null ) {
tabCache[tab.id] = { tabCache[tab.id] = {
@ -140,11 +140,11 @@
if ( count > 0 ) { if ( count > 0 ) {
// Find the main application to display // Find the main application to display
var i, appName, found = false; var i, appName, confidence, found = false;
w.driver.categoryOrder.map(function(match) { w.driver.categoryOrder.map(function(match) {
for ( i in w.detected[tab.url] ) { for ( appName in w.detected[tab.url] ) {
appName = w.detected[tab.url][i]; confidence = w.detected[tab.url][appName].total;
w.apps[appName].cats.map(function(cat) { w.apps[appName].cats.map(function(cat) {
if ( cat == match && !found ) { if ( cat == match && !found ) {

@ -42,6 +42,8 @@ document.addEventListener('DOMContentLoaded', function() {
}, },
displayApps: function() { displayApps: function() {
var appName, confidence;
chrome.tabs.getSelected(null, function(tab) { chrome.tabs.getSelected(null, function(tab) {
chrome.extension.sendRequest({ id: 'get_apps', tab: tab }, function(response) { chrome.extension.sendRequest({ id: 'get_apps', tab: tab }, function(response) {
if ( response.tabCache.analyzed.indexOf('headers') > 0 ) { if ( response.tabCache.analyzed.indexOf('headers') > 0 ) {
@ -55,12 +57,14 @@ document.addEventListener('DOMContentLoaded', function() {
if ( response.tabCache.count > 0 ) { if ( response.tabCache.count > 0 ) {
detectedApps.innerHTML = ''; detectedApps.innerHTML = '';
response.tabCache.appsDetected.map(function(appName) { for ( appName in response.tabCache.appsDetected ) {
confidence = response.tabCache.appsDetected[appName].total;
html = html =
'<div class="detected-app">' + '<div class="detected-app">' +
'<a target="_blank" href="http://wappalyzer.com/applications/' + appName.toLowerCase().replace(/ /g, '-').replace(/[^\w-]/g, '') + '?utm_source=chrome&utm_medium=popup&utm_campaign=extensions">' + '<a target="_blank" href="http://wappalyzer.com/applications/' + appName.toLowerCase().replace(/ /g, '-').replace(/[^\w-]/g, '') + '?utm_source=chrome&utm_medium=popup&utm_campaign=extensions">' +
'<img src="images/icons/' + appName + '.png"/>' + '<img src="images/icons/' + appName + '.png"/>' +
'<span class="label">' + appName + '</span>' + '<span class="label">' + appName + ( confidence < 100 ? ' (' + confidence + '% sure)' : '' ) + '</span>' +
'</a>'; '</a>';
response.apps[appName].cats.map(function(cat) { response.apps[appName].cats.map(function(cat) {
@ -75,7 +79,7 @@ document.addEventListener('DOMContentLoaded', function() {
'</div>'; '</div>';
detectedApps.innerHTML = detectedApps.innerHTML + html; detectedApps.innerHTML = detectedApps.innerHTML + html;
}); }
} }
}); });
}); });

@ -30,8 +30,8 @@ var wappalyzer = (function() {
var w = { var w = {
apps: null, apps: null,
cats: null, cats: null,
ping: {}, ping: { hostnames: {} },
detected: [], detected: {},
config: { config: {
environment: 'dev', // dev | live environment: 'dev', // dev | live
@ -103,11 +103,11 @@ var wappalyzer = (function() {
} }
if ( typeof w.detected[url] === 'undefined' ) { if ( typeof w.detected[url] === 'undefined' ) {
w.detected[url] = []; w.detected[url] = {};
} }
var var
i, app, type, regex, regexMeta, regexScript, match, content, meta, header, i, app, confidence, type, regex, regexMeta, regexScript, match, content, meta, header,
profiler = { profiler = {
regexCount: 0, regexCount: 0,
startTime: new Date().getTime() startTime: new Date().getTime()
@ -118,11 +118,15 @@ var wappalyzer = (function() {
appLoop: appLoop:
for ( app in w.apps ) { for ( app in w.apps ) {
// Skip if the app has already been detected // Skip if the app has already been detected
if ( w.detected[url].indexOf(app) !== -1 || apps.indexOf(app) !== -1 ) { if ( w.detected[url].hasOwnProperty(app) || apps.indexOf(app) !== -1 ) {
continue; continue;
} }
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
confidence = {};
confidence[type] = w.apps[app].hasOwnProperty('confidence') && w.apps[app].confidence.hasOwnProperty(type) ? w.apps[app].confidence[type] : 100;
switch ( type ) { switch ( type ) {
case 'url': case 'url':
regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i'); regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i');
@ -130,7 +134,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(url) ) { if ( regex.test(url) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -146,7 +150,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type]) ) { if ( regex.test(data[type]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -166,7 +170,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(match[2]) ) { if ( regex.test(match[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -194,7 +198,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( content && content.length === 4 && regex.test(content[2]) ) { if ( content && content.length === 4 && regex.test(content[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -214,7 +218,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) { if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -232,7 +236,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type][i]) ) { if ( regex.test(data[type][i]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -250,9 +254,11 @@ var wappalyzer = (function() {
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
for ( j in apps ) { for ( j in apps ) {
if ( w.apps[apps[j]] && w.apps[apps[j]].implies ) { app = apps[j].app;
for ( k in w.apps[apps[j]].implies ) {
implied = w.apps[apps[j]].implies[k]; if ( w.apps[app] && w.apps[app].implies ) {
for ( k in w.apps[app].implies ) {
implied = w.apps[app].implies[k];
if ( !w.apps[implied] ) { if ( !w.apps[implied] ) {
w.log('Implied application ' + implied + ' does not exist'); w.log('Implied application ' + implied + ' does not exist');
@ -260,45 +266,72 @@ var wappalyzer = (function() {
continue; continue;
} }
if ( w.detected[url].indexOf(implied) === -1 && apps.indexOf(implied) === -1 ) { if ( !w.detected[url].hasOwnProperty(implied) && apps.indexOf(implied) === -1 ) {
apps.push(implied); apps.push({ app: implied, confidence: apps[j].confidence });
} }
} }
} }
} }
} }
w.log(apps.length + ' apps detected: ' + apps.join(', ') + ' on ' + url); w.log(apps.reduce(function(reduced, app) {
var i;
for ( i in app.confidence ) {
return app.app + ' (' + app.confidence[i] + '%) ';
}
}, Object.keys(apps).length + ' apps detected: ') + 'on ' + url);
// Keep history of detected apps // Keep history of detected apps
var i, app, regex, regexMeta, match; var i, app, regex, regexMeta, match;
for ( i in apps ) { for ( i in apps ) {
app = apps[i]; app = apps[i].app;
// Per hostname confidence = apps[i].confidence;
if ( /^[a-z0-9._\-]+\.[a-z]+/.test(hostname) && !/((local|dev|development|stage|staging|test|testing|demo|admin)\.|\/admin|\.local)/.test(url) ) {
if ( typeof w.ping.hostnames === 'undefined' ) {
w.ping.hostnames = {};
}
if ( typeof w.ping.hostnames[hostname] === 'undefined' ) { // Per URL
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; if ( !w.detected[url].hasOwnProperty(app)) {
} w.detected[url][app] = {};
}
if ( typeof w.ping.hostnames[hostname].applications[app] === 'undefined' ) { for ( type in confidence ) {
w.ping.hostnames[hostname].applications[app] = 1; w.detected[url][app][type] = confidence[type];
} }
w.ping.hostnames[hostname].applications[app] ++; // Calculate confidence total
w.detected[url][app].total = 0;
for ( type in w.detected[url][app] ) {
if ( type !== 'total' ) {
w.detected[url][app].total += w.detected[url][app][type];
w.detected[url][app].total = Math.min(w.detected[url][app].total, 100);
}
} }
// Per URL if ( w.detected[url][app].total >= 100 ) {
if ( w.detected[url].indexOf(app) === -1 ) { w.detected[url].push(app); } // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|staging)?|test(ing)?|demo(shop)?|admin)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} };
}
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = 1;
}
w.ping.hostnames[hostname].applications[app] ++;
} else {
w.log('Ignoring hostname "' + hostname + '"');
}
}
} }
w.log(JSON.stringify(w.detected));
// Additional information // Additional information
if ( typeof w.ping.hostnames !== 'undefined' && typeof w.ping.hostnames[hostname] !== 'undefined' ) { if ( w.ping.hostnames.hasOwnProperty(hostname) ) {
if ( typeof data.html === 'string' && data.html ) { if ( typeof data.html === 'string' && data.html ) {
match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
@ -322,7 +355,7 @@ var wappalyzer = (function() {
w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname])); w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname]));
} }
if ( typeof w.ping.hostnames === 'object' && Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); } if ( Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); }
apps = null; apps = null;
data = null; data = null;

@ -1380,8 +1380,9 @@
"cats": [ 18 ], "cats": [ 18 ],
"script": "/assets/application-[a-z\\d]{32}/\\.js", "script": "/assets/application-[a-z\\d]{32}/\\.js",
"meta": { "csrf-param": "authenticity_token" }, "meta": { "csrf-param": "authenticity_token" },
"headers": { "Server": "(mod_rails|mod_rack|Phusion\\.Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion\\.Passenger)" }, "headers": { "Server": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)" },
"implies": [ "Ruby" ] "implies": [ "Ruby" ],
"confidence": { "script": 50, "meta": 50, "headers": 50 }
}, },
"S.Builder": { "S.Builder": {
"cats": [ 1 ], "cats": [ 1 ],
@ -1581,7 +1582,7 @@
"Tumblr": { "Tumblr": {
"cats": [ 11 ], "cats": [ 11 ],
"html": "<iframe src=\\\"[^>]+tumblr\\.com", "html": "<iframe src=\\\"[^>]+tumblr\\.com",
"url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/" "url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/",
"headers": { "X-Tumblr-Usec": ".*" } "headers": { "X-Tumblr-Usec": ".*" }
}, },
"Twilight CMS": { "Twilight CMS": {

@ -30,8 +30,8 @@ var wappalyzer = (function() {
var w = { var w = {
apps: null, apps: null,
cats: null, cats: null,
ping: {}, ping: { hostnames: {} },
detected: [], detected: {},
config: { config: {
environment: 'dev', // dev | live environment: 'dev', // dev | live
@ -103,11 +103,11 @@ var wappalyzer = (function() {
} }
if ( typeof w.detected[url] === 'undefined' ) { if ( typeof w.detected[url] === 'undefined' ) {
w.detected[url] = []; w.detected[url] = {};
} }
var var
i, app, type, regex, regexMeta, regexScript, match, content, meta, header, i, app, confidence, type, regex, regexMeta, regexScript, match, content, meta, header,
profiler = { profiler = {
regexCount: 0, regexCount: 0,
startTime: new Date().getTime() startTime: new Date().getTime()
@ -118,11 +118,15 @@ var wappalyzer = (function() {
appLoop: appLoop:
for ( app in w.apps ) { for ( app in w.apps ) {
// Skip if the app has already been detected // Skip if the app has already been detected
if ( w.detected[url].indexOf(app) !== -1 || apps.indexOf(app) !== -1 ) { if ( w.detected[url].hasOwnProperty(app) || apps.indexOf(app) !== -1 ) {
continue; continue;
} }
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
confidence = {};
confidence[type] = w.apps[app].hasOwnProperty('confidence') && w.apps[app].confidence.hasOwnProperty(type) ? w.apps[app].confidence[type] : 100;
switch ( type ) { switch ( type ) {
case 'url': case 'url':
regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i'); regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i');
@ -130,7 +134,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(url) ) { if ( regex.test(url) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -146,7 +150,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type]) ) { if ( regex.test(data[type]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -166,7 +170,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(match[2]) ) { if ( regex.test(match[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -194,7 +198,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( content && content.length === 4 && regex.test(content[2]) ) { if ( content && content.length === 4 && regex.test(content[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -214,7 +218,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) { if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -232,7 +236,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type][i]) ) { if ( regex.test(data[type][i]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -250,9 +254,11 @@ var wappalyzer = (function() {
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
for ( j in apps ) { for ( j in apps ) {
if ( w.apps[apps[j]] && w.apps[apps[j]].implies ) { app = apps[j].app;
for ( k in w.apps[apps[j]].implies ) {
implied = w.apps[apps[j]].implies[k]; if ( w.apps[app] && w.apps[app].implies ) {
for ( k in w.apps[app].implies ) {
implied = w.apps[app].implies[k];
if ( !w.apps[implied] ) { if ( !w.apps[implied] ) {
w.log('Implied application ' + implied + ' does not exist'); w.log('Implied application ' + implied + ' does not exist');
@ -260,45 +266,72 @@ var wappalyzer = (function() {
continue; continue;
} }
if ( w.detected[url].indexOf(implied) === -1 && apps.indexOf(implied) === -1 ) { if ( !w.detected[url].hasOwnProperty(implied) && apps.indexOf(implied) === -1 ) {
apps.push(implied); apps.push({ app: implied, confidence: apps[j].confidence });
} }
} }
} }
} }
} }
w.log(apps.length + ' apps detected: ' + apps.join(', ') + ' on ' + url); w.log(apps.reduce(function(reduced, app) {
var i;
for ( i in app.confidence ) {
return app.app + ' (' + app.confidence[i] + '%) ';
}
}, Object.keys(apps).length + ' apps detected: ') + 'on ' + url);
// Keep history of detected apps // Keep history of detected apps
var i, app, regex, regexMeta, match; var i, app, regex, regexMeta, match;
for ( i in apps ) { for ( i in apps ) {
app = apps[i]; app = apps[i].app;
// Per hostname confidence = apps[i].confidence;
if ( /^[a-z0-9._\-]+\.[a-z]+/.test(hostname) && !/((local|dev|development|stage|staging|test|testing|demo|admin)\.|\/admin|\.local)/.test(url) ) {
if ( typeof w.ping.hostnames === 'undefined' ) {
w.ping.hostnames = {};
}
if ( typeof w.ping.hostnames[hostname] === 'undefined' ) { // Per URL
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; if ( !w.detected[url].hasOwnProperty(app)) {
} w.detected[url][app] = {};
}
if ( typeof w.ping.hostnames[hostname].applications[app] === 'undefined' ) { for ( type in confidence ) {
w.ping.hostnames[hostname].applications[app] = 1; w.detected[url][app][type] = confidence[type];
} }
w.ping.hostnames[hostname].applications[app] ++; // Calculate confidence total
w.detected[url][app].total = 0;
for ( type in w.detected[url][app] ) {
if ( type !== 'total' ) {
w.detected[url][app].total += w.detected[url][app][type];
w.detected[url][app].total = Math.min(w.detected[url][app].total, 100);
}
} }
// Per URL if ( w.detected[url][app].total >= 100 ) {
if ( w.detected[url].indexOf(app) === -1 ) { w.detected[url].push(app); } // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|staging)?|test(ing)?|demo(shop)?|admin)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} };
}
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = 1;
}
w.ping.hostnames[hostname].applications[app] ++;
} else {
w.log('Ignoring hostname "' + hostname + '"');
}
}
} }
w.log(JSON.stringify(w.detected));
// Additional information // Additional information
if ( typeof w.ping.hostnames !== 'undefined' && typeof w.ping.hostnames[hostname] !== 'undefined' ) { if ( w.ping.hostnames.hasOwnProperty(hostname) ) {
if ( typeof data.html === 'string' && data.html ) { if ( typeof data.html === 'string' && data.html ) {
match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
@ -322,7 +355,7 @@ var wappalyzer = (function() {
w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname])); w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname]));
} }
if ( typeof w.ping.hostnames === 'object' && Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); } if ( Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); }
apps = null; apps = null;
data = null; data = null;

@ -1380,8 +1380,9 @@
"cats": [ 18 ], "cats": [ 18 ],
"script": "/assets/application-[a-z\\d]{32}/\\.js", "script": "/assets/application-[a-z\\d]{32}/\\.js",
"meta": { "csrf-param": "authenticity_token" }, "meta": { "csrf-param": "authenticity_token" },
"headers": { "Server": "(mod_rails|mod_rack|Phusion\\.Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion\\.Passenger)" }, "headers": { "Server": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)" },
"implies": [ "Ruby" ] "implies": [ "Ruby" ],
"confidence": { "script": 50, "meta": 50, "headers": 50 }
}, },
"S.Builder": { "S.Builder": {
"cats": [ 1 ], "cats": [ 1 ],
@ -1581,7 +1582,7 @@
"Tumblr": { "Tumblr": {
"cats": [ 11 ], "cats": [ 11 ],
"html": "<iframe src=\\\"[^>]+tumblr\\.com", "html": "<iframe src=\\\"[^>]+tumblr\\.com",
"url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/" "url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/",
"headers": { "X-Tumblr-Usec": ".*" } "headers": { "X-Tumblr-Usec": ".*" }
}, },
"Twilight CMS": { "Twilight CMS": {

@ -136,7 +136,7 @@
*/ */
displayApps: function() { displayApps: function() {
var var
i, j, elements, menuItem, menuSeparator, image, i, j, app, confidence, elements, menuItem, menuSeparator, image,
remove = [], remove = [],
container = d.getElementById('wappalyzer-container'), container = d.getElementById('wappalyzer-container'),
menu = d.getElementById('wappalyzer-applications'), menu = d.getElementById('wappalyzer-applications'),
@ -145,7 +145,7 @@
if ( !container ) { return; } if ( !container ) { return; }
if ( w.detected[url] != null && w.detected[url].length ) { if ( w.detected[url] != null && Object.keys(w.detected[url]).length ) {
// No change // No change
if ( w.driver.lastDisplayed === JSON.stringify(w.detected[url]) ) { return; } if ( w.driver.lastDisplayed === JSON.stringify(w.detected[url]) ) { return; }
} else { } else {
@ -164,7 +164,7 @@
} }
} }
if ( w.detected[url] != null && w.detected[url].length ) { if ( w.detected[url] != null && Object.keys(w.detected[url]).length ) {
if ( !prefs.getBoolPref('showIcons') ) { if ( !prefs.getBoolPref('showIcons') ) {
image = d.createElement('image'); image = d.createElement('image');
@ -173,7 +173,9 @@
container.appendChild(image); container.appendChild(image);
} }
w.detected[url].map(function(app, i) { for ( app in w.detected[url] ) {
confidence = w.detected[url][app].total;
var j, cat, showCat, categories = []; var j, cat, showCat, categories = [];
for ( i in w.apps[app].cats ) { for ( i in w.apps[app].cats ) {
@ -189,7 +191,7 @@
menuItem.setAttribute('class', 'wappalyzer-application menuitem-iconic'); menuItem.setAttribute('class', 'wappalyzer-application menuitem-iconic');
menuItem.setAttribute('image', 'chrome://wappalyzer/skin/images/icons/' + app + '.png'); menuItem.setAttribute('image', 'chrome://wappalyzer/skin/images/icons/' + app + '.png');
menuItem.setAttribute('label', app); menuItem.setAttribute('label', app + ( confidence < 100 ? ' (' + confidence + '% sure)' : '' ));
menuItem.setAttribute('name', app); menuItem.setAttribute('name', app);
menuItem.addEventListener('command', function() { menuItem.addEventListener('command', function() {
@ -228,7 +230,7 @@
break; break;
} }
} }
}); }
w.driver.lastDisplayed = JSON.stringify(w.detected[url]); w.driver.lastDisplayed = JSON.stringify(w.detected[url]);
} else { } else {

@ -30,8 +30,8 @@ var wappalyzer = (function() {
var w = { var w = {
apps: null, apps: null,
cats: null, cats: null,
ping: {}, ping: { hostnames: {} },
detected: [], detected: {},
config: { config: {
environment: 'dev', // dev | live environment: 'dev', // dev | live
@ -103,11 +103,11 @@ var wappalyzer = (function() {
} }
if ( typeof w.detected[url] === 'undefined' ) { if ( typeof w.detected[url] === 'undefined' ) {
w.detected[url] = []; w.detected[url] = {};
} }
var var
i, app, type, regex, regexMeta, regexScript, match, content, meta, header, i, app, confidence, type, regex, regexMeta, regexScript, match, content, meta, header,
profiler = { profiler = {
regexCount: 0, regexCount: 0,
startTime: new Date().getTime() startTime: new Date().getTime()
@ -118,11 +118,15 @@ var wappalyzer = (function() {
appLoop: appLoop:
for ( app in w.apps ) { for ( app in w.apps ) {
// Skip if the app has already been detected // Skip if the app has already been detected
if ( w.detected[url].indexOf(app) !== -1 || apps.indexOf(app) !== -1 ) { if ( w.detected[url].hasOwnProperty(app) || apps.indexOf(app) !== -1 ) {
continue; continue;
} }
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
confidence = {};
confidence[type] = w.apps[app].hasOwnProperty('confidence') && w.apps[app].confidence.hasOwnProperty(type) ? w.apps[app].confidence[type] : 100;
switch ( type ) { switch ( type ) {
case 'url': case 'url':
regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i'); regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i');
@ -130,7 +134,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(url) ) { if ( regex.test(url) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -146,7 +150,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type]) ) { if ( regex.test(data[type]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -166,7 +170,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(match[2]) ) { if ( regex.test(match[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -194,7 +198,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( content && content.length === 4 && regex.test(content[2]) ) { if ( content && content.length === 4 && regex.test(content[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -214,7 +218,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) { if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -232,7 +236,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type][i]) ) { if ( regex.test(data[type][i]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -250,9 +254,11 @@ var wappalyzer = (function() {
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
for ( j in apps ) { for ( j in apps ) {
if ( w.apps[apps[j]] && w.apps[apps[j]].implies ) { app = apps[j].app;
for ( k in w.apps[apps[j]].implies ) {
implied = w.apps[apps[j]].implies[k]; if ( w.apps[app] && w.apps[app].implies ) {
for ( k in w.apps[app].implies ) {
implied = w.apps[app].implies[k];
if ( !w.apps[implied] ) { if ( !w.apps[implied] ) {
w.log('Implied application ' + implied + ' does not exist'); w.log('Implied application ' + implied + ' does not exist');
@ -260,45 +266,72 @@ var wappalyzer = (function() {
continue; continue;
} }
if ( w.detected[url].indexOf(implied) === -1 && apps.indexOf(implied) === -1 ) { if ( !w.detected[url].hasOwnProperty(implied) && apps.indexOf(implied) === -1 ) {
apps.push(implied); apps.push({ app: implied, confidence: apps[j].confidence });
} }
} }
} }
} }
} }
w.log(apps.length + ' apps detected: ' + apps.join(', ') + ' on ' + url); w.log(apps.reduce(function(reduced, app) {
var i;
for ( i in app.confidence ) {
return app.app + ' (' + app.confidence[i] + '%) ';
}
}, Object.keys(apps).length + ' apps detected: ') + 'on ' + url);
// Keep history of detected apps // Keep history of detected apps
var i, app, regex, regexMeta, match; var i, app, regex, regexMeta, match;
for ( i in apps ) { for ( i in apps ) {
app = apps[i]; app = apps[i].app;
// Per hostname confidence = apps[i].confidence;
if ( /^[a-z0-9._\-]+\.[a-z]+/.test(hostname) && !/((local|dev|development|stage|staging|test|testing|demo|admin)\.|\/admin|\.local)/.test(url) ) {
if ( typeof w.ping.hostnames === 'undefined' ) {
w.ping.hostnames = {};
}
if ( typeof w.ping.hostnames[hostname] === 'undefined' ) { // Per URL
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; if ( !w.detected[url].hasOwnProperty(app)) {
} w.detected[url][app] = {};
}
if ( typeof w.ping.hostnames[hostname].applications[app] === 'undefined' ) { for ( type in confidence ) {
w.ping.hostnames[hostname].applications[app] = 1; w.detected[url][app][type] = confidence[type];
} }
w.ping.hostnames[hostname].applications[app] ++; // Calculate confidence total
w.detected[url][app].total = 0;
for ( type in w.detected[url][app] ) {
if ( type !== 'total' ) {
w.detected[url][app].total += w.detected[url][app][type];
w.detected[url][app].total = Math.min(w.detected[url][app].total, 100);
}
} }
// Per URL if ( w.detected[url][app].total >= 100 ) {
if ( w.detected[url].indexOf(app) === -1 ) { w.detected[url].push(app); } // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|staging)?|test(ing)?|demo(shop)?|admin)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} };
}
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = 1;
}
w.ping.hostnames[hostname].applications[app] ++;
} else {
w.log('Ignoring hostname "' + hostname + '"');
}
}
} }
w.log(JSON.stringify(w.detected));
// Additional information // Additional information
if ( typeof w.ping.hostnames !== 'undefined' && typeof w.ping.hostnames[hostname] !== 'undefined' ) { if ( w.ping.hostnames.hasOwnProperty(hostname) ) {
if ( typeof data.html === 'string' && data.html ) { if ( typeof data.html === 'string' && data.html ) {
match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
@ -322,7 +355,7 @@ var wappalyzer = (function() {
w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname])); w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname]));
} }
if ( typeof w.ping.hostnames === 'object' && Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); } if ( Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); }
apps = null; apps = null;
data = null; data = null;

@ -1380,8 +1380,9 @@
"cats": [ 18 ], "cats": [ 18 ],
"script": "/assets/application-[a-z\\d]{32}/\\.js", "script": "/assets/application-[a-z\\d]{32}/\\.js",
"meta": { "csrf-param": "authenticity_token" }, "meta": { "csrf-param": "authenticity_token" },
"headers": { "Server": "(mod_rails|mod_rack|Phusion\\.Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion\\.Passenger)" }, "headers": { "Server": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)" },
"implies": [ "Ruby" ] "implies": [ "Ruby" ],
"confidence": { "script": 50, "meta": 50, "headers": 50 }
}, },
"S.Builder": { "S.Builder": {
"cats": [ 1 ], "cats": [ 1 ],
@ -1581,7 +1582,7 @@
"Tumblr": { "Tumblr": {
"cats": [ 11 ], "cats": [ 11 ],
"html": "<iframe src=\\\"[^>]+tumblr\\.com", "html": "<iframe src=\\\"[^>]+tumblr\\.com",
"url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/" "url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/",
"headers": { "X-Tumblr-Usec": ".*" } "headers": { "X-Tumblr-Usec": ".*" }
}, },
"Twilight CMS": { "Twilight CMS": {

@ -30,8 +30,8 @@ var wappalyzer = (function() {
var w = { var w = {
apps: null, apps: null,
cats: null, cats: null,
ping: {}, ping: { hostnames: {} },
detected: [], detected: {},
config: { config: {
environment: 'dev', // dev | live environment: 'dev', // dev | live
@ -103,11 +103,11 @@ var wappalyzer = (function() {
} }
if ( typeof w.detected[url] === 'undefined' ) { if ( typeof w.detected[url] === 'undefined' ) {
w.detected[url] = []; w.detected[url] = {};
} }
var var
i, app, type, regex, regexMeta, regexScript, match, content, meta, header, i, app, confidence, type, regex, regexMeta, regexScript, match, content, meta, header,
profiler = { profiler = {
regexCount: 0, regexCount: 0,
startTime: new Date().getTime() startTime: new Date().getTime()
@ -118,11 +118,15 @@ var wappalyzer = (function() {
appLoop: appLoop:
for ( app in w.apps ) { for ( app in w.apps ) {
// Skip if the app has already been detected // Skip if the app has already been detected
if ( w.detected[url].indexOf(app) !== -1 || apps.indexOf(app) !== -1 ) { if ( w.detected[url].hasOwnProperty(app) || apps.indexOf(app) !== -1 ) {
continue; continue;
} }
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
confidence = {};
confidence[type] = w.apps[app].hasOwnProperty('confidence') && w.apps[app].confidence.hasOwnProperty(type) ? w.apps[app].confidence[type] : 100;
switch ( type ) { switch ( type ) {
case 'url': case 'url':
regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i'); regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i');
@ -130,7 +134,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(url) ) { if ( regex.test(url) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -146,7 +150,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type]) ) { if ( regex.test(data[type]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -166,7 +170,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(match[2]) ) { if ( regex.test(match[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -194,7 +198,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( content && content.length === 4 && regex.test(content[2]) ) { if ( content && content.length === 4 && regex.test(content[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -214,7 +218,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) { if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -232,7 +236,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type][i]) ) { if ( regex.test(data[type][i]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -250,9 +254,11 @@ var wappalyzer = (function() {
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
for ( j in apps ) { for ( j in apps ) {
if ( w.apps[apps[j]] && w.apps[apps[j]].implies ) { app = apps[j].app;
for ( k in w.apps[apps[j]].implies ) {
implied = w.apps[apps[j]].implies[k]; if ( w.apps[app] && w.apps[app].implies ) {
for ( k in w.apps[app].implies ) {
implied = w.apps[app].implies[k];
if ( !w.apps[implied] ) { if ( !w.apps[implied] ) {
w.log('Implied application ' + implied + ' does not exist'); w.log('Implied application ' + implied + ' does not exist');
@ -260,45 +266,72 @@ var wappalyzer = (function() {
continue; continue;
} }
if ( w.detected[url].indexOf(implied) === -1 && apps.indexOf(implied) === -1 ) { if ( !w.detected[url].hasOwnProperty(implied) && apps.indexOf(implied) === -1 ) {
apps.push(implied); apps.push({ app: implied, confidence: apps[j].confidence });
} }
} }
} }
} }
} }
w.log(apps.length + ' apps detected: ' + apps.join(', ') + ' on ' + url); w.log(apps.reduce(function(reduced, app) {
var i;
for ( i in app.confidence ) {
return app.app + ' (' + app.confidence[i] + '%) ';
}
}, Object.keys(apps).length + ' apps detected: ') + 'on ' + url);
// Keep history of detected apps // Keep history of detected apps
var i, app, regex, regexMeta, match; var i, app, regex, regexMeta, match;
for ( i in apps ) { for ( i in apps ) {
app = apps[i]; app = apps[i].app;
// Per hostname confidence = apps[i].confidence;
if ( /^[a-z0-9._\-]+\.[a-z]+/.test(hostname) && !/((local|dev|development|stage|staging|test|testing|demo|admin)\.|\/admin|\.local)/.test(url) ) {
if ( typeof w.ping.hostnames === 'undefined' ) {
w.ping.hostnames = {};
}
if ( typeof w.ping.hostnames[hostname] === 'undefined' ) { // Per URL
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; if ( !w.detected[url].hasOwnProperty(app)) {
} w.detected[url][app] = {};
}
if ( typeof w.ping.hostnames[hostname].applications[app] === 'undefined' ) { for ( type in confidence ) {
w.ping.hostnames[hostname].applications[app] = 1; w.detected[url][app][type] = confidence[type];
} }
w.ping.hostnames[hostname].applications[app] ++; // Calculate confidence total
w.detected[url][app].total = 0;
for ( type in w.detected[url][app] ) {
if ( type !== 'total' ) {
w.detected[url][app].total += w.detected[url][app][type];
w.detected[url][app].total = Math.min(w.detected[url][app].total, 100);
}
} }
// Per URL if ( w.detected[url][app].total >= 100 ) {
if ( w.detected[url].indexOf(app) === -1 ) { w.detected[url].push(app); } // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|staging)?|test(ing)?|demo(shop)?|admin)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} };
}
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = 1;
}
w.ping.hostnames[hostname].applications[app] ++;
} else {
w.log('Ignoring hostname "' + hostname + '"');
}
}
} }
w.log(JSON.stringify(w.detected));
// Additional information // Additional information
if ( typeof w.ping.hostnames !== 'undefined' && typeof w.ping.hostnames[hostname] !== 'undefined' ) { if ( w.ping.hostnames.hasOwnProperty(hostname) ) {
if ( typeof data.html === 'string' && data.html ) { if ( typeof data.html === 'string' && data.html ) {
match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
@ -322,7 +355,7 @@ var wappalyzer = (function() {
w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname])); w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname]));
} }
if ( typeof w.ping.hostnames === 'object' && Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); } if ( Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); }
apps = null; apps = null;
data = null; data = null;

@ -54,7 +54,7 @@ class Wappalyzer
//'env' => $env //'env' => $env
)); ));
return $this->v8->executeString(' $result = $this->v8->executeString('
w.apps = ' . json_encode($this->apps) . '; w.apps = ' . json_encode($this->apps) . ';
w.categories = ' . json_encode($this->categories) . '; w.categories = ' . json_encode($this->categories) . ';
w.driver.debug = ' . ( $this->debug ? 'true' : 'false' ) . '; w.driver.debug = ' . ( $this->debug ? 'true' : 'false' ) . ';
@ -62,6 +62,8 @@ class Wappalyzer
w.driver.init(); w.driver.init();
'); ');
return json_decode($result);
} catch ( V8JsException $e ) { } catch ( V8JsException $e ) {
throw new WappalyzerException('JavaScript error: ' . $e->getMessage()); throw new WappalyzerException('JavaScript error: ' . $e->getMessage());
} }

@ -1380,8 +1380,9 @@
"cats": [ 18 ], "cats": [ 18 ],
"script": "/assets/application-[a-z\\d]{32}/\\.js", "script": "/assets/application-[a-z\\d]{32}/\\.js",
"meta": { "csrf-param": "authenticity_token" }, "meta": { "csrf-param": "authenticity_token" },
"headers": { "Server": "(mod_rails|mod_rack|Phusion\\.Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion\\.Passenger)" }, "headers": { "Server": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)" },
"implies": [ "Ruby" ] "implies": [ "Ruby" ],
"confidence": { "script": 50, "meta": 50, "headers": 50 }
}, },
"S.Builder": { "S.Builder": {
"cats": [ 1 ], "cats": [ 1 ],
@ -1581,7 +1582,7 @@
"Tumblr": { "Tumblr": {
"cats": [ 11 ], "cats": [ 11 ],
"html": "<iframe src=\\\"[^>]+tumblr\\.com", "html": "<iframe src=\\\"[^>]+tumblr\\.com",
"url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/" "url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/",
"headers": { "X-Tumblr-Usec": ".*" } "headers": { "X-Tumblr-Usec": ".*" }
}, },
"Twilight CMS": { "Twilight CMS": {

@ -21,7 +21,9 @@ try {
$detectedApps = $wappalyzer->analyze(); $detectedApps = $wappalyzer->analyze();
if ( $detectedApps ) { if ( $detectedApps ) {
echo implode("\n", $detectedApps) . "\n"; foreach ( $detectedApps as $detectedApp => $data ) {
echo $detectedApp . ', ' . $data->confidence . '%, ', implode(', ', $data->categories) . "\n";
}
} else { } else {
echo "No applications detected\n"; echo "No applications detected\n";
} }

@ -15,20 +15,25 @@ w.driver = {
* Initialize * Initialize
*/ */
init: function() { init: function() {
var app, apps = {};
w.analyze(w.driver.data.host, w.driver.data.url, { w.analyze(w.driver.data.host, w.driver.data.url, {
html: w.driver.data.html, html: w.driver.data.html,
headers: w.driver.data.headers headers: w.driver.data.headers
}); });
/* Return categories for ( app in w.detected[w.driver.data.url] ) {
w.detected[w.driver.data.url].map(function(app, i) { apps[app] = {
categories: [],
confidence: w.detected[w.driver.data.url][app].total
};
w.apps[app].cats.map(function(cat) { w.apps[app].cats.map(function(cat) {
w.detected[w.driver.data.url][i] += ' ' + w.categories[cat]; apps[app].categories.push(w.categories[cat]);
}); });
}); };
*/
return w.detected[w.driver.data.url]; return JSON.stringify(apps);
}, },
/** /**

@ -30,8 +30,8 @@ var wappalyzer = (function() {
var w = { var w = {
apps: null, apps: null,
cats: null, cats: null,
ping: {}, ping: { hostnames: {} },
detected: [], detected: {},
config: { config: {
environment: 'dev', // dev | live environment: 'dev', // dev | live
@ -103,11 +103,11 @@ var wappalyzer = (function() {
} }
if ( typeof w.detected[url] === 'undefined' ) { if ( typeof w.detected[url] === 'undefined' ) {
w.detected[url] = []; w.detected[url] = {};
} }
var var
i, app, type, regex, regexMeta, regexScript, match, content, meta, header, i, app, confidence, type, regex, regexMeta, regexScript, match, content, meta, header,
profiler = { profiler = {
regexCount: 0, regexCount: 0,
startTime: new Date().getTime() startTime: new Date().getTime()
@ -118,11 +118,15 @@ var wappalyzer = (function() {
appLoop: appLoop:
for ( app in w.apps ) { for ( app in w.apps ) {
// Skip if the app has already been detected // Skip if the app has already been detected
if ( w.detected[url].indexOf(app) !== -1 || apps.indexOf(app) !== -1 ) { if ( w.detected[url].hasOwnProperty(app) || apps.indexOf(app) !== -1 ) {
continue; continue;
} }
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
confidence = {};
confidence[type] = w.apps[app].hasOwnProperty('confidence') && w.apps[app].confidence.hasOwnProperty(type) ? w.apps[app].confidence[type] : 100;
switch ( type ) { switch ( type ) {
case 'url': case 'url':
regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i'); regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i');
@ -130,7 +134,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(url) ) { if ( regex.test(url) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -146,7 +150,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type]) ) { if ( regex.test(data[type]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -166,7 +170,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(match[2]) ) { if ( regex.test(match[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -194,7 +198,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( content && content.length === 4 && regex.test(content[2]) ) { if ( content && content.length === 4 && regex.test(content[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -214,7 +218,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) { if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -232,7 +236,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type][i]) ) { if ( regex.test(data[type][i]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -250,9 +254,11 @@ var wappalyzer = (function() {
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
for ( j in apps ) { for ( j in apps ) {
if ( w.apps[apps[j]] && w.apps[apps[j]].implies ) { app = apps[j].app;
for ( k in w.apps[apps[j]].implies ) {
implied = w.apps[apps[j]].implies[k]; if ( w.apps[app] && w.apps[app].implies ) {
for ( k in w.apps[app].implies ) {
implied = w.apps[app].implies[k];
if ( !w.apps[implied] ) { if ( !w.apps[implied] ) {
w.log('Implied application ' + implied + ' does not exist'); w.log('Implied application ' + implied + ' does not exist');
@ -260,45 +266,72 @@ var wappalyzer = (function() {
continue; continue;
} }
if ( w.detected[url].indexOf(implied) === -1 && apps.indexOf(implied) === -1 ) { if ( !w.detected[url].hasOwnProperty(implied) && apps.indexOf(implied) === -1 ) {
apps.push(implied); apps.push({ app: implied, confidence: apps[j].confidence });
} }
} }
} }
} }
} }
w.log(apps.length + ' apps detected: ' + apps.join(', ') + ' on ' + url); w.log(apps.reduce(function(reduced, app) {
var i;
for ( i in app.confidence ) {
return app.app + ' (' + app.confidence[i] + '%) ';
}
}, Object.keys(apps).length + ' apps detected: ') + 'on ' + url);
// Keep history of detected apps // Keep history of detected apps
var i, app, regex, regexMeta, match; var i, app, regex, regexMeta, match;
for ( i in apps ) { for ( i in apps ) {
app = apps[i]; app = apps[i].app;
// Per hostname confidence = apps[i].confidence;
if ( /^[a-z0-9._\-]+\.[a-z]+/.test(hostname) && !/((local|dev|development|stage|staging|test|testing|demo|admin)\.|\/admin|\.local)/.test(url) ) {
if ( typeof w.ping.hostnames === 'undefined' ) {
w.ping.hostnames = {};
}
if ( typeof w.ping.hostnames[hostname] === 'undefined' ) { // Per URL
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; if ( !w.detected[url].hasOwnProperty(app)) {
} w.detected[url][app] = {};
}
if ( typeof w.ping.hostnames[hostname].applications[app] === 'undefined' ) { for ( type in confidence ) {
w.ping.hostnames[hostname].applications[app] = 1; w.detected[url][app][type] = confidence[type];
} }
w.ping.hostnames[hostname].applications[app] ++; // Calculate confidence total
w.detected[url][app].total = 0;
for ( type in w.detected[url][app] ) {
if ( type !== 'total' ) {
w.detected[url][app].total += w.detected[url][app][type];
w.detected[url][app].total = Math.min(w.detected[url][app].total, 100);
}
} }
// Per URL if ( w.detected[url][app].total >= 100 ) {
if ( w.detected[url].indexOf(app) === -1 ) { w.detected[url].push(app); } // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|staging)?|test(ing)?|demo(shop)?|admin)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} };
}
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = 1;
}
w.ping.hostnames[hostname].applications[app] ++;
} else {
w.log('Ignoring hostname "' + hostname + '"');
}
}
} }
w.log(JSON.stringify(w.detected));
// Additional information // Additional information
if ( typeof w.ping.hostnames !== 'undefined' && typeof w.ping.hostnames[hostname] !== 'undefined' ) { if ( w.ping.hostnames.hasOwnProperty(hostname) ) {
if ( typeof data.html === 'string' && data.html ) { if ( typeof data.html === 'string' && data.html ) {
match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
@ -322,7 +355,7 @@ var wappalyzer = (function() {
w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname])); w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname]));
} }
if ( typeof w.ping.hostnames === 'object' && Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); } if ( Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); }
apps = null; apps = null;
data = null; data = null;

@ -1380,8 +1380,9 @@
"cats": [ 18 ], "cats": [ 18 ],
"script": "/assets/application-[a-z\\d]{32}/\\.js", "script": "/assets/application-[a-z\\d]{32}/\\.js",
"meta": { "csrf-param": "authenticity_token" }, "meta": { "csrf-param": "authenticity_token" },
"headers": { "Server": "(mod_rails|mod_rack|Phusion\\.Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion\\.Passenger)" }, "headers": { "Server": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)", "X-Powered-By": "(mod_rails|mod_rack|Phusion(\\.|_)Passenger)" },
"implies": [ "Ruby" ] "implies": [ "Ruby" ],
"confidence": { "script": 50, "meta": 50, "headers": 50 }
}, },
"S.Builder": { "S.Builder": {
"cats": [ 1 ], "cats": [ 1 ],
@ -1581,7 +1582,7 @@
"Tumblr": { "Tumblr": {
"cats": [ 11 ], "cats": [ 11 ],
"html": "<iframe src=\\\"[^>]+tumblr\\.com", "html": "<iframe src=\\\"[^>]+tumblr\\.com",
"url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/" "url": "^https?://(www\\.)?[^/]+\\.tumblr\\.com/",
"headers": { "X-Tumblr-Usec": ".*" } "headers": { "X-Tumblr-Usec": ".*" }
}, },
"Twilight CMS": { "Twilight CMS": {

@ -30,8 +30,8 @@ var wappalyzer = (function() {
var w = { var w = {
apps: null, apps: null,
cats: null, cats: null,
ping: {}, ping: { hostnames: {} },
detected: [], detected: {},
config: { config: {
environment: 'dev', // dev | live environment: 'dev', // dev | live
@ -103,11 +103,11 @@ var wappalyzer = (function() {
} }
if ( typeof w.detected[url] === 'undefined' ) { if ( typeof w.detected[url] === 'undefined' ) {
w.detected[url] = []; w.detected[url] = {};
} }
var var
i, app, type, regex, regexMeta, regexScript, match, content, meta, header, i, app, confidence, type, regex, regexMeta, regexScript, match, content, meta, header,
profiler = { profiler = {
regexCount: 0, regexCount: 0,
startTime: new Date().getTime() startTime: new Date().getTime()
@ -118,11 +118,15 @@ var wappalyzer = (function() {
appLoop: appLoop:
for ( app in w.apps ) { for ( app in w.apps ) {
// Skip if the app has already been detected // Skip if the app has already been detected
if ( w.detected[url].indexOf(app) !== -1 || apps.indexOf(app) !== -1 ) { if ( w.detected[url].hasOwnProperty(app) || apps.indexOf(app) !== -1 ) {
continue; continue;
} }
for ( type in w.apps[app] ) { for ( type in w.apps[app] ) {
confidence = {};
confidence[type] = w.apps[app].hasOwnProperty('confidence') && w.apps[app].confidence.hasOwnProperty(type) ? w.apps[app].confidence[type] : 100;
switch ( type ) { switch ( type ) {
case 'url': case 'url':
regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i'); regex = new RegExp(w.apps[app][type].replace('/', '\\\/'), 'i');
@ -130,7 +134,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(url) ) { if ( regex.test(url) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -146,7 +150,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type]) ) { if ( regex.test(data[type]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -166,7 +170,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(match[2]) ) { if ( regex.test(match[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -194,7 +198,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( content && content.length === 4 && regex.test(content[2]) ) { if ( content && content.length === 4 && regex.test(content[2]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -214,7 +218,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) { if ( typeof data[type][header] === 'string' && regex.test(data[type][header]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -232,7 +236,7 @@ var wappalyzer = (function() {
profiler.regexCount ++; profiler.regexCount ++;
if ( regex.test(data[type][i]) ) { if ( regex.test(data[type][i]) ) {
apps.push(app); apps.push({ app: app, confidence: confidence });
continue appLoop; continue appLoop;
} }
@ -250,9 +254,11 @@ var wappalyzer = (function() {
for ( i = 0; i < 3; i ++ ) { for ( i = 0; i < 3; i ++ ) {
for ( j in apps ) { for ( j in apps ) {
if ( w.apps[apps[j]] && w.apps[apps[j]].implies ) { app = apps[j].app;
for ( k in w.apps[apps[j]].implies ) {
implied = w.apps[apps[j]].implies[k]; if ( w.apps[app] && w.apps[app].implies ) {
for ( k in w.apps[app].implies ) {
implied = w.apps[app].implies[k];
if ( !w.apps[implied] ) { if ( !w.apps[implied] ) {
w.log('Implied application ' + implied + ' does not exist'); w.log('Implied application ' + implied + ' does not exist');
@ -260,45 +266,72 @@ var wappalyzer = (function() {
continue; continue;
} }
if ( w.detected[url].indexOf(implied) === -1 && apps.indexOf(implied) === -1 ) { if ( !w.detected[url].hasOwnProperty(implied) && apps.indexOf(implied) === -1 ) {
apps.push(implied); apps.push({ app: implied, confidence: apps[j].confidence });
} }
} }
} }
} }
} }
w.log(apps.length + ' apps detected: ' + apps.join(', ') + ' on ' + url); w.log(apps.reduce(function(reduced, app) {
var i;
for ( i in app.confidence ) {
return app.app + ' (' + app.confidence[i] + '%) ';
}
}, Object.keys(apps).length + ' apps detected: ') + 'on ' + url);
// Keep history of detected apps // Keep history of detected apps
var i, app, regex, regexMeta, match; var i, app, regex, regexMeta, match;
for ( i in apps ) { for ( i in apps ) {
app = apps[i]; app = apps[i].app;
// Per hostname confidence = apps[i].confidence;
if ( /^[a-z0-9._\-]+\.[a-z]+/.test(hostname) && !/((local|dev|development|stage|staging|test|testing|demo|admin)\.|\/admin|\.local)/.test(url) ) {
if ( typeof w.ping.hostnames === 'undefined' ) {
w.ping.hostnames = {};
}
if ( typeof w.ping.hostnames[hostname] === 'undefined' ) { // Per URL
w.ping.hostnames[hostname] = { applications: {}, meta: {} }; if ( !w.detected[url].hasOwnProperty(app)) {
} w.detected[url][app] = {};
}
if ( typeof w.ping.hostnames[hostname].applications[app] === 'undefined' ) { for ( type in confidence ) {
w.ping.hostnames[hostname].applications[app] = 1; w.detected[url][app][type] = confidence[type];
} }
w.ping.hostnames[hostname].applications[app] ++; // Calculate confidence total
w.detected[url][app].total = 0;
for ( type in w.detected[url][app] ) {
if ( type !== 'total' ) {
w.detected[url][app].total += w.detected[url][app][type];
w.detected[url][app].total = Math.min(w.detected[url][app].total, 100);
}
} }
// Per URL if ( w.detected[url][app].total >= 100 ) {
if ( w.detected[url].indexOf(app) === -1 ) { w.detected[url].push(app); } // Per hostname
if ( /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/.test(hostname) && !/((local|dev(elopment)?|stag(e|staging)?|test(ing)?|demo(shop)?|admin)\.|\/admin|\.local)/.test(url) ) {
if ( !w.ping.hostnames.hasOwnProperty(hostname) ) {
w.ping.hostnames[hostname] = { applications: {}, meta: {} };
}
if ( !w.ping.hostnames[hostname].applications.hasOwnProperty(app) ) {
w.ping.hostnames[hostname].applications[app] = 1;
}
w.ping.hostnames[hostname].applications[app] ++;
} else {
w.log('Ignoring hostname "' + hostname + '"');
}
}
} }
w.log(JSON.stringify(w.detected));
// Additional information // Additional information
if ( typeof w.ping.hostnames !== 'undefined' && typeof w.ping.hostnames[hostname] !== 'undefined' ) { if ( w.ping.hostnames.hasOwnProperty(hostname) ) {
if ( typeof data.html === 'string' && data.html ) { if ( typeof data.html === 'string' && data.html ) {
match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i); match = data.html.match(/<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"/i);
@ -322,7 +355,7 @@ var wappalyzer = (function() {
w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname])); w.log(hostname + ': ' + JSON.stringify(w.ping.hostnames[hostname]));
} }
if ( typeof w.ping.hostnames === 'object' && Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); } if ( Object.keys(w.ping.hostnames).length >= 50 ) { driver('ping'); }
apps = null; apps = null;
data = null; data = null;

Loading…
Cancel
Save