Add short DOM notation

main
Elbert Alias 4 years ago
parent 5270da9536
commit 35090b3606

@ -66,6 +66,7 @@ Patterns (regular expressions) are kept in [`src/technologies.json`](https://git
}, },
"dom": { "dom": {
"#example-id": { "#example-id": {
"exists": "",
"attributes": { "attributes": {
"class": "example-class" "class": "example-class"
}, },
@ -277,7 +278,7 @@ Plus any of:
</tr> </tr>
<tr> <tr>
<td><code>dom</code></td> <td><code>dom</code></td>
<td>Object</td> <td>String | Array | Object</td>
<td> <td>
Uses a Uses a
<a <a

@ -87,12 +87,25 @@
} }
}, },
"dom": { "dom": {
"oneOf": [
{
"type": "array",
"items": {
"$ref": "#/definitions/non-empty-non-blank-string"
}
},
{
"$ref": "#/definitions/non-empty-non-blank-string"
},
{
"type": "object", "type": "object",
"additionalProperties": false, "additionalProperties": false,
"patternProperties": { "patternProperties": {
"^.+$": { "^.+$": {
} }
} }
}
]
}, },
"dns": { "dns": {
"type": "object", "type": "object",

@ -73,22 +73,30 @@ function analyzeJs(js) {
function analyzeDom(dom) { function analyzeDom(dom) {
return Array.prototype.concat.apply( return Array.prototype.concat.apply(
[], [],
dom.map(({ name, selector, text, property, attribute, value }) => { dom.map(({ name, selector, exists, text, property, attribute, value }) => {
const technology = Wappalyzer.technologies.find( const technology = Wappalyzer.technologies.find(
({ name: _name }) => name === _name ({ name: _name }) => name === _name
) )
if (text) { if (typeof exists !== 'undefined') {
return analyzeManyToMany(technology, 'dom.text', { [selector]: [text] }) return analyzeManyToMany(technology, 'dom.exists', {
[selector]: [''],
})
}
if (typeof text !== 'undefined') {
return analyzeManyToMany(technology, 'dom.text', {
[selector]: [text],
})
} }
if (property) { if (typeof property !== 'undefined') {
return analyzeManyToMany(technology, `dom.properties.${property}`, { return analyzeManyToMany(technology, `dom.properties.${property}`, {
[selector]: [value], [selector]: [value],
}) })
} }
if (attribute) { if (typeof attribute !== 'undefined') {
return analyzeManyToMany(technology, `dom.attributes.${attribute}`, { return analyzeManyToMany(technology, `dom.attributes.${attribute}`, {
[selector]: [value], [selector]: [value],
}) })
@ -424,7 +432,7 @@ class Site {
await sleep(1000) await sleep(1000)
// page.on('console', (message) => this.log(message.text())) page.on('console', (message) => this.log(message.text())) // TODO
// Links // Links
const links = await this.promiseTimeout( const links = await this.promiseTimeout(
@ -560,14 +568,29 @@ class Site {
: !!value : !!value
Object.keys(dom).forEach((selector) => { Object.keys(dom).forEach((selector) => {
const nodes = document.querySelectorAll(selector) let nodes = []
try {
nodes = document.querySelectorAll(selector)
} catch (error) {
// Continue
}
if (!nodes.length) { if (!nodes.length) {
return return
} }
dom[selector].forEach(({ text, properties, attributes }) => { dom[selector].forEach(
({ exists, text, properties, attributes }) => {
nodes.forEach((node) => { nodes.forEach((node) => {
if (exists) {
technologies.push({
name,
selector,
exists: '',
})
}
if (text) { if (text) {
const value = node.textContent.trim() const value = node.textContent.trim()
@ -614,14 +637,15 @@ class Site {
}) })
} }
}) })
}) }
)
}) })
return technologies return technologies
}, []) }, [])
}, },
Wappalyzer.technologies Wappalyzer.technologies
.filter(({ dom }) => dom) .filter(({ dom }) => dom && dom.constructor === Object)
.map(({ name, dom }) => ({ name, dom })) .map(({ name, dom }) => ({ name, dom }))
) )
).catch(() => []) ).catch(() => [])

@ -0,0 +1 @@
<svg viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg"><g fill="#e10098"><path d="m57.468 302.66-14.376-8.3 160.15-277.38 14.376 8.3z"/><path d="m39.8 272.2h320.3v16.6h-320.3z"/><path d="m206.348 374.026-160.21-92.5 8.3-14.376 160.21 92.5zm139.174-241.079-160.21-92.5 8.3-14.376 160.21 92.5z"/><path d="m54.482 132.883-8.3-14.375 160.21-92.5 8.3 14.376z"/><path d="m342.568 302.663-160.15-277.38 14.376-8.3 160.15 277.38zm-290.068-195.163h16.6v185h-16.6zm278.4 0h16.6v185h-16.6z"/><path d="m203.522 367-7.25-12.558 139.34-80.45 7.25 12.557z"/><path d="m369.5 297.9c-9.6 16.7-31 22.4-47.7 12.8s-22.4-31-12.8-47.7 31-22.4 47.7-12.8c16.8 9.7 22.5 31 12.8 47.7m-278.6-160.9c-9.6 16.7-31 22.4-47.7 12.8s-22.4-31-12.8-47.7 31-22.4 47.7-12.8c16.7 9.7 22.4 31 12.8 47.7m-60.4 160.9c-9.6-16.7-3.9-38 12.8-47.7 16.7-9.6 38-3.9 47.7 12.8 9.6 16.7 3.9 38-12.8 47.7-16.8 9.6-38.1 3.9-47.7-12.8m278.6-160.9c-9.6-16.7-3.9-38 12.8-47.7 16.7-9.6 38-3.9 47.7 12.8 9.6 16.7 3.9 38-12.8 47.7-16.7 9.6-38.1 3.9-47.7-12.8m-109.1 258.8c-19.3 0-34.9-15.6-34.9-34.9s15.6-34.9 34.9-34.9 34.9 15.6 34.9 34.9c0 19.2-15.6 34.9-34.9 34.9m0-321.8c-19.3 0-34.9-15.6-34.9-34.9s15.6-34.9 34.9-34.9 34.9 15.6 34.9 34.9-15.6 34.9-34.9 34.9"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -115,7 +115,11 @@ const Content = {
}, },
(response) => { (response) => {
chrome.runtime.lastError chrome.runtime.lastError
? reject(new Error(chrome.runtime.lastError.message)) ? reject(
new Error(
`${chrome.runtime.lastError}: Driver.${func}(${args})`
)
)
: resolve(response) : resolve(response)
} }
) )
@ -164,7 +168,7 @@ const Content = {
// DOM // DOM
const dom = technologies const dom = technologies
.filter(({ dom }) => dom) .filter(({ dom }) => dom && dom.constructor === Object)
.map(({ name, dom }) => ({ name, dom })) .map(({ name, dom }) => ({ name, dom }))
.reduce((technologies, { name, dom }) => { .reduce((technologies, { name, dom }) => {
const toScalar = (value) => const toScalar = (value) =>
@ -173,14 +177,29 @@ const Content = {
: !!value : !!value
Object.keys(dom).forEach((selector) => { Object.keys(dom).forEach((selector) => {
const nodes = document.querySelectorAll(selector) let nodes = []
try {
nodes = document.querySelectorAll(selector)
} catch (error) {
// eslint-disable-next-line no-console
console.error(error)
}
if (!nodes.length) { if (!nodes.length) {
return return
} }
dom[selector].forEach(({ text, properties, attributes }) => { dom[selector].forEach(({ exists, text, properties, attributes }) => {
nodes.forEach((node) => { nodes.forEach((node) => {
if (exists) {
technologies.push({
name,
selector,
exists: '',
})
}
if (text) { if (text) {
const value = node.textContent.trim() const value = node.textContent.trim()

@ -186,24 +186,38 @@ const Driver = {
url, url,
Array.prototype.concat.apply( Array.prototype.concat.apply(
[], [],
dom.map(({ name, selector, text, property, attribute, value }) => { dom.map(
(
{ name, selector, exists, text, property, attribute, value },
index
) => {
const technology = Wappalyzer.technologies.find( const technology = Wappalyzer.technologies.find(
({ name: _name }) => name === _name ({ name: _name }) => name === _name
) )
if (text) { if (typeof exists !== 'undefined') {
return analyzeManyToMany(technology, 'dom.exists', {
[selector]: [''],
})
}
if (typeof text !== 'undefined') {
return analyzeManyToMany(technology, 'dom.text', { return analyzeManyToMany(technology, 'dom.text', {
[selector]: [text], [selector]: [text],
}) })
} }
if (property) { if (typeof property !== 'undefined') {
return analyzeManyToMany(technology, `dom.properties.${property}`, { return analyzeManyToMany(
technology,
`dom.properties.${property}`,
{
[selector]: [value], [selector]: [value],
}) }
)
} }
if (attribute) { if (typeof attribute !== 'undefined') {
return analyzeManyToMany( return analyzeManyToMany(
technology, technology,
`dom.attributes.${attribute}`, `dom.attributes.${attribute}`,
@ -214,7 +228,8 @@ const Driver = {
} }
return [] return []
}) }
)
) )
) )
}, },
@ -263,8 +278,6 @@ const Driver = {
* @param {Object} request * @param {Object} request
*/ */
async onWebRequestComplete(request) { async onWebRequestComplete(request) {
console.log('xxx', request.responseHeaders)
if (request.responseHeaders) { if (request.responseHeaders) {
if (await Driver.isDisabledDomain(request.url)) { if (await Driver.isDisabledDomain(request.url)) {
return return

@ -320,18 +320,7 @@
36 36
], ],
"description": "33Across is a technology company focused on solving the challenge of consumer attention for automated advertising.", "description": "33Across is a technology company focused on solving the challenge of consumer attention for automated advertising.",
"dom": { "dom": "iframe[src*='.33across.com'], link[href*='.33across.com']",
"iframe[src*='.33across.com]": {
"attributes": {
"src": ""
}
},
"link[href*='.33across.com']": {
"attributes": {
"href": ""
}
}
},
"icon": "33Across.png", "icon": "33Across.png",
"saas": true, "saas": true,
"website": "https://www.33across.com", "website": "https://www.33across.com",
@ -401,13 +390,7 @@
71 71
], ],
"description": " A8.net is an affiliate marketing network.", "description": " A8.net is an affiliate marketing network.",
"dom": { "dom": "img[src*='.a8.net']",
"img[src*='.a8.net']": {
"attributes": {
"src": "\\.a8\\.net"
}
}
},
"icon": "A8.net.png", "icon": "A8.net.png",
"js": { "js": {
"A8salesCookieRepository": "", "A8salesCookieRepository": "",
@ -535,13 +518,7 @@
6 6
], ],
"description": "ATSHOP is an all-in-one ecommerce platform.", "description": "ATSHOP is an all-in-one ecommerce platform.",
"dom": { "dom": "link[href*='cdn.atshop.io']",
"link[href*='cdn.atshop.io']": {
"attributes": {
"href": ""
}
}
},
"icon": "ATSHOP.png", "icon": "ATSHOP.png",
"pricing": [ "pricing": [
"low", "low",
@ -618,13 +595,7 @@
71 71
], ],
"description": "AccessTrade is an affiliate marketing platform based on the CPA model developed by Interspace Co.", "description": "AccessTrade is an affiliate marketing platform based on the CPA model developed by Interspace Co.",
"dom": { "dom": "img[src*='.accesstrade.net'],img[data-src*='.accesstrade.net']",
"img[src*='.accesstrade.net'],img[data-src*='.accesstrade.net']": {
"attributes": {
"src": ""
}
}
},
"icon": "AccessTrade.png", "icon": "AccessTrade.png",
"website": "https://www.accesstrade.ne.jp" "website": "https://www.accesstrade.ne.jp"
}, },
@ -664,14 +635,7 @@
10 10
], ],
"description": "Ackee is a self-hosted, Node.js based analytics tool with a focus on privacy.", "description": "Ackee is a self-hosted, Node.js based analytics tool with a focus on privacy.",
"dom": { "dom": "[data-ackee-domain-id], [data-ackee-server]",
"[data-ackee-domain-id]": {
"attributes": {
"data-ackee-domain-id": "",
"data-ackee-server": ""
}
}
},
"icon": "Ackee.png", "icon": "Ackee.png",
"js": { "js": {
"ackeeTracker": "" "ackeeTracker": ""
@ -1166,13 +1130,7 @@
36 36
], ],
"description": "Affiliate B is an advertising system that allows site operators (HP, blogs, e-mail newsletters, etc.) to place advertiser advertisements on their own sites.", "description": "Affiliate B is an advertising system that allows site operators (HP, blogs, e-mail newsletters, etc.) to place advertiser advertisements on their own sites.",
"dom": { "dom": "img[src*='www.afi-b.com']",
"img[src*='www.afi-b.com']": {
"attributes": {
"src": "www\\.afi-b\\.com"
}
}
},
"icon": "Affiliate B.svg", "icon": "Affiliate B.svg",
"scripts": "t\\.afi-b\\.com", "scripts": "t\\.afi-b\\.com",
"website": "https://affiliate-b.com" "website": "https://affiliate-b.com"
@ -1182,13 +1140,7 @@
71 71
], ],
"description": "Affiliate Future is a provider of advertisers with marketing solution through its affiliate network and tools.", "description": "Affiliate Future is a provider of advertisers with marketing solution through its affiliate network and tools.",
"dom": { "dom": "img[src*='banners.affiliatefuture.com']",
"img[src*='banners.affiliatefuture.com']": {
"attributes": {
"src": "banners\\.affiliatefuture\\.com"
}
}
},
"icon": "Affiliate Future.png", "icon": "Affiliate Future.png",
"scripts": "tags\\.affiliatefuture\\.com", "scripts": "tags\\.affiliatefuture\\.com",
"website": "http://affiliatefuture.com" "website": "http://affiliatefuture.com"
@ -1569,7 +1521,7 @@
}, },
"a[href*='amzn.to']": { "a[href*='amzn.to']": {
"attributes": { "attributes": {
"href": "^https?://amzn.to" "href": ""
} }
} }
}, },
@ -1640,11 +1592,7 @@
41 41
], ],
"description": "Amazon Pay is an online payments processing service that is owned by Amazon. It lets you use the payment methods associated with your Amazon account to make payments for goods and services.", "description": "Amazon Pay is an online payments processing service that is owned by Amazon. It lets you use the payment methods associated with your Amazon account to make payments for goods and services.",
"dom": { "dom": "img[src*='amazonpay']",
"img[src*='amazonpay']": {
"text": ""
}
},
"icon": "Amazon Pay.svg", "icon": "Amazon Pay.svg",
"js": { "js": {
"AmazonPayments": "", "AmazonPayments": "",
@ -1915,18 +1863,7 @@
36 36
], ],
"description": "AnyClip is a video engagement platform that uses an AI-driven content analysis engine to analyze and categorize video content in real-time to create personalized video feeds.", "description": "AnyClip is a video engagement platform that uses an AI-driven content analysis engine to analyze and categorize video content in real-time to create personalized video feeds.",
"dom": { "dom": "img[src*='.anyclip.com'], video[poster*='.anyclip.com']",
"img[src*='.anyclip.com']": {
"attributes": {
"src": ""
}
},
"video[poster*='.anyclip.com']": {
"attributes": {
"poster": ""
}
}
},
"icon": "AnyClip.svg", "icon": "AnyClip.svg",
"js": { "js": {
"anyclip": "" "anyclip": ""
@ -2076,18 +2013,7 @@
36 36
], ],
"description": "AppNexus is a cloud-based software platform that enables and optimizes programmatic online advertising.", "description": "AppNexus is a cloud-based software platform that enables and optimizes programmatic online advertising.",
"dom": { "dom": "iframe[src*='.adnxs.com'], img[src*='.adnxs.com']",
"iframe[src*='.adnxs.com']": {
"attributes": {
"src": ""
}
},
"img[src*='.adnxs.com']": {
"attributes": {
"src": ""
}
}
},
"icon": "AppNexus.svg", "icon": "AppNexus.svg",
"js": { "js": {
"appnexus": "", "appnexus": "",
@ -2134,9 +2060,7 @@
"description": "Apple Sign-in is based on OAuth 2.0 and OpenID Connect, and provides a privacy-friendly way for users to sign in to websites and apps.", "description": "Apple Sign-in is based on OAuth 2.0 and OpenID Connect, and provides a privacy-friendly way for users to sign in to websites and apps.",
"dom": { "dom": {
"a[href*='appleid.apple.com/auth/authorize']": { "a[href*='appleid.apple.com/auth/authorize']": {
"attributes": { "exists": ""
"href": ""
}
}, },
"button": { "button": {
"text": "(Sign (in|up)|Log in|Continue) with Apple" "text": "(Sign (in|up)|Log in|Continue) with Apple"
@ -2227,13 +2151,7 @@
31 31
], ],
"description": "Arc is a peer-to-peer CDN that pays site owners for using it. Instead of expensive servers in distant datacenters, Arc's network is comprised of browsers.", "description": "Arc is a peer-to-peer CDN that pays site owners for using it. Instead of expensive servers in distant datacenters, Arc's network is comprised of browsers.",
"dom": { "dom": "#arc-widget",
"#arc-widget": {
"attributes": {
"id": ""
}
}
},
"icon": "Arc.svg", "icon": "Arc.svg",
"js": { "js": {
"arc.p2pClient": "", "arc.p2pClient": "",
@ -2348,18 +2266,7 @@
53 53
], ],
"description": "Astute Solutions is a customer engagement software.", "description": "Astute Solutions is a customer engagement software.",
"dom": { "dom": "iframe[src*='.iperceptions.com'], link[href*='.iperceptions.com']",
"iframe[src*='.iperceptions.com]": {
"attributes": {
"src": ""
}
},
"link[href*='.iperceptions.com']": {
"attributes": {
"href": ""
}
}
},
"icon": "Astute Solutions.svg", "icon": "Astute Solutions.svg",
"pricing": [ "pricing": [
"poa" "poa"
@ -2373,13 +2280,7 @@
29 29
], ],
"description": "Athena Search is a customizable autocomplete, feature-rich dashboard, smart predictions, real-time reports search engine developed from scratch by Syncit Groups.", "description": "Athena Search is a customizable autocomplete, feature-rich dashboard, smart predictions, real-time reports search engine developed from scratch by Syncit Groups.",
"dom": { "dom": "link[href*='athena/autocomplete/css/autocomplete.css']",
"link[href*='athena/autocomplete/css/autocomplete.css']": {
"attributes": {
"href": ""
}
}
},
"icon": "Athena Search.svg", "icon": "Athena Search.svg",
"pricing": [ "pricing": [
"payg" "payg"
@ -2443,11 +2344,7 @@
13 13
], ],
"cpe": "cpe:/a:atlassian:jira", "cpe": "cpe:/a:atlassian:jira",
"dom": { "dom": "#jira",
"#jira": {
"text": ""
}
},
"icon": "Atlassian Jira.svg", "icon": "Atlassian Jira.svg",
"implies": "Java", "implies": "Java",
"js": { "js": {
@ -2609,13 +2506,7 @@
75 75
], ],
"description": "AWeber is an email marketing service.", "description": "AWeber is an email marketing service.",
"dom": { "dom": "form[action*='aweber.com']",
"form[action*='aweber.com']": {
"attributes": {
"action": "www\\.aweber\\.com"
}
}
},
"icon": "Aweber.svg", "icon": "Aweber.svg",
"js": { "js": {
"awt_analytics": "" "awt_analytics": ""
@ -2896,14 +2787,7 @@
6 6
], ],
"description": "BigCommerce is a hosted ecommerce platform that allows business owners to set up an online store and sell their products online.", "description": "BigCommerce is a hosted ecommerce platform that allows business owners to set up an online store and sell their products online.",
"dom": { "dom": "img[data-src*='.bigcommerce.com'], img[src*='.bigcommerce.com']",
"img[data-src*='.bigcommerce.com'],img[src*='.bigcommerce.com']": {
"attributes": {
"data-src": "",
"src": ""
}
}
},
"html": "<link href=[^>]+cdn\\d+\\.bigcommerce\\.com/", "html": "<link href=[^>]+cdn\\d+\\.bigcommerce\\.com/",
"icon": "BigCommerce.svg", "icon": "BigCommerce.svg",
"pricing": [ "pricing": [
@ -3103,13 +2987,7 @@
77 77
], ],
"description": "Blue is a ecommerce data marketing, lead generation, real time bidding and recommendation solutions.", "description": "Blue is a ecommerce data marketing, lead generation, real time bidding and recommendation solutions.",
"dom": { "dom": "iframe[src*='.getblue.io']",
"iframe[src*='.getblue.io']": {
"attributes": {
"src": ""
}
}
},
"icon": "Blue.svg", "icon": "Blue.svg",
"js": { "js": {
"blueProductId": "", "blueProductId": "",
@ -3232,11 +3110,7 @@
41 41
], ],
"description": "Bolt powers a checkout experience designed to convert shoppers.", "description": "Bolt powers a checkout experience designed to convert shoppers.",
"dom": { "dom": "bolt-checkout-button",
"bolt-checkout-button": {
"text": ""
}
},
"icon": "Bolt.svg", "icon": "Bolt.svg",
"js": { "js": {
"BoltCheckout": "", "BoltCheckout": "",
@ -3389,11 +3263,7 @@
67 67
], ],
"description": "Borlabs Cookie is a GDPR cookie consent plugin for WordPress.", "description": "Borlabs Cookie is a GDPR cookie consent plugin for WordPress.",
"dom": { "dom": "#BorlabsCookieBox",
"#BorlabsCookieBox": {
"text": ""
}
},
"icon": "Borlabs Cookie.svg", "icon": "Borlabs Cookie.svg",
"implies": "WordPress", "implies": "WordPress",
"js": { "js": {
@ -3500,11 +3370,7 @@
41 41
], ],
"description": "Bread is a buy now, pay later platform for ecommerce websites.", "description": "Bread is a buy now, pay later platform for ecommerce websites.",
"dom": { "dom": "#bread-mini-cart-btn",
"#bread-mini-cart-btn": {
"text": ""
}
},
"icon": "Bread.svg", "icon": "Bread.svg",
"js": { "js": {
"BreadCalc": "", "BreadCalc": "",
@ -3681,11 +3547,7 @@
1 1
], ],
"description": "Builder.io is a headless CMS with a powerful drag-and-drop visual editor that lets you build and optimize digital experiences with speed and flexibility. ", "description": "Builder.io is a headless CMS with a powerful drag-and-drop visual editor that lets you build and optimize digital experiences with speed and flexibility. ",
"dom": { "dom": "[data-builder-content-id], img[src*='cdn.builder.io']",
"[data-builder-content-id], img[src*='cdn.builder.io']": {
"text": ""
}
},
"icon": "Builder.svg", "icon": "Builder.svg",
"pricing": [ "pricing": [
"low", "low",
@ -3700,13 +3562,7 @@
"cats": [ "cats": [
19 19
], ],
"dom": { "dom": "iframe[src*='buildertrend.net'], script[src*='buildertrend.net']",
"iframe[src*='buildertrend.net'], script[src*='buildertrend.net']": {
"attributes": {
"src": ""
}
}
},
"icon": "Buildertrend.svg", "icon": "Buildertrend.svg",
"pricing": [ "pricing": [
"mid", "mid",
@ -4002,11 +3858,7 @@
6 6
], ],
"description": "Websites that have a shopping cart or checkout page, either using a known ecommerce platform or a custom solution.", "description": "Websites that have a shopping cart or checkout page, either using a known ecommerce platform or a custom solution.",
"dom": { "dom": "a[href*='/cart'], a[href*='/basket'], a[href*='/trolley'], a[href*='/bag'], a[href*='/shoppingbag'], a[href*='/checkout'], [aria-controls='cart']",
"a[href*='/cart'], a[href*='/basket'], a[href*='/trolley'], a[href*='/bag'], a[href*='/shoppingbag'], a[href*='/checkout'], [aria-controls='cart']": {
"text": ""
}
},
"icon": "Cart-generic.svg", "icon": "Cart-generic.svg",
"website": "https://www.wappalyzer.com/technologies/ecommerce/cart-functionality" "website": "https://www.wappalyzer.com/technologies/ecommerce/cart-functionality"
}, },
@ -4428,14 +4280,7 @@
"cats": [ "cats": [
71 71
], ],
"dom": { "dom": "a[href*='pay.clickbank.net?cbfid'], img[width='1'][src*='hop.clickbank.net?affiliate']",
"a[href*='pay.clickbank.net?cbfid']": {
"text": ""
},
"img[width='1'][src*='hop.clickbank.net?affiliate']": {
"text": ""
}
},
"icon": "Clickbank.svg", "icon": "Clickbank.svg",
"js": { "js": {
"cbtb": "" "cbtb": ""
@ -4540,13 +4385,7 @@
35 35
], ],
"description": "ClustrMaps widget is a visitor tracker, designed for general web and blog use.", "description": "ClustrMaps widget is a visitor tracker, designed for general web and blog use.",
"dom": { "dom": "img[src*='clustrmaps.com']",
"img[src*='clustrmaps.com']": {
"attributes": {
"src": ""
}
}
},
"icon": "ClustrMaps.svg", "icon": "ClustrMaps.svg",
"scripts": "clustrmaps\\.com", "scripts": "clustrmaps\\.com",
"website": "https://clustrmaps.com/" "website": "https://clustrmaps.com/"
@ -4555,18 +4394,7 @@
"cats": [ "cats": [
19 19
], ],
"dom": { "dom": "a[href*='co-construct.com/skins'], iframe[src*='co-construct.com']",
"a[href*='co-construct.com/skins']": {
"attributes": {
"href": ""
}
},
"iframe[src*='co-construct.com']": {
"attributes": {
"src": ""
}
}
},
"icon": "CoConstruct.png", "icon": "CoConstruct.png",
"pricing": [ "pricing": [
"mid", "mid",
@ -4658,13 +4486,7 @@
41 41
], ],
"description": "Coinbase Commerce is a platform that enables merchants to accept cryptocurrency payments.", "description": "Coinbase Commerce is a platform that enables merchants to accept cryptocurrency payments.",
"dom": { "dom": "a[href^='https://commerce.coinbase.com/checkout/']",
"a[href^='https://commerce.coinbase.com/checkout/']": {
"attributes": {
"href": "^https://commerce\\.coinbase\\.com/checkout/[a-z0-9-]+$"
}
}
},
"icon": "Coinbase.svg", "icon": "Coinbase.svg",
"website": "https://commerce.coinbase.com/" "website": "https://commerce.coinbase.com/"
}, },
@ -4904,18 +4726,7 @@
75 75
], ],
"description": "ConvertKit is an email marketing tool built for content creators.", "description": "ConvertKit is an email marketing tool built for content creators.",
"dom": { "dom": "form[action*='.convertkit.com'], link[href*='.convertkit.com']",
"form[action*='.convertkit.com']": {
"attributes": {
"action": ""
}
},
"link[href*='.convertkit.com']": {
"attributes": {
"href": ""
}
}
},
"icon": "ConvertKit.svg", "icon": "ConvertKit.svg",
"pricing": [ "pricing": [
"low", "low",
@ -4949,11 +4760,7 @@
"cats": [ "cats": [
67 67
], ],
"dom": { "dom": "#cookie-law-info-bar",
"#cookie-law-info-bar": {
"text": ""
}
},
"icon": "cookieyes.svg", "icon": "cookieyes.svg",
"scripts": "app\\.cookieyes\\.com/client_data/", "scripts": "app\\.cookieyes\\.com/client_data/",
"website": "https://www.cookieyes.com/" "website": "https://www.cookieyes.com/"
@ -5448,13 +5255,7 @@
32 32
], ],
"description": "Dealer Spike is a digital marketing and advertising company focused that helps dealers grow their business.", "description": "Dealer Spike is a digital marketing and advertising company focused that helps dealers grow their business.",
"dom": { "dom": "meta[name='author'][content*='Dealer Spike']",
"meta[name='author'][content*='Dealer Spike']": {
"attributes": {
"content": ""
}
}
},
"icon": "Dealer Spike.svg", "icon": "Dealer Spike.svg",
"pricing": [ "pricing": [
"poa" "poa"
@ -5613,13 +5414,7 @@
36 36
], ],
"description": "District M is a programmatic advertising exchange.", "description": "District M is a programmatic advertising exchange.",
"dom": { "dom": "iframe[src*='.districtm.io']",
"iframe[src*='.districtm.io']": {
"attributes": {
"src": ""
}
}
},
"icon": "District M.svg", "icon": "District M.svg",
"saas": true, "saas": true,
"website": "https://districtm.net", "website": "https://districtm.net",
@ -5909,18 +5704,7 @@
], ],
"cpe": "cpe:/a:commerceguys:commerce", "cpe": "cpe:/a:commerceguys:commerce",
"description": "Drupal Commerce is open-source ecommerce software that augments the content management system Drupal.", "description": "Drupal Commerce is open-source ecommerce software that augments the content management system Drupal.",
"dom": { "dom": "aside#cart-offcanvas, form.commerce-order-item-add-to-cart-form,form.commerce-add-to-cart",
"aside#cart-offcanvas": {
"attributes": {
"id": ""
}
},
"form.commerce-order-item-add-to-cart-form,form.commerce-add-to-cart": {
"attributes": {
"class": ""
}
}
},
"html": "<[^>]+(?:id=\"block[_-]commerce[_-]cart[_-]cart|class=\"commerce[_-]product[_-]field)", "html": "<[^>]+(?:id=\"block[_-]commerce[_-]cart[_-]cart|class=\"commerce[_-]product[_-]field)",
"icon": "Drupal Commerce.png", "icon": "Drupal Commerce.png",
"implies": "Drupal", "implies": "Drupal",
@ -6318,11 +6102,7 @@
47 47
], ],
"description": "Emotion is a library designed for writing CSS styles with JavaScript.", "description": "Emotion is a library designed for writing CSS styles with JavaScript.",
"dom": { "dom": "style[data-emotion], style[data-emotion-css]",
"style[data-emotion], style[data-emotion-css]": {
"text": ""
}
},
"icon": "Emotion.png", "icon": "Emotion.png",
"website": "http://emotion.sh" "website": "http://emotion.sh"
}, },
@ -6680,13 +6460,7 @@
], ],
"description": "Pinterest Conversion Tag allows you to track actions people take on your website after viewing your Promoted Pin.", "description": "Pinterest Conversion Tag allows you to track actions people take on your website after viewing your Promoted Pin.",
"icon": "Pinterest.svg", "icon": "Pinterest.svg",
"dom": { "dom": "img[src*='ct.pinterest.com/v3/?tid']",
"img[src*='ct.pinterest.com/v3/?tid']": {
"attributes": {
"src": ""
}
}
},
"js": { "js": {
"pintrk": "" "pintrk": ""
}, },
@ -6697,13 +6471,7 @@
10 10
], ],
"description": "Facebook pixel is an analytics tool that allows you to measure the effectiveness of your advertising.", "description": "Facebook pixel is an analytics tool that allows you to measure the effectiveness of your advertising.",
"dom": { "dom": "img[src*='facebook.com/tr']",
"img[src*='facebook.com/tr']": {
"attributes": {
"src": ""
}
}
},
"icon": "Facebook.svg", "icon": "Facebook.svg",
"js": { "js": {
"_fbq": "" "_fbq": ""
@ -6881,13 +6649,7 @@
71 71
], ],
"description": "FinanceAds is a performance marketing agency that has grown affiliate network for the financial sector.", "description": "FinanceAds is a performance marketing agency that has grown affiliate network for the financial sector.",
"dom": { "dom": "link[href*='js.financeads.net'], link[href*='js.financeads.com'], a[href*='www.financeads.net/tc.php']",
"link[href*='js.financeads.net'], link[href*='js.financeads.com'], a[href*='www.financeads.net/tc.php']": {
"attributes": {
"href": ""
}
}
},
"icon": "Financeads.png", "icon": "Financeads.png",
"pricing": [ "pricing": [
"payg" "payg"
@ -6942,13 +6704,7 @@
32 32
], ],
"description": "Firepush is an omnichannel marketing app that helps Shopify stores to drive sales with automated web push, email and SMS campaigns.", "description": "Firepush is an omnichannel marketing app that helps Shopify stores to drive sales with automated web push, email and SMS campaigns.",
"dom": { "dom": "link[href*='cdn.firepush.net']",
"link[href*='cdn.firepush.net']": {
"attributes": {
"href": ""
}
}
},
"icon": "Firepush.svg", "icon": "Firepush.svg",
"pricing": [ "pricing": [
"low", "low",
@ -8098,14 +7854,7 @@
41 41
], ],
"description": "Google Pay is a digital wallet platform and online payment system developed by Google to power in-app and tap-to-pay purchases on mobile devices, enabling users to make payments with Android phones, tablets or watches.", "description": "Google Pay is a digital wallet platform and online payment system developed by Google to power in-app and tap-to-pay purchases on mobile devices, enabling users to make payments with Android phones, tablets or watches.",
"dom": { "dom": "[aria-labelledby='pi-google_pay'], ul[data-shopify-buttoncontainer] li",
"[aria-labelledby='pi-google_pay']": {
"text": ""
},
"ul[data-shopify-buttoncontainer] li": {
"text": "GooglePay"
}
},
"icon": "Google.svg", "icon": "Google.svg",
"scripts": [ "scripts": [
"pay\\.google\\.com/([a-z/]+)/pay\\.js" "pay\\.google\\.com/([a-z/]+)/pay\\.js"
@ -8370,13 +8119,7 @@
72 72
], ],
"description": "Guestonline is a restaurant table booking widget.", "description": "Guestonline is a restaurant table booking widget.",
"dom": { "dom": "iframe[src*='ib.guestonline.']",
"iframe[src*='guestonline']": {
"attributes": {
"src": "ib\\.guestonline\\.\\w+"
}
}
},
"icon": "Guestonline.svg", "icon": "Guestonline.svg",
"pricing": [ "pricing": [
"low", "low",
@ -8391,18 +8134,7 @@
36 36
], ],
"description": "GumGum is a technology and media company specializing in contextual intelligence.", "description": "GumGum is a technology and media company specializing in contextual intelligence.",
"dom": { "dom": "iframe[src*='gumgum.com'], img[src*='gumgum.com']",
"iframe[src*='gumgum.com']": {
"attributes": {
"src": ""
}
},
"img[src*='gumgum.com']": {
"attributes": {
"src": ""
}
}
},
"icon": "GumGum.png", "icon": "GumGum.png",
"pricing": [ "pricing": [
"low", "low",
@ -8604,11 +8336,7 @@
18 18
], ],
"description": "Helix Ultimate a free template framework for Joomla.", "description": "Helix Ultimate a free template framework for Joomla.",
"dom": { "dom": "header#sp-header, body.helix-ultimate",
"header#sp-header, body.helix-ultimate": {
"text": ""
}
},
"icon": "HelixUltimate.svg", "icon": "HelixUltimate.svg",
"implies": "Joomla", "implies": "Joomla",
"oss": true, "oss": true,
@ -9430,14 +9158,8 @@
"cats": [ "cats": [
71 71
], ],
"description": "JANet is an affiliate marketing network.", "description": "JANet is an affiliate marketing network.",
"dom": { "dom": "img[src*='.j-a-net.jp'],img[data-src*='.j-a-net.jp']",
"img[src*='.j-a-net.jp'],img[data-src*='.j-a-net.jp']": {
"attributes": {
"src": ""
}
}
},
"icon": "JANet.png", "icon": "JANet.png",
"website": "https://j-a-net.jp" "website": "https://j-a-net.jp"
}, },
@ -9536,13 +9258,7 @@
14 14
], ],
"description": "JW Player is a online video player with video engagement analytics, custom video player skins, and live video streaming capability.", "description": "JW Player is a online video player with video engagement analytics, custom video player skins, and live video streaming capability.",
"dom": { "dom": "div[data-video-provider*=jwplayer]",
"div[data-video-provider*=jwplayer]": {
"attributes": {
"data-video-provider": ""
}
}
},
"icon": "JW Player.svg", "icon": "JW Player.svg",
"js": { "js": {
"jwDefaults": "", "jwDefaults": "",
@ -9904,13 +9620,7 @@
"kameleoonVisitorCode": "" "kameleoonVisitorCode": ""
}, },
"description": "Kameleoon is a personalisation technology platform for real-time omnichannel optimisation and conversion.", "description": "Kameleoon is a personalisation technology platform for real-time omnichannel optimisation and conversion.",
"dom": { "dom": "link[href*='.kameleoon.eu/kameleoon.js']",
"link[href*='.kameleoon.eu/kameleoon.js']": {
"attributes": {
"href": ""
}
}
},
"icon": "Kameleoon.svg", "icon": "Kameleoon.svg",
"js": { "js": {
"Kameleoon.Gatherer.SCRIPT_VERSION": "(.+)\\;version:\\1", "Kameleoon.Gatherer.SCRIPT_VERSION": "(.+)\\;version:\\1",
@ -9975,13 +9685,7 @@
53 53
], ],
"description": "Keap offers an email marketing and sales platform for small businesses, including products to manage customers, customer relationship management, marketing, and ecommerce.", "description": "Keap offers an email marketing and sales platform for small businesses, including products to manage customers, customer relationship management, marketing, and ecommerce.",
"dom": { "dom": "form[action*='property.infusionsoft.com']",
"form[action*='property.infusionsoft.com']": {
"attributes": {
"action": ""
}
}
},
"icon": "Keap.svg", "icon": "Keap.svg",
"pricing": [ "pricing": [
"low", "low",
@ -10336,13 +10040,7 @@
6 6
], ],
"description": "Kooomo is a SaaS ecommerce platform with a focus on localisation and internationalisation.", "description": "Kooomo is a SaaS ecommerce platform with a focus on localisation and internationalisation.",
"dom": { "dom": "img[src*='.kooomo-cloud.com']",
"img[src*='.kooomo-cloud.com']": {
"attributes": {
"src": ""
}
}
},
"icon": "Kooomo.svg", "icon": "Kooomo.svg",
"implies": [ "implies": [
"PHP", "PHP",
@ -10408,13 +10106,7 @@
36 36
], ],
"description": "LKQD is a video advertising platform that enables publishers to serve video ads across multiple devices and formats.", "description": "LKQD is a video advertising platform that enables publishers to serve video ads across multiple devices and formats.",
"dom": { "dom": "iframe[src*='.lkqd.net']",
"iframe[src*='.lkqd.net']": {
"attributes": {
"src": ""
}
}
},
"icon": "LKQD.svg", "icon": "LKQD.svg",
"js": { "js": {
"lkqdCall": "", "lkqdCall": "",
@ -10655,13 +10347,7 @@
"cats": [ "cats": [
10 10
], ],
"dom": { "dom": "noscript > img[src*='dc.ads.linkedin.com']",
"noscript > img[src*='dc.ads.linkedin.com']": {
"attributes": {
"src": ""
}
}
},
"icon": "Linkedin.svg", "icon": "Linkedin.svg",
"js": { "js": {
"_linkedin_data_partner_id": "" "_linkedin_data_partner_id": ""
@ -10853,13 +10539,7 @@
67 67
], ],
"description": "LiveRamp PCM is a preference and consent management platform that enables comply with the ePrivacy Directive, GDPR, CCPA, and other data protection and privacy laws and regulations.", "description": "LiveRamp PCM is a preference and consent management platform that enables comply with the ePrivacy Directive, GDPR, CCPA, and other data protection and privacy laws and regulations.",
"dom": { "dom": "iframe[src*='gdpr-consent-tool\\.privacymanager\\.io']",
"iframe[src*='gdpr-consent-tool\\.privacymanager\\.io']": {
"attributes": {
"src": ""
}
}
},
"icon": "LiveRamp.svg", "icon": "LiveRamp.svg",
"js": { "js": {
"wpJsonpLiverampGdprCmp": "" "wpJsonpLiverampGdprCmp": ""
@ -11365,13 +11045,7 @@
35 35
], ],
"description": "Mapbox GL JS is a JavaScript library that uses WebGL to render interactive maps from vector tiles and Mapbox styles.", "description": "Mapbox GL JS is a JavaScript library that uses WebGL to render interactive maps from vector tiles and Mapbox styles.",
"dom": { "dom": "link[href*='mapbox-gl.css']",
"link[href*='mapbox-gl.css']": {
"attributes": {
"href": ""
}
}
},
"icon": "Mapbogljs.png", "icon": "Mapbogljs.png",
"js": { "js": {
"mapboxgl.version": "^(.+)$\\;version:\\1\\;confidence:0" "mapboxgl.version": "^(.+)$\\;version:\\1\\;confidence:0"
@ -11689,13 +11363,7 @@
72 72
], ],
"description": "Meeting Scheduler is a schedule appointments widget.", "description": "Meeting Scheduler is a schedule appointments widget.",
"dom": { "dom": "a[href*='bookmenow.info/book']",
"a[href*='bookmenow.info/book']": {
"attributes": {
"href": ""
}
}
},
"icon": "Meeting Scheduler.png", "icon": "Meeting Scheduler.png",
"scripts": "bookmenow\\.info/(?:runtime|main).+\\.js", "scripts": "bookmenow\\.info/(?:runtime|main).+\\.js",
"website": "https://bookmenow.info" "website": "https://bookmenow.info"
@ -12587,11 +12255,7 @@
6 6
], ],
"description": "NEO is an ecommerce software that manages multiple online stores.", "description": "NEO is an ecommerce software that manages multiple online stores.",
"dom": { "dom": "#svr[value^=\"NEOWEBV\"]",
"#svr[value^=\"NEOWEBV\"]": {
"text": ""
}
},
"headers": { "headers": {
"powered": "jet-neo" "powered": "jet-neo"
}, },
@ -12603,16 +12267,7 @@
"cats": [ "cats": [
66 66
], ],
"dom": { "dom": ".nsw-container, .nsw-header, .nsw-icon, link[href*='nsw-design-system']",
".nsw-container, .nsw-header, .nsw-icon": {
"text": ""
},
"link[href*='nsw-design-system']": {
"attributes": {
"href": ""
}
}
},
"icon": "NSW Design System.svg", "icon": "NSW Design System.svg",
"js": { "js": {
"NSW.initSite": "" "NSW.initSite": ""
@ -13081,13 +12736,7 @@
"ometria": "" "ometria": ""
}, },
"description": "Ometria is a customer insight and marketing automation platform.", "description": "Ometria is a customer insight and marketing automation platform.",
"dom": { "dom": "form[action*='api.ometria.com']",
"form[action*='api.ometria.com']": {
"attributes": {
"action": "api\\.ometria\\.com"
}
}
},
"icon": "Ometria.svg", "icon": "Ometria.svg",
"js": { "js": {
"AddOmetriaBasket": "", "AddOmetriaBasket": "",
@ -13122,13 +12771,7 @@
74 74
], ],
"description": "Omniconvert is an award-winning conversion rate optimisation (CRO) software that can be used for A/B testing, online surveys, traffic segmentation.", "description": "Omniconvert is an award-winning conversion rate optimisation (CRO) software that can be used for A/B testing, online surveys, traffic segmentation.",
"dom": { "dom": "link[href*='app.omniconvert.com']",
"link[href*='app.omniconvert.com']": {
"attributes": {
"href": ""
}
}
},
"icon": "Omniconvert.png", "icon": "Omniconvert.png",
"js": { "js": {
"_omni": "" "_omni": ""
@ -13446,13 +13089,7 @@
35 35
], ],
"description": "OpenStreetMap is a free, editable map of the whole world that is being built by volunteers largely from scratch and released with an open-content license.", "description": "OpenStreetMap is a free, editable map of the whole world that is being built by volunteers largely from scratch and released with an open-content license.",
"dom": { "dom": "iframe[src*='openstreetmap.org'],iframe[data-lazy-src*='openstreetmap.org']",
"iframe[src*='openstreetmap.org'],iframe[data-lazy-src*='openstreetmap.org']": {
"attributes": {
"src": ""
}
}
},
"icon": "OpenStreetMap.svg", "icon": "OpenStreetMap.svg",
"oss": true, "oss": true,
"website": "https://www.openstreetmap.org" "website": "https://www.openstreetmap.org"
@ -13483,18 +13120,7 @@
], ],
"cpe": "cpe:/a:openx:openx", "cpe": "cpe:/a:openx:openx",
"description": "OpenX is a programmatic advertising technology company.", "description": "OpenX is a programmatic advertising technology company.",
"dom": { "dom": "iframe[src*='.openx.net'], img[src*='.openx.net']",
"iframe[src*='.openx.net']": {
"attributes": {
"src": ""
}
},
"img[src*='.openx.net']": {
"attributes": {
"src": ""
}
}
},
"icon": "OpenX.png", "icon": "OpenX.png",
"js": { "js": {
"openx.name": "openx" "openx.name": "openx"
@ -13993,13 +13619,7 @@
41 41
], ],
"description": "PayBright is a Canadian fintech company that offers short-term interest-free installment loans for online shopping to consumers at checkout.", "description": "PayBright is a Canadian fintech company that offers short-term interest-free installment loans for online shopping to consumers at checkout.",
"dom": { "dom": "link[href*='app.paybright.com']",
"link[href*='app.paybright.com']": {
"attributes": {
"href": ""
}
}
},
"icon": "PayBright.png", "icon": "PayBright.png",
"js": { "js": {
"_paybright_config": "" "_paybright_config": ""
@ -14032,18 +13652,8 @@
"button": { "button": {
"text": "PayPal" "text": "PayPal"
}, },
"iframe[src*='paypal.com']": { "iframe[src*='paypal.com'], img[src*='paypal.com'], img[src*='paypalobjects.com'], link[href*='paypal-express-checkout']": {
"attributes": { "exists": ""
"src": ""
}
},
"img[src*='paypal.com'], img[src*='paypalobjects.com']": {
"text": ""
},
"link": {
"attributes": {
"href": "paypal-express-checkout"
}
} }
}, },
"html": "<input[^>]+_s-xclick", "html": "<input[^>]+_s-xclick",
@ -14325,13 +13935,7 @@
5 5
], ],
"description": "Picreel is a conversion optimisation software.", "description": "Picreel is a conversion optimisation software.",
"dom": { "dom": "iframe[src*='app.picreel.com']",
"iframe[src*='app.picreel.com']": {
"attributes": {
"src": ""
}
}
},
"icon": "Picreel.svg", "icon": "Picreel.svg",
"js": { "js": {
"picreel": "" "picreel": ""
@ -14353,6 +13957,7 @@
"headers": { "headers": {
"X-Powered-By": "^pimcore$" "X-Powered-By": "^pimcore$"
}, },
"dom": ".pimcore_area_content",
"icon": "pimcore.svg", "icon": "pimcore.svg",
"implies": "PHP", "implies": "PHP",
"website": "http://pimcore.org" "website": "http://pimcore.org"

@ -1,24 +1,24 @@
'use strict' 'use strict'
function toArray(value) {
return Array.isArray(value) ? value : [value]
}
const Wappalyzer = { const Wappalyzer = {
technologies: [], technologies: [],
categories: [], categories: [],
slugify(string) { slugify: (string) =>
return string string
.toLowerCase() .toLowerCase()
.replace(/[^a-z0-9-]/g, '-') .replace(/[^a-z0-9-]/g, '-')
.replace(/--+/g, '-') .replace(/--+/g, '-')
.replace(/(?:^-|-$)/g, '') .replace(/(?:^-|-$)/g, ''),
},
getTechnology(name) { getTechnology: (name) =>
return Wappalyzer.technologies.find(({ name: _name }) => name === _name) Wappalyzer.technologies.find(({ name: _name }) => name === _name),
},
getCategory(id) { getCategory: (id) => Wappalyzer.categories.find(({ id: _id }) => id === _id),
return Wappalyzer.categories.find(({ id: _id }) => id === _id)
},
/** /**
* Resolve promises for implied technology. * Resolve promises for implied technology.
@ -270,7 +270,19 @@ const Wappalyzer = {
headers: transform(headers), headers: transform(headers),
dns: transform(dns), dns: transform(dns),
cookies: transform(cookies), cookies: transform(cookies),
dom: transform(dom, true), dom: transform(
typeof dom === 'string' || Array.isArray(dom)
? toArray(dom).reduce(
(dom, selector) => ({
...dom,
[selector]: { exists: '' },
}),
{}
)
: dom,
false,
false
),
html: transform(html), html: transform(html),
css: transform(css), css: transform(css),
certIssuer: transform(certIssuer), certIssuer: transform(certIssuer),
@ -319,13 +331,11 @@ const Wappalyzer = {
* @param {string|array} patterns * @param {string|array} patterns
* @param {boolean} caseSensitive * @param {boolean} caseSensitive
*/ */
transformPatterns(patterns, caseSensitive = false) { transformPatterns(patterns, caseSensitive = false, isRegex = true) {
if (!patterns) { if (!patterns) {
return [] return []
} }
const toArray = (value) => (Array.isArray(value) ? value : [value])
if (typeof patterns === 'string' || Array.isArray(patterns)) { if (typeof patterns === 'string' || Array.isArray(patterns)) {
patterns = { main: patterns } patterns = { main: patterns }
} }
@ -333,7 +343,7 @@ const Wappalyzer = {
const parsed = Object.keys(patterns).reduce((parsed, key) => { const parsed = Object.keys(patterns).reduce((parsed, key) => {
parsed[caseSensitive ? key : key.toLowerCase()] = toArray( parsed[caseSensitive ? key : key.toLowerCase()] = toArray(
patterns[key] patterns[key]
).map((pattern) => Wappalyzer.parsePattern(pattern)) ).map((pattern) => Wappalyzer.parsePattern(pattern, isRegex))
return parsed return parsed
}, {}) }, {})
@ -345,7 +355,7 @@ const Wappalyzer = {
* Extract information from regex pattern. * Extract information from regex pattern.
* @param {string|object} pattern * @param {string|object} pattern
*/ */
parsePattern(pattern) { parsePattern(pattern, isRegex = true) {
if (typeof pattern === 'object') { if (typeof pattern === 'object') {
return Object.keys(pattern).reduce( return Object.keys(pattern).reduce(
(parsed, key) => ({ (parsed, key) => ({
@ -369,7 +379,10 @@ const Wappalyzer = {
attrs.value = attr attrs.value = attr
// Escape slashes in regular expression // Escape slashes in regular expression
attrs.regex = new RegExp(attr.replace(/\//g, '\\/'), 'i') attrs.regex = new RegExp(
isRegex ? attr.replace(/\//g, '\\/') : '',
'i'
)
} }
return attrs return attrs