From 66c703c2fab130655e743f752b6ab0ee3ab8777d Mon Sep 17 00:00:00 2001 From: Elbert Alias Date: Sun, 17 Dec 2017 14:18:09 +1100 Subject: [PATCH] Refactoring, allow multiple expressions per js field, remove env detection --- src/apps.json | 3 + src/drivers/webextension/js/content.js | 84 ++++++++++---------------- src/drivers/webextension/js/driver.js | 7 ++- src/drivers/webextension/js/inject.js | 74 +++++++++++------------ src/wappalyzer.js | 39 ++++++------ 5 files changed, 96 insertions(+), 111 deletions(-) diff --git a/src/apps.json b/src/apps.json index e7f81c8e4..6b3c4cac6 100644 --- a/src/apps.json +++ b/src/apps.json @@ -10436,6 +10436,9 @@ "cats": [ "12" ], + "js": { + "jQuery.fn.jquery": "^([\\d.]+)\\;version:\\1" + }, "env": "^jQuery$", "icon": "jQuery.svg", "script": [ diff --git a/src/drivers/webextension/js/content.js b/src/drivers/webextension/js/content.js index df11d725b..b24ff133f 100644 --- a/src/drivers/webextension/js/content.js +++ b/src/drivers/webextension/js/content.js @@ -1,76 +1,58 @@ /** global: browser */ if ( typeof browser !== 'undefined' && typeof document.body !== 'undefined' ) { - var html = document.documentElement.outerHTML; + try { + var html = document.documentElement.outerHTML; - if ( html.length > 50000 ) { - html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length); - } + if ( html.length > 50000 ) { + html = html.substring(0, 25000) + html.substring(html.length - 25000, html.length); + } - var scripts = Array.prototype.slice + const scripts = Array.prototype.slice .apply(document.scripts) - .filter(s => s.src) - .map(s => s.src); + .filter(script => script.src) + .map(script => script.src); - try { browser.runtime.sendMessage({ id: 'analyze', - subject: { html }, + subject: { html, scripts }, source: 'content.js' }); - browser.runtime.sendMessage({ - id: 'analyze', - subject: { scripts }, - source: 'content.js' - }); - - var container = document.createElement('wappalyzerData'); - - container.setAttribute('id', 'wappalyzerData'); - container.setAttribute('style', 'display: none'); - - var script = document.createElement('script'); - - script.setAttribute('id', 'wappalyzerEnvDetection'); - script.setAttribute('src', browser.extension.getURL('js/inject.js')); + const script = document.createElement('script'); - container.addEventListener('wappalyzerEnvEvent', (event => { - browser.runtime.sendMessage({ - id: 'JS_ready', - subject: { }, - source: 'content.js' - }, response => { - window.postMessage({patterns: response.patterns}, "*"); - }); - window.addEventListener('message', (event => { - if (event.data.js === undefined) + script.onload = () => { + addEventListener('message', event => { + if ( event.data.id !== 'js' ) { return; - var js = event.data.js ; + } + browser.runtime.sendMessage({ id: 'analyze', - subject: { js }, + subject: { + js: event.data.js + }, source: 'content.js' }); - }), true); - - var env = event.target.childNodes[0].nodeValue; + }, true); - document.documentElement.removeChild(container); - document.documentElement.removeChild(script); - - env = env.split(' ').slice(0, 500); - - browser.runtime.sendMessage({ - id: 'analyze', - subject: { env }, + ( chrome || browser ).runtime.sendMessage({ + id: 'init_js', + subject: {}, source: 'content.js' + }, response => { + postMessage({ + id: 'patterns', + patterns: response.patterns + }, '*'); }); - }), true); + }; + + script.setAttribute('id', 'wappalyzer'); + script.setAttribute('src', browser.extension.getURL('js/inject.js')); - document.documentElement.appendChild(container); - document.documentElement.appendChild(script); - } catch(e) { + document.body.appendChild(script); + } catch (e) { log(e); } } diff --git a/src/drivers/webextension/js/driver.js b/src/drivers/webextension/js/driver.js index 700aaf39b..52b393875 100644 --- a/src/drivers/webextension/js/driver.js +++ b/src/drivers/webextension/js/driver.js @@ -74,7 +74,8 @@ fetch('../apps.json') .then(json => { wappalyzer.apps = json.apps; wappalyzer.categories = json.categories; - wappalyzer.parseJS(); + + wappalyzer.parseJsPatterns(); categoryOrder = Object.keys(wappalyzer.categories).sort((a, b) => wappalyzer.categories[a].priority - wappalyzer.categories[b].priority); }) @@ -193,9 +194,9 @@ browser.webRequest.onCompleted.addListener(request => { }; break; - case 'JS_ready': + case 'init_js': response = { - patterns: wappalyzer.parsedJS + patterns: wappalyzer.jsPatterns }; break; diff --git a/src/drivers/webextension/js/inject.js b/src/drivers/webextension/js/inject.js index 26ac3d6eb..a105360c4 100644 --- a/src/drivers/webextension/js/inject.js +++ b/src/drivers/webextension/js/inject.js @@ -1,43 +1,43 @@ (function() { try { - var i, environmentVars = '', eEnv = document.createEvent('Events'), container = document.getElementById('wappalyzerData'); - - eEnv.initEvent('wappalyzerEnvEvent', true, false); - - for ( i in window ) { - environmentVars += i + ' '; - } - container.appendChild(document.createComment(environmentVars)); - container.dispatchEvent(eEnv); - - window.addEventListener('message', (event => { - if (event.data.patterns === undefined) - return; - var properties = event.data.patterns; - var js = {}; - Object.keys(properties).forEach(appname => { - Object.keys(properties[appname]).forEach(property => { - var content = false; - if( content = JSdetection(property) ){ - if ( js[appname] === undefined ) - js[appname] = {}; - js[appname][property] = properties[appname][property]; - js[appname][property]["content"] = content; - } - }); - }); - window.postMessage({js: js}, "*"); - }), false); - } catch(e) { - // Fail quietly - } + addEventListener('message', (event => { + if ( event.data.id !== 'patterns' ) { + return; + } + + const patterns = event.data.patterns || {}; + + const js = {}; + + Object.keys(patterns).forEach(appName => { + js[appName] = {}; + + Object.keys(patterns[appName]).forEach(chain => { + js[appName][chain] = {}; + + patterns[appName][chain].forEach((pattern, index) => { + const value = detect(chain); + + if ( value ) { + js[appName][chain][index] = value; + } + }); + }); + }); + + postMessage({ id: 'js', js }, '*'); + }), false); + } catch(e) { + // Fail quietly + } }()); -function JSdetection(p){ - const objects = p.split('.'); - const value = objects.reduce((parent, property) => { - return parent && parent.hasOwnProperty(property) ? parent[property] : null; - }, window); +function detect(chain) { + const properties = chain.split('.'); + + const value = properties.reduce((parent, property) => { + return parent && parent.hasOwnProperty(property) ? parent[property] : null; + }, window); - return typeof value === 'string' ? value : !!value; + return typeof value === 'string' ? value : !!value; } diff --git a/src/wappalyzer.js b/src/wappalyzer.js index 74fd66393..e85ae576d 100644 --- a/src/wappalyzer.js +++ b/src/wappalyzer.js @@ -18,7 +18,7 @@ class Wappalyzer { this.apps = {}; this.categories = {}; this.driver = {}; - this.parsedJS = undefined; + this.jsPatterns = {}; this.detected = {}; this.hostnameCache = {}; @@ -85,7 +85,7 @@ class Wappalyzer { if ( data.js ) { Object.keys(data.js).forEach(appName => { - this.analyzeJS(apps[appName], data.js[appName]); + this.analyzeJs(apps[appName], data.js[appName]); }); } @@ -252,16 +252,14 @@ class Wappalyzer { } /** - * Parse JS patterns + * Parse JavaScript patterns */ - parseJS() { - if ( this.parsedJS === undefined){ - this.parsedJS = {}; - Object.keys(this.apps).forEach(appName => { - if (this.apps[appName].js) - this.parsedJS[appName] = this.parsePatterns(this.apps[appName].js); - }); - } + parseJsPatterns() { + Object.keys(this.apps).forEach(appName => { + if ( this.apps[appName].js ) { + this.jsPatterns[appName] = this.parsePatterns(this.apps[appName].js); + } + }); } resolveExcludes(apps) { @@ -508,16 +506,17 @@ class Wappalyzer { } /** - * Analyze JS variables + * Analyze JavaScript variables */ - analyzeJS(app, js) { - Object.keys(js).forEach(property => { - var content = js[property]["content"]; - delete js[property]["content"]; - js[property].forEach(pattern => { - if ( pattern.regex.test(content) ) { - this.addDetected(app, pattern, 'js', content, property); - } + analyzeJs(app, results) { + Object.keys(results).forEach(string => { + Object.keys(results[string]).forEach(index => { + const pattern = this.jsPatterns[app.name][string][index]; + const value = results[string][index]; + + if ( pattern.regex.test(value) ) { + this.addDetected(app, pattern, 'js', value); + } }); }); }