diff --git a/src/drivers/webextension/js/content.js b/src/drivers/webextension/js/content.js index 63e387b51..20ef73230 100644 --- a/src/drivers/webextension/js/content.js +++ b/src/drivers/webextension/js/content.js @@ -2,20 +2,20 @@ /* eslint-env browser */ /* globals chrome */ -function getJs(technologies) { +function inject(src, id, message) { return new Promise((resolve) => { // Inject a script tag into the page to access methods of the window object const script = document.createElement('script') script.onload = () => { const onMessage = ({ data }) => { - if (!data.wappalyzer || !data.wappalyzer.js) { + if (!data.wappalyzer || !data.wappalyzer[id]) { return } window.removeEventListener('message', onMessage) - resolve(data.wappalyzer.js) + resolve(data.wappalyzer[id]) script.remove() } @@ -23,25 +23,38 @@ function getJs(technologies) { window.addEventListener('message', onMessage) window.postMessage({ - wappalyzer: { - technologies: technologies - .filter(({ js }) => Object.keys(js).length) - .map(({ name, js }) => ({ name, chains: Object.keys(js) })), - }, + wappalyzer: message, }) } - script.setAttribute('src', chrome.runtime.getURL('js/inject.js')) + script.setAttribute('src', chrome.runtime.getURL(src)) document.body.appendChild(script) }) } -function getDom(technologies) { - return technologies +function getJs(technologies) { + return inject('js/js.js', 'js', { + technologies: technologies + .filter(({ js }) => Object.keys(js).length) + .map(({ name, js }) => ({ name, chains: Object.keys(js) })), + }) +} + +async function getDom(technologies) { + const _technologies = technologies .filter(({ dom }) => dom && dom.constructor === Object) .map(({ name, dom }) => ({ name, dom })) - .reduce((technologies, { name, dom }) => { + + return [ + ...(await inject('js/dom.js', 'dom', { + technologies: _technologies.filter(({ dom }) => + Object.values(dom) + .flat() + .some(({ properties }) => properties) + ), + })), + ..._technologies.reduce((technologies, { name, dom }) => { const toScalar = (value) => typeof value === 'string' || typeof value === 'number' ? value : !!value @@ -58,7 +71,7 @@ function getDom(technologies) { return } - dom[selector].forEach(({ exists, text, properties, attributes }) => { + dom[selector].forEach(({ exists, text, attributes }) => { nodes.forEach((node) => { if (exists) { technologies.push({ @@ -80,23 +93,6 @@ function getDom(technologies) { } } - if (properties) { - Object.keys(properties).forEach((property) => { - if (Object.prototype.hasOwnProperty.call(node, property)) { - const value = node[property] - - if (typeof value !== 'undefined') { - technologies.push({ - name, - selector, - property, - value: toScalar(value), - }) - } - } - }) - } - if (attributes) { Object.keys(attributes).forEach((attribute) => { if (node.hasAttribute(attribute)) { @@ -116,7 +112,8 @@ function getDom(technologies) { }) return technologies - }, []) + }, []), + ] } const Content = { @@ -353,7 +350,7 @@ const Content = { const url = location.href const js = await getJs(technologies) - const dom = getDom(technologies) + const dom = await getDom(technologies) await Promise.all([ Content.driver('analyzeJs', [url, js, requires]), diff --git a/src/drivers/webextension/js/dom.js b/src/drivers/webextension/js/dom.js new file mode 100644 index 000000000..0c1ab6535 --- /dev/null +++ b/src/drivers/webextension/js/dom.js @@ -0,0 +1,67 @@ +/* eslint-env browser */ + +;(function () { + try { + const onMessage = ({ data }) => { + if (!data.wappalyzer || !data.wappalyzer.technologies) { + return + } + + const { technologies } = data.wappalyzer + + const toScalar = (value) => + typeof value === 'string' || typeof value === 'number' ? value : !!value + + removeEventListener('message', onMessage) + + postMessage({ + wappalyzer: { + dom: technologies.reduce((technologies, { name, dom }) => { + Object.keys(dom).forEach((selector) => { + let nodes = [] + + try { + nodes = document.querySelectorAll(selector) + } catch (error) { + // Continue + } + + if (!nodes.length) { + return + } + + nodes.forEach((node) => { + dom[selector].forEach(({ properties }) => { + if (properties) { + Object.keys(properties).forEach((property) => { + if ( + Object.prototype.hasOwnProperty.call(node, property) + ) { + const value = node[property] + + if (typeof value !== 'undefined') { + technologies.push({ + name, + selector, + property, + value: toScalar(value), + }) + } + } + }) + } + }) + }) + }) + + return technologies + }, []), + }, + }) + } + + addEventListener('message', onMessage) + } catch (e) { + // Fail quietly + } +})() diff --git a/src/drivers/webextension/js/inject.js b/src/drivers/webextension/js/js.js similarity index 100% rename from src/drivers/webextension/js/inject.js rename to src/drivers/webextension/js/js.js diff --git a/src/drivers/webextension/manifest.json b/src/drivers/webextension/manifest.json index b3a05166f..09761d1a2 100644 --- a/src/drivers/webextension/manifest.json +++ b/src/drivers/webextension/manifest.json @@ -60,7 +60,8 @@ } ], "web_accessible_resources": [ - "js/inject.js" + "js/js.js", + "js/dom.js" ], "options_ui": { "page": "html/options.html", @@ -76,4 +77,4 @@ "https://*/*" ], "content_security_policy": "script-src 'self'; object-src 'self'" -} \ No newline at end of file +} diff --git a/src/technologies/a.json b/src/technologies/a.json index aa2279399..3f449b2e7 100644 --- a/src/technologies/a.json +++ b/src/technologies/a.json @@ -2751,16 +2751,14 @@ "azuredns-cloud\\.net" ] }, - "xhr": { - "x-ms-gateway-requestid": "", - "x-ms-client-request-id": "", - "x-ms-correlation-request-id": "" - }, "headers": { "azure-regionname": "", "azure-sitename": "", "azure-slotname": "", "azure-version": "", + "x-ms-gateway-requestid": "", + "x-ms-client-request-id": "", + "x-ms-correlation-request-id": "", "server": "^Windows-Azure" }, "icon": "azure.svg",