/detector.js
JavaScript | 502 lines | 435 code | 32 blank | 35 comment | 82 complexity | 30a017f6ad67e0668dc08d329cb6433b MD5 | raw file
1/** 2 * Chrome AppSniffer 3 * 4 * Detect apps run on current page and send back to background page. 5 * Some part of this script was refered from Wappalyzer Firefox Addon. 6 * 7 * @author Bao Nguyen <contact@nqbao.com> 8 * @author justjavac <justjavac@gmail.com> 9 * @license GPLv3 10 **/ 11 12(function () { 13 var _apps = {}; 14 var doc = document.documentElement; 15 var name; 16 var r; 17 18 // 1: detect by meta tags, the first matching group will be version 19 // 根据 meta 信息判断 20 var metas = doc.getElementsByTagName("meta"); 21 var meta_tests = { 22 'generator': { 23 'Joomla': /joomla!?\s*([\d\.]+)?/i, 24 'vBulletin': /vBulletin\s*(.*)/i, 25 'WordPress': /WordPress\s*(.*)/i, 26 'XOOPS': /xoops/i, 27 'Plone': /plone/i, 28 'MediaWiki': /MediaWiki/i, 29 'CMSMadeSimple': /CMS Made Simple/i, 30 'SilverStripe': /SilverStripe/i, 31 'Movable Type': /Movable Type/i, 32 'Amiro.CMS': /Amiro/i, 33 'Koobi': /koobi/i, 34 'bbPress': /bbPress/i, 35 'DokuWiki': /dokuWiki/i, 36 'TYPO3': /TYPO3/i, 37 'PHP-Nuke': /PHP-Nuke/i, 38 'DotNetNuke': /DotNetNuke/i, 39 'Sitefinity': /Sitefinity\s+(.*)/i, 40 'WebGUI': /WebGUI/i, 41 'ez Publish': /eZ\s*Publish/i, 42 'BIGACE': /BIGACE/i, 43 'TypePad': /typepad\.com/i, 44 'Blogger': /blogger/i, 45 'PrestaShop': /PrestaShop/i, 46 'SharePoint': /SharePoint/, 47 'JaliosJCMS': /Jalios JCMS/i, 48 'ZenCart': /zen-cart/i, 49 'WPML': /WPML/i, 50 'PivotX': /PivotX/i, 51 'OpenACS': /OpenACS/i, 52 'AlphaCMS': /alphacms\s+(.*)/i, 53 'concrete5': /concrete5 -\s*(.*)$/, 54 'Webnode': /Webnode/, 55 'GetSimple': /GetSimple/, 56 'DataLifeEngine': /DataLife Engine/, 57 'ClanSphere': /ClanSphere/, 58 'Mura CMS': /Mura CMS\s*(.*)/i, 59 'Tiki Wiki CMS Groupware': /Tiki/i 60 }, 61 'copyright': { 62 'phpBB': /phpBB/i 63 }, 64 'elggrelease': { 65 'Elgg': /.+/ 66 }, 67 'powered-by': { 68 'Serendipity': /Serendipity/i 69 }, 70 'author': { 71 'Avactis': /Avactis Team/i 72 } 73 }; 74 75 for (var idx in metas) { 76 var m = metas[idx]; 77 name = m.name ? m.name.toLowerCase() : ""; 78 79 if (!meta_tests[name]) continue; 80 81 for (var t in meta_tests[name]) { 82 if (t in _apps) continue; 83 84 r = meta_tests[name][t].exec(m.content); 85 if (r) { 86 _apps[t] = r[1] ? r[1] : -1; 87 } 88 } 89 } 90 91 // 2: detect by script tags 92 // 根据 script 标签判断 93 var scripts = doc.getElementsByTagName("script"); 94 95 var script_tests = { 96 '\u767e\u5ea6\u7edf\u8ba1': /hm\.baidu\.com\/h\.js/i, // 百度统计 97 'cnzz': /(w|s14)\.cnzz\.com\/(c|stat)\.php/i, 98 'bShare': /static\.bshare\.cn\/b/, 99 '\u767e\u5ea6\u5206\u4eab': /bdimg\.share\.baidu\.com\/static\/js\//, // 百度分享 100 '\u591a\u8bf4': /static\.duoshuo\.com\/embed\.js/, // 多说 101 '\u53cb\u8350': /v\d\.ujian\.cc\/code\/ujian\.js/, // 友荐 102 '\u53cb\u8a00': /v\d\.uyan\.cc\/(code\/uyan\.js|js\/iframe\.js)/, // 友言 103 'JiaThis': /v\d\.jiathis\.com\/code(_mini)?\/jiathis/, 104 '\u65e0\u89c5': /widget\.wumii\.(cn|com)\/ext\/relatedItemsWidget/, // 无觅相关文章插件 105 'segmentfault': /w\.segmentfault\.com\/card\/\d+\.js/i, 106 107 'Google Analytics': /google-analytics\.com\/(ga|urchin).js/i, 108 'Quantcast': /quantserve\.com\/quant\.js/i, 109 'Prototype': /prototype\.js/i, 110 'Joomla': /\/components\/com_/, 111 'Ubercart': /uc_cart/i, 112 'Closure': /\/goog\/base\.js/i, 113 'MODx': /\/min\/b=.*f=.*/, 114 'MooTools': /mootools/i, 115 'Dojo': /dojo(\.xd)?\.js/i, 116 'script.aculo.us': /scriptaculous\.js/i, 117 'Disqus': /disqus.com/i, 118 'GetSatisfaction': /getsatisfaction\.com\/feedback/i, 119 'Wibiya': /wibiya\.com\/Loaders\//i, 120 'reCaptcha': /(google\.com\/recaptcha|api\.recaptcha\.net\/)/i, 121 'Mollom': /mollom\/mollom\.js/i, // only work on Drupal now 122 'ZenPhoto': /zp-core\/js/i, 123 'Gallery2': /main\.php\?.*g2_.*/i, 124 'AdSense': /pagead\/show_ads\.js/, 125 'XenForo': /js\/xenforo\//i, 126 'Cappuccino': /Frameworks\/Objective-J\/Objective-J\.js/, 127 'Avactis': /\/avactis-themes\//i, 128 'Volusion': /a\/j\/javascripts\.js/, 129 'AddThis': /addthis\.com\/js/, 130 131 'BuySellAds': /buysellads.com\/.*bsa\.js/, 132 'Weebly': /weebly\.com\/weebly\//, 133 'Bootstrap': /bootstrap-.*\.js/, 134 'Jigsy': /javascripts\/asterion\.js/, // may change later 135 'Yola': /analytics\.yola\.net/, // may change later 136 'Alfresco': /(alfresco)+(-min)?(\/scripts\/menu)?\.js/, // both Alfresco Share and Explorer apps 137 'Mura CMS': /mura\/js/, 138 'Tiki Wiki CMS Groupware': /tiki-js/, 139 'OpenTag': /opentag.*\.js/, 140 'KISSmetrics': /i.kissmetrics.com\/i.js/ 141 }; 142 143 for (var idx in scripts) { 144 var s = scripts[idx]; 145 if (!s.src) continue; 146 s = s.src; 147 148 for (var t in script_tests) { 149 if (t in _apps) continue; 150 if (script_tests[t].test(s)) { 151 _apps[t] = -1; 152 } 153 } 154 } 155 156 // 3: detect by domains 157 // 根据 domain 信息判断 158 159 // 4: detect by regexp 160 // 用正则表达式判断 161 var text = document.documentElement.outerHTML; 162 var text_tests = { 163 'SMF': /<script .+\s+var smf_/i, 164 'Magento': /var BLANK_URL = '[^>]+js\/blank\.html'/i, 165 'Tumblr': /<iframe src=("|')http:\/\/\S+\.tumblr\.com/i, 166 'WordPress': /<link rel=("|')stylesheet("|') [^>]+wp-content/i, 167 'Closure': /<script[^>]*>.*goog\.require/i, 168 'Liferay': /<script[^>]*>.*LifeRay\.currentURL/i, 169 'vBulletin': /vbmenu_control/i, 170 'MODx': /(<a[^>]+>Powered by MODx<\/a>|var el= \$\('modxhost'\);|<script type=("|')text\/javascript("|')>var MODX_MEDIA_PATH = "media";)/i, 171 'miniBB': /<a href=("|')[^>]+minibb.+\s*<!--End of copyright link/i, 172 'PHP-Fusion': /(href|src)=["']?infusions\//i, // @todo: recheck this pattern again 173 'OpenX': /(href|src)=["'].*delivery\/(afr|ajs|avw|ck)\.php[^"']*/, 174 'GetSatisfaction': /asset_host\s*\+\s*"javascripts\/feedback.*\.js/igm, // better recognization 175 'Fatwire': /\/Satellite\?|\/ContentServer\?/, 176 'Contao': /powered by (TYPOlight|Contao)/i, 177 'Moodle' : /<link[^>]*\/theme\/standard\/styles.php".*>|<link[^>]*\/theme\/styles.php\?theme=.*".*>/, 178 '1c-bitrix' : /<link[^>]*\/bitrix\/.*?>/i, 179 'OpenCMS' : /<link[^>]*\.opencms\..*?>/i, 180 'HumansTxt': /<link[^>]*rel=['"]?author['"]?/i, 181 'GoogleFontApi': /ref=["']?http:\/\/fonts.googleapis.com\//i, 182 'Prostores' : /-legacycss\/Asset">/, 183 'osCommerce': /(product_info\.php\?products_id|_eof \/\/-->)/, 184 'OpenCart': /index.php\?route=product\/product/, 185 'Shibboleth': /<form action="\/idp\/Authn\/UserPassword" method="post">/ 186 }; 187 188 for (var t in text_tests) { 189 if (t in _apps) continue; 190 if (text_tests[t].test(text)) { 191 _apps[t] = -1; 192 } 193 } 194 195 // TODO: merge inline detector with version detector 196 197 // 5: detect by inline javascript 198 // 根据 inline javascript 判断 199 var js_tests = { 200 'Highcharts': function() { 201 return window.Highcharts; 202 }, 203 'Zepto.js': function() { 204 return window.Zepto && window.Zepto.fn; 205 }, 206 'CodeIgniter': function() { 207 return document.cookie.indexOf("cisession") != -1; 208 }, 209 'Java': function() { 210 return document.cookie.indexOf("JSESSIONID") != -1; 211 }, 212 'Domino': function() { 213 return document.cookie.indexOf("LtpaToken") != -1||document.cookie.indexOf("DomAuthSessId") != -1; 214 }, 215 'Drupal': function() { 216 return window.Drupal; 217 }, 218 'TomatoCMS': function() { 219 return window.Tomato; 220 }, 221 'MojoMotor': function() { 222 return window.Mojo; 223 }, 224 'ErainCart': function() { 225 return window.fn_register_hooks; 226 }, 227 'SugarCRM': function() { 228 return window.SUGAR; 229 }, 230 'YUI': function() { 231 return window.YAHOO; 232 }, 233 'YUI 3': function() { 234 return window.YUI; 235 }, 236 'jQuery': function() { 237 return window.jQuery; 238 }, 239 'jQuery UI': function() { 240 return window.jQuery && window.jQuery.ui; 241 }, 242 'Typekit': function() { 243 return window.Typekit; 244 }, 245 'Facebook': function() { 246 return window.FB && window.FB.api; 247 }, 248 'ExtJS': function() { 249 return window.Ext; 250 }, 251 'Modernizr': function() { 252 return window.Modernizr; 253 }, 254 'Raphael': function() { 255 return window.Raphael; 256 }, 257 'Cufon': function() { 258 return window.Cufon; 259 }, 260 'sIFR': function() { 261 return window.sIFR; 262 }, 263 'Xiti': function() { 264 return window.xtsite && window.xtpage; 265 }, 266 'Piwik': function() { 267 return window.Piwik; 268 }, 269 'IPB': function() { 270 return window.IPBoard; 271 }, 272 'MyBB': function() { 273 return window.MyBB; 274 }, 275 'Clicky': function() { 276 return window.clicky; 277 }, 278 'Woopra': function() { 279 return window.woopraTracker; 280 }, 281 'RightJS': function() { 282 return window.RightJS; 283 }, 284 'OpenWebAnalytics': function() { 285 return window.owa_baseUrl; 286 }, 287 'Prettify': function() { 288 return window.prettyPrint; 289 }, 290 'SiteCatalyst': function() { 291 return window.s_account; 292 }, 293 'Twitter': function() { 294 return window.twttr; 295 }, 296 'Coremetrics': function() { 297 return window.cmCreatePageviewTag; 298 }, 299 'Buzz': function() { 300 return window.google_buzz__base_url; 301 }, 302 'Plus1': function() { 303 return window.gapi && window.gapi.plusone; 304 }, 305 'Google Loader': function() { 306 return window.google && window.google.load; 307 }, 308 'GoogleMapApi': function() { 309 return window.google && window.google.maps; 310 }, 311 'Head JS': function() { 312 return window.head && window.head.js; 313 }, 314 'SWFObject': function() { 315 return window.swfobject; 316 }, 317 'Chitika': function() { 318 return window.ch_client && window.ch_write_iframe; 319 }, 320 'Jimdo': function() { 321 return window.jimdoData; 322 }, 323 'Webs': function() { 324 return window.webs; 325 }, 326 'Backbone.js': function() { 327 return window.Backbone && typeof(window.Backbone.sync) === 'function'; 328 }, 329 'Underscore.js': function() { 330 return window._ && typeof(window._.identity) === 'function' && 331 window._.identity('abc') === 'abc'; 332 }, 333 'Spine': function() { 334 return window.Spine; 335 }, 336 'Angular': function () { 337 return window.angular; 338 }, 339 'Ning': function () { 340 return window.ning; 341 }, 342 'ektron': function () { 343 return window.Ektron; 344 }, 345 'etracker': function () { 346 return window.et_params; 347 }, 348 'SPDY': function () { 349 return window.chrome.loadTimes().wasFetchedViaSpdy; 350 }, 351 'LiveStreet': function () { 352 return window.LIVESTREET_SECURITY_KEY; 353 } 354 }; 355 356 for (var t in js_tests) { 357 if (t in _apps) continue; 358 if (js_tests[t]()) { 359 _apps[t] = -1; 360 } 361 } 362 363 // 6: detect some script version when available 364 // 判断 javascript 版本 365 var js_versions = { 366 'Highcharts': function() { 367 if ('Highcharts' in window && Highcharts.version !== undefined) 368 return window.Highcharts.version; 369 }, 370 'Prototype': function() { 371 if ('Prototype' in window && Prototype.Version !== undefined) 372 return window.Prototype.Version; 373 }, 374 'script.aculo.us': function() { 375 if ('Scriptaculous' in window && Scriptaculous.Version !== undefined) 376 return window.Scriptaculous.Version; 377 }, 378 'jQuery': function() { 379 if (typeof jQuery === 'function' && jQuery.prototype.jquery !== undefined) 380 return jQuery.prototype.jquery; 381 }, 382 'jQuery UI': function() { 383 if (typeof jQuery === 'function' && jQuery.ui && jQuery.ui.version !== undefined) 384 return jQuery.ui.version; 385 }, 386 'Dojo': function() { 387 if (typeof dojo === 'object' && dojo.version.toString() !== undefined) 388 return dojo.version; 389 }, 390 'YUI': function() { 391 if (typeof YAHOO === 'object' && YAHOO.VERSION !== undefined) 392 return YAHOO.VERSION; 393 }, 394 'YUI 3': function() { 395 if ('YUI' in window && typeof YUI === 'function' && YUI().version !== undefined) 396 return YUI().version; 397 }, 398 'MooTools': function() { 399 if (typeof MooTools === 'object' && MooTools.version !== undefined) 400 return MooTools.version; 401 }, 402 'ExtJS': function() { 403 if (typeof Ext === 'object' && Ext.version !== undefined) 404 return Ext.version; 405 }, 406 'RightJS': function() { 407 if ('RightJS' in window && RightJS.version !== undefined) 408 return RightJS.version; 409 }, 410 'Modernizr': function() { 411 if (window.Modernizr && Modernizr._version !== undefined) 412 return Modernizr._version; 413 }, 414 'Raphael': function() { 415 if (window.Raphael && Raphael.version !== undefined) 416 return Raphael.version; 417 }, 418 'Backbone.js': function() { 419 if (window.Backbone && window.Backbone.VERSION) 420 return window.Backbone.VERSION; 421 }, 422 'Underscore.js': function() { 423 if (window._ && window._.VERSION) 424 return window._.VERSION; 425 }, 426 'Spine': function() { 427 if(window.Spine && window.Spine.version) 428 return window.Spine.version; 429 }, 430 'Angular': function () { 431 if (window.angular && window.angular.version && 'full' in window.angular.version) 432 return window.angular.version.full; 433 } 434 }; 435 436 for (var a in _apps) { 437 if (_apps[a] === -1 && js_versions[a]) { 438 r = js_versions[a](); 439 _apps[a] = r ? r : -1; 440 } 441 } 442 443 // 7: detect by header 444 // 根据 header 信息判断 445 // @todo 446 447 // 8: detect based on built-in database 448 // 根据内建的数据库判断 449 // @todo 450 451 // 9: detect based on defined css classes 452 // 根据 css 类判断 453 var cssClasses = { 454 'Bootstrap': ['hero-unit', '.carousel-control', '[class^="icon-"]:last-child'] 455 }; 456 457 for (var t in cssClasses) { 458 if (t in _apps) continue; 459 460 var found = true; 461 for (var css in cssClasses[t]) { 462 var act = false; 463 name = cssClasses[t][css]; 464 465 /* Iterate through all registered css classes and check for presence */ 466 for (var cssFile in document.styleSheets) { 467 for (var cssRule in document.styleSheets[cssFile].cssRules) { 468 var style = document.styleSheets[cssFile].cssRules[cssRule]; 469 470 if (typeof style === "undefined") continue; 471 if (typeof style.selectorText === "undefined") continue; 472 473 if (style.selectorText.indexOf(name) !== -1) { 474 act = true; 475 break; 476 } 477 } 478 if (act === true) break; 479 } 480 481 found = found & act; 482 } 483 484 if (found === true) { 485 _apps[t] = -1; 486 } 487 else { 488 break; 489 } 490 } 491 492 // convert to array 493 var jsonString = JSON.stringify(_apps); 494 // send back to background page 495 var meta = document.getElementById('chromesniffer_meta'); 496 meta.content = jsonString; 497 498 //Notify Background Page 499 var done = document.createEvent('Event'); 500 done.initEvent('ready', true, true); 501 meta.dispatchEvent(done); 502})();