diff --git a/src/drivers/npm/driver.js b/src/drivers/npm/driver.js index a68696437..d304830d5 100644 --- a/src/drivers/npm/driver.js +++ b/src/drivers/npm/driver.js @@ -9,6 +9,7 @@ const { setTechnologies, setCategories, analyze, + analyzeOneToMany, analyzeManyToMany, resolve, } = Wappalyzer @@ -67,6 +68,35 @@ function analyzeJs(js) { ) } +function analyzeDom(dom) { + return Array.prototype.concat.apply( + [], + dom.map(({ name, selector, text, property, attribute, value }) => { + const technology = Wappalyzer.technologies.find( + ({ name: _name }) => name === _name + ) + + if (text) { + return analyzeManyToMany(technology, 'dom.text', { [selector]: [text] }) + } + + if (property) { + return analyzeManyToMany(technology, `dom.properties.${property}`, { + [selector]: [value], + }) + } + + if (attribute) { + return analyzeManyToMany(technology, `dom.attributes.${attribute}`, { + [selector]: [value], + }) + } + + return [] + }) + ) +} + function get(url) { if (['http:', 'https:'].includes(url.protocol)) { const { get } = url.protocol === 'http:' ? http : https @@ -173,7 +203,11 @@ class Driver { class Site { constructor(url, headers = {}, driver) { - ;({ options: this.options, browser: this.browser } = driver) + ;({ + options: this.options, + browser: this.browser, + init: this.initDriver, + } = driver) this.options.headers = { ...this.options.headers, @@ -225,12 +259,26 @@ class Site { } } - timeout() { - return new Promise((resolve, reject) => - setTimeout(() => { - reject(new Error('The website took too long to respond')) - }, this.options.maxWait) - ) + promiseTimeout( + promise, + errorMessage = 'The website took too long to respond' + ) { + let timeout = null + + return Promise.race([ + new Promise((resolve, reject) => { + timeout = setTimeout(() => { + clearTimeout(timeout) + + reject(new Error(errorMessage)) + }, this.options.maxWait) + }), + promise.then((value) => { + clearTimeout(timeout) + + return value + }), + ]) } async goto(url) { @@ -249,7 +297,11 @@ class Site { } if (!this.browser) { - throw new Error('Browser closed') + await this.initDriver() + + if (!this.browser) { + throw new Error('Browser closed') + } } const page = await this.browser.newPage() @@ -337,99 +389,101 @@ class Site { ) try { - await Promise.race([ - this.timeout(), - page.goto(url.href, { waitUntil: 'domcontentloaded' }), - ]) + await this.promiseTimeout( + page.goto(url.href, { waitUntil: 'domcontentloaded' }) + ) await sleep(1000) // Links - const links = await ( - await Promise.race([ - this.timeout(), - page.evaluateHandle(() => - Array.from(document.getElementsByTagName('a')).map( - ({ hash, hostname, href, pathname, protocol, rel }) => ({ - hash, - hostname, - href, - pathname, - protocol, - rel, - }) + const links = await this.promiseTimeout( + ( + await this.promiseTimeout( + page.evaluateHandle(() => + Array.from(document.getElementsByTagName('a')).map( + ({ hash, hostname, href, pathname, protocol, rel }) => ({ + hash, + hostname, + href, + pathname, + protocol, + rel, + }) + ) ) - ), - ]) - ).jsonValue() + ) + ).jsonValue() + ) // CSS - const css = await ( - await Promise.race([ - this.timeout(), - page.evaluateHandle((maxRows) => { - const css = [] - - try { - if (!document.styleSheets.length) { - return '' - } + const css = await this.promiseTimeout( + ( + await this.promiseTimeout( + page.evaluateHandle((maxRows) => { + const css = [] + + try { + if (!document.styleSheets.length) { + return '' + } - for (const sheet of Array.from(document.styleSheets)) { - for (const rules of Array.from(sheet.cssRules)) { - css.push(rules.cssText) + for (const sheet of Array.from(document.styleSheets)) { + for (const rules of Array.from(sheet.cssRules)) { + css.push(rules.cssText) - if (css.length >= maxRows) { - break + if (css.length >= maxRows) { + break + } } } + } catch (error) { + return '' } - } catch (error) { - return '' - } - return css.join('\n') - }, this.options.htmlMaxRows), - ]) - ).jsonValue() + return css.join('\n') + }, this.options.htmlMaxRows) + ) + ).jsonValue() + ) // Script tags - const scripts = await ( - await Promise.race([ - this.timeout(), - page.evaluateHandle(() => - Array.from(document.getElementsByTagName('script')) - .map(({ src }) => src) - .filter((src) => src) - ), - ]) - ).jsonValue() + const scripts = await this.promiseTimeout( + ( + await this.promiseTimeout( + page.evaluateHandle(() => + Array.from(document.getElementsByTagName('script')) + .map(({ src }) => src) + .filter((src) => src) + ) + ) + ).jsonValue() + ) // Meta tags - const meta = await ( - await Promise.race([ - this.timeout(), - page.evaluateHandle(() => - Array.from(document.querySelectorAll('meta')).reduce( - (metas, meta) => { - const key = - meta.getAttribute('name') || meta.getAttribute('property') - - if (key) { - metas[key.toLowerCase()] = [meta.getAttribute('content')] - } + const meta = await this.promiseTimeout( + ( + await this.promiseTimeout( + page.evaluateHandle(() => + Array.from(document.querySelectorAll('meta')).reduce( + (metas, meta) => { + const key = + meta.getAttribute('name') || meta.getAttribute('property') + + if (key) { + metas[key.toLowerCase()] = [meta.getAttribute('content')] + } - return metas - }, - {} + return metas + }, + {} + ) ) - ), - ]) - ).jsonValue() + ) + ).jsonValue() + ) // JavaScript - const js = await Promise.race([ - this.timeout(), + const js = await this.promiseTimeout( page.evaluate( (technologies) => { return technologies.reduce((technologies, { name, chains }) => { @@ -461,8 +515,81 @@ class Site { Wappalyzer.technologies .filter(({ js }) => Object.keys(js).length) .map(({ name, js }) => ({ name, chains: Object.keys(js) })) - ), - ]) + ) + ) + + // DOM + const dom = await this.promiseTimeout( + page.evaluate( + (technologies) => { + return technologies.reduce((technologies, { name, dom }) => { + const toScalar = (value) => + typeof value === 'string' || typeof value === 'number' + ? value + : !!value + + Object.keys(dom).forEach((selector) => { + const el = document.querySelector(selector) + + if (!el) { + return + } + + dom[selector].forEach(({ text, properties, attributes }) => { + if (text) { + const value = el.textContent.trim() + + if (value) { + technologies.push({ + name, + selector, + text: value, + }) + } + } + + if (properties) { + Object.keys(properties).forEach((property) => { + if (Object.prototype.hasOwnProperty.call(el, property)) { + const value = el[property] + + if (typeof value !== 'undefined') { + technologies.push({ + name, + selector, + property, + value: toScalar(value), + }) + } + } + }) + } + + if (attributes) { + Object.keys(attributes).forEach((attribute) => { + if (el.hasAttribute(attribute)) { + const value = el.getAttribute(attribute) + + technologies.push({ + name, + selector, + attribute, + value: toScalar(value), + }) + } + }) + } + }) + }) + + return technologies + }, []) + }, + Wappalyzer.technologies + .filter(({ dom }) => dom) + .map(({ name, dom }) => ({ name, dom })) + ) + ) // Cookies const cookies = (await page.cookies()).reduce( @@ -506,6 +633,7 @@ class Site { throw new Error('No response from server') } + this.onDetect(analyzeDom(dom)) this.onDetect(analyzeJs(js)) this.onDetect( @@ -559,7 +687,11 @@ class Site { return reducedLinks } catch (error) { - this.error(error) + if (error.constructor.name === 'TimeoutError') { + throw new Error('The website took too long to respond') + } + + throw new Error(error.message) } } diff --git a/src/drivers/npm/package.json b/src/drivers/npm/package.json index 59fb46b9d..1f4bf3485 100644 --- a/src/drivers/npm/package.json +++ b/src/drivers/npm/package.json @@ -13,7 +13,7 @@ "software" ], "homepage": "https://www.wappalyzer.com/", - "version": "6.3.2", + "version": "6.3.8", "author": "Wappalyzer", "license": "MIT", "repository": { @@ -40,6 +40,6 @@ "wappalyzer": "./cli.js" }, "dependencies": { - "puppeteer": "^5.2.1" + "puppeteer": "^5.3.0" } } diff --git a/src/drivers/npm/yarn.lock b/src/drivers/npm/yarn.lock index 96f84a2a0..1ebd145e4 100644 --- a/src/drivers/npm/yarn.lock +++ b/src/drivers/npm/yarn.lock @@ -76,10 +76,10 @@ debug@4, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -devtools-protocol@0.0.781568: - version "0.0.781568" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.781568.tgz#4cdca90a952d2c77831096ff6cd32695d8715a04" - integrity sha512-9Uqnzy6m6zEStluH9iyJ3iHyaQziFnMnLeC8vK0eN6smiJmIx7+yB64d67C2lH/LZra+5cGscJAJsNXO+MdPMg== +devtools-protocol@0.0.799653: + version "0.0.799653" + resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.799653.tgz#86fc95ce5bf4fdf4b77a58047ba9d2301078f119" + integrity sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg== end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" @@ -264,13 +264,13 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -puppeteer@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.2.1.tgz#7f0564f0a5384f352a38c8cc42af875cd87f4ea6" - integrity sha512-PZoZG7u+T6N1GFWBQmGVG162Ak5MAy8nYSVpeeQrwJK2oYUlDWpHEJPcd/zopyuEMTv7DiztS1blgny1txR2qw== +puppeteer@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.3.0.tgz#0abf83d0f2d1273baf2b56885a813f8052903e33" + integrity sha512-GjqMk5GRro3TO0sw3QMsF1H7n+/jaK2OW45qMvqjYUyJ7y4oA//9auy969HHhTG3HZXaMxY/NWXF/NXlAFIvtw== dependencies: debug "^4.1.0" - devtools-protocol "0.0.781568" + devtools-protocol "0.0.799653" extract-zip "^2.0.0" https-proxy-agent "^4.0.0" mime "^2.0.3" diff --git a/src/drivers/webextension/_locales/ca/messages.json b/src/drivers/webextension/_locales/ca/messages.json index c5053ff24..6a773dbcd 100644 --- a/src/drivers/webextension/_locales/ca/messages.json +++ b/src/drivers/webextension/_locales/ca/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility"}, "categoryName69": { "message": "Social login"}, - "categoryName70": { "message": "SSL/TLS certificate authority"} + "categoryName70": { "message": "SSL/TLS certificate authority"}, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/de/messages.json b/src/drivers/webextension/_locales/de/messages.json index 631d7010d..7a89c97bb 100644 --- a/src/drivers/webextension/_locales/de/messages.json +++ b/src/drivers/webextension/_locales/de/messages.json @@ -8,35 +8,35 @@ "optionUpgradeMessage": { "message": "Benachrichtige mich bei Upgrades" }, "optionDynamicIcon": { "message": "Applikations Icon anstatt des Wappalyzer Icons verwenden" }, "optionTracking": { "message": "Anonyme Statistiken an wappalyzer.com übermitteln" }, - "optionThemeMode": { "message": "Aktivieren dunklen Modus Kompatibilität." }, - "optionBadge": { "message": "Show the number of identified technologies on the icon" }, - "disableOnDomain": { "message": "Disable on this website" }, - "clearCache": { "message": "Clear cached detections" }, + "optionThemeMode": { "message": "Dunkel-Modus aktivieren" }, + "optionBadge": { "message": "Anzahl der identifizierten Optionen am Icon anzeigen" }, + "disableOnDomain": { "message": "Auf dieser Website deaktivieren" }, + "clearCache": { "message": "Cache leeren" }, "nothingToDo": { "message": "Nichts zu tun." }, - "noAppsDetected": { "message": "Keine Applikation entdeckt." }, - "categoryPin": { "message": "Immer Icon anzeigen" }, - "termsAccept": { "message": "Accept" }, - "termsContent": { "message": "This extension sends anonymous information about websites you visit, including domain name and identified technologies, to wappalyzer.com. This can be disabled in the settings." }, - "privacyPolicy": { "message": "Privacy policy" }, - "createAlert": { "message": "Create an alert for this website" }, + "noAppsDetected": { "message": "Keine Applikationen gefunden" }, + "categoryPin": { "message": "Icon immer anzeigen" }, + "termsAccept": { "message": "Akzeptieren" }, + "termsContent": { "message": "Diese Erweiterung sendet anonyme Informationen über Websites, die Sie besuchen, einschließlich der Domain und der identifizierten Technologien, an wappalyzer.com. Dies kann in den Einstellungen deaktiviert werden." }, + "privacyPolicy": { "message": "Datenschutzerklärung" }, + "createAlert": { "message": "Alarm für diese Website erstellen" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Nachrichten Board" }, "categoryName3": { "message": "Datenbankverwaltung" }, "categoryName4": { "message": "Dokumentations Tool" }, "categoryName5": { "message": "Widget" }, - "categoryName6": { "message": "Ecommerce" }, + "categoryName6": { "message": "E-Commerce" }, "categoryName7": { "message": "Fotogalerien" }, "categoryName8": { "message": "Wikis" }, "categoryName9": { "message": "Hosting-Panels" }, "categoryName10": { "message": "Statistiken" }, "categoryName11": { "message": "Blog" }, - "categoryName12": { "message": "JavaScript Framework" }, - "categoryName13": { "message": "Fehlertracker" }, - "categoryName14": { "message": "Videospieler" }, + "categoryName12": { "message": "JavaScript Frameworks" }, + "categoryName13": { "message": "Ticketsysteme" }, + "categoryName14": { "message": "Videoplayer" }, "categoryName15": { "message": "Kommentarsystem" }, "categoryName16": { "message": "Security" }, "categoryName17": { "message": "Schrift Script" }, - "categoryName18": { "message": "Web Framework" }, + "categoryName18": { "message": "Web Frameworks" }, "categoryName19": { "message": "Sonstiges" }, "categoryName20": { "message": "Editor" }, "categoryName21": { "message": "LMS" }, @@ -46,47 +46,48 @@ "categoryName25": { "message": "JavaScript Graphics" }, "categoryName26": { "message": "Mobile Framework" }, "categoryName27": { "message": "Programmiersprache" }, - "categoryName28": { "message": "Betriebssystem" }, - "categoryName29": { "message": "Suchmaschine" }, + "categoryName28": { "message": "Betriebssysteme" }, + "categoryName29": { "message": "Suchmaschinen" }, "categoryName30": { "message": "Webmail" }, "categoryName31": { "message": "CDN" }, "categoryName32": { "message": "Marketing Automation" }, - "categoryName33": { "message": "Web Server Erweiterung" }, - "categoryName34": { "message": "Datenbank" }, - "categoryName35": { "message": "Map" }, - "categoryName36": { "message": "Werbenetzwerk" }, - "categoryName37": { "message": "Netzwerkdienst" }, + "categoryName33": { "message": "Web Server Erweiterungen" }, + "categoryName34": { "message": "Datenbanken" }, + "categoryName35": { "message": "Karten" }, + "categoryName36": { "message": "Werbenetzwerke" }, + "categoryName37": { "message": "Netzwerkdienste" }, "categoryName38": { "message": "Medienserver" }, - "categoryName39": { "message": "Webcam" }, + "categoryName39": { "message": "Web-Kameras" }, "categoryName40": { "message": "Drucker" }, "categoryName41": { "message": "Zahlungsverarbeiter" }, - "categoryName42": { "message": "Schlagwort Manager" }, + "categoryName42": { "message": "Tag Manager" }, "categoryName43": { "message": "Bezahlblockade" }, - "categoryName44": { "message": "Build/CI-System" }, + "categoryName44": { "message": "CI-Systeme" }, "categoryName45": { "message": "SCADA System" }, "categoryName46": { "message": "Fernzugriff" }, - "categoryName47": { "message": "Entwicklungswerkzeug" }, + "categoryName47": { "message": "Entwicklungswerkzeuge" }, "categoryName48": { "message": "Netzwerkspeicher" }, "categoryName49": { "message": "Feedleser" }, "categoryName50": { "message": "Dokumentmanagementsysteme" }, - "categoryName51": { "message": "Startseitenersteller" }, + "categoryName51": { "message": "Website Baukästen" }, "categoryName52": { "message": "Live-Chat" }, "categoryName53": { "message": "CRM" }, - "categoryName54": { "message": "SEO" }, + "categoryName54": { "message": "SEO" }, "categoryName55": { "message": "Buchhaltung" }, "categoryName56": { "message": "Cryptominer" }, "categoryName57": { "message": "Statischer Seitengenerator" }, - "categoryName58": { "message": "Benutzer-Einbindung" }, + "categoryName58": { "message": "Benutzer-Onboarding" }, "categoryName59": { "message": "JavaScript Bibliotheken" }, - "categoryName60": { "message": "Containers" }, + "categoryName60": { "message": "Container" }, "categoryName61": { "message": "SaaS" }, "categoryName62": { "message": "PaaS" }, "categoryName63": { "message": "IaaS" }, - "categoryName64": { "message": "Reverse Proxy" }, + "categoryName64": { "message": "Reverse Proxies" }, "categoryName65": { "message": "Load Balancer" }, "categoryName66": { "message": "UI Frameworks" }, - "categoryName67": { "message": "Cookie compliance" }, - "categoryName68": { "message": "Accessibility"}, - "categoryName69": { "message": "Social login"}, - "categoryName70": { "message": "SSL/TLS certificate authority"} + "categoryName67": { "message": "Cookie Compliance" }, + "categoryName68": { "message": "Barrierefreiheit"}, + "categoryName69": { "message": "Social Login"}, + "categoryName70": { "message": "SSL/TLS certificate authority"}, + "categoryName71": { "message": "Partnerprogram"} } diff --git a/src/drivers/webextension/_locales/el/messages.json b/src/drivers/webextension/_locales/el/messages.json index d96216826..b85696b09 100644 --- a/src/drivers/webextension/_locales/el/messages.json +++ b/src/drivers/webextension/_locales/el/messages.json @@ -84,5 +84,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/en/messages.json b/src/drivers/webextension/_locales/en/messages.json index 453b284bb..329ea3439 100644 --- a/src/drivers/webextension/_locales/en/messages.json +++ b/src/drivers/webextension/_locales/en/messages.json @@ -86,5 +86,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/es/messages.json b/src/drivers/webextension/_locales/es/messages.json index b073fb5aa..acd0a51a4 100644 --- a/src/drivers/webextension/_locales/es/messages.json +++ b/src/drivers/webextension/_locales/es/messages.json @@ -55,7 +55,7 @@ "categoryName34": { "message": "Base de Datos" }, "categoryName35": { "message": "Mapa" }, "categoryName36": { "message": "Red de Publicidad" }, - "categoryName37": { "message": "Network Sevice" }, + "categoryName37": { "message": "Network Service" }, "categoryName38": { "message": "Media Server" }, "categoryName39": { "message": "Webcam" }, "categoryName40": { "message": "Printer" }, @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/fa/messages.json b/src/drivers/webextension/_locales/fa/messages.json index 8a221fefb..66a9108d6 100644 --- a/src/drivers/webextension/_locales/fa/messages.json +++ b/src/drivers/webextension/_locales/fa/messages.json @@ -10,15 +10,15 @@ "optionTracking": { "message": "ارسال فن آوری های شناسایی شده به صورت ناشناس به wappalyzer.com" }, "optionThemeMode": { "message": "فعال کردن حالت سازگاری تاریک." }, "nothingToDo": { "message": "هیچ چیز برای انجام اینجا نیست." }, - "optionBadge": { "message": "Show the number of identified technologies on the icon" }, - "disableOnDomain": { "message": "Disable on this website" }, - "clearCache": { "message": "Clear cached detections" }, + "optionBadge": { "message": "نمایش تعداد فناوری های شناسایی شده روی آیکون" }, + "disableOnDomain": { "message": "غیرفعال کردن در این وبسایت" }, + "clearCache": { "message": "پاکسازی شناسایی های کش شده" }, "noAppsDetected": { "message": "هیچ فنآوری شناسایی نشده است." }, "categoryPin": { "message": "همیشه نماد را نشان بده" }, "termsAccept": { "message": "قبول" }, "termsContent": { "message": "این افزونه اطلاعات وبسایتهای بازدید شده توسط شما را به صورت ناشناس ارسال میکند، مانند آدرس سایت و تکنولوژیهای استفاده شده در آن سایت را ارسال میکند. اطلاعات بیشتر در wappalyzer.com. شما میتوانید این افزونه را غیرفعال کنید." }, - "privacyPolicy": { "message": "Privacy policy" }, - "createAlert": { "message": "Create an alert for this website" }, + "privacyPolicy": { "message": "سیاست حفظ حریم خصوصی" }, + "createAlert": { "message": "ساخت یک هشدار برای این وبسایت" }, "categoryName1": { "message": "سیستم مدیریت محتوا" }, "categoryName2": { "message": "انجمن پیام" }, "categoryName3": { "message": "مدیریت پایگاه داده" }, @@ -34,7 +34,7 @@ "categoryName13": { "message": "ردیاب مشکل" }, "categoryName14": { "message": "پخش کننده ویدیویی" }, "categoryName15": { "message": "سیستم نظرسنجی" }, - "categoryName16": { "message": "Security" }, + "categoryName16": { "message": "امنیت" }, "categoryName17": { "message": "اسکریپ فونت" }, "categoryName18": { "message": "چارچوب وب" }, "categoryName19": { "message": "متفرقه" }, @@ -83,10 +83,11 @@ "categoryName62": { "message": "PaaS" }, "categoryName63": { "message": "IaaS" }, "categoryName64": { "message": "پروکسی معکوس" }, - "categoryName65": { "message": "Load Balancer" }, - "categoryName66": { "message": "UI Frameworks" }, + "categoryName65": { "message": "لودبالانسر" }, + "categoryName66": { "message": "فریمورکهای رابط کاربری" }, "categoryName67": { "message": "Cookie compliance" }, - "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName68": { "message": "دسترسی" }, + "categoryName69": { "message": "ورود به شبکه های اجتماعی" }, + "categoryName70": { "message": "صادر کننده SSL/TLS" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/fr/messages.json b/src/drivers/webextension/_locales/fr/messages.json index 9f58d9b7f..dcd25e86b 100644 --- a/src/drivers/webextension/_locales/fr/messages.json +++ b/src/drivers/webextension/_locales/fr/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/gl_ES/messages.json b/src/drivers/webextension/_locales/gl_ES/messages.json index fb8fa26fd..cb599c477 100644 --- a/src/drivers/webextension/_locales/gl_ES/messages.json +++ b/src/drivers/webextension/_locales/gl_ES/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/gr/messages.json b/src/drivers/webextension/_locales/gr/messages.json index fcd10ad82..7fcd40740 100644 --- a/src/drivers/webextension/_locales/gr/messages.json +++ b/src/drivers/webextension/_locales/gr/messages.json @@ -84,5 +84,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/id/messages.json b/src/drivers/webextension/_locales/id/messages.json index a0d1b2897..a154d4074 100644 --- a/src/drivers/webextension/_locales/id/messages.json +++ b/src/drivers/webextension/_locales/id/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/it/messages.json b/src/drivers/webextension/_locales/it/messages.json index a80798521..150a85fc5 100644 --- a/src/drivers/webextension/_locales/it/messages.json +++ b/src/drivers/webextension/_locales/it/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/ja/messages.json b/src/drivers/webextension/_locales/ja/messages.json index 656901314..7cce9190c 100644 --- a/src/drivers/webextension/_locales/ja/messages.json +++ b/src/drivers/webextension/_locales/ja/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/ko/messages.json b/src/drivers/webextension/_locales/ko/messages.json index 86a217896..199a4b996 100644 --- a/src/drivers/webextension/_locales/ko/messages.json +++ b/src/drivers/webextension/_locales/ko/messages.json @@ -86,5 +86,6 @@ "categoryName67": { "message": "쿠키 동의" }, "categoryName68": { "message": "접근성" }, "categoryName69": { "message": "소셜 로그인" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/pl/messages.json b/src/drivers/webextension/_locales/pl/messages.json index d000e321f..0195b2b0a 100644 --- a/src/drivers/webextension/_locales/pl/messages.json +++ b/src/drivers/webextension/_locales/pl/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/pt/messages.json b/src/drivers/webextension/_locales/pt/messages.json index c4c41277d..5cb77d9b2 100644 --- a/src/drivers/webextension/_locales/pt/messages.json +++ b/src/drivers/webextension/_locales/pt/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/pt_BR/messages.json b/src/drivers/webextension/_locales/pt_BR/messages.json index 36f90a93f..741c0bf4a 100644 --- a/src/drivers/webextension/_locales/pt_BR/messages.json +++ b/src/drivers/webextension/_locales/pt_BR/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/ro/messages.json b/src/drivers/webextension/_locales/ro/messages.json index fb621d40c..117097d1b 100644 --- a/src/drivers/webextension/_locales/ro/messages.json +++ b/src/drivers/webextension/_locales/ro/messages.json @@ -84,5 +84,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/ru/messages.json b/src/drivers/webextension/_locales/ru/messages.json index e1fd536ee..70e340022 100644 --- a/src/drivers/webextension/_locales/ru/messages.json +++ b/src/drivers/webextension/_locales/ru/messages.json @@ -86,5 +86,6 @@ "categoryName67": { "message": "Соответствие cookie" }, "categoryName68": { "message": "Доступность" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/sk/messages.json b/src/drivers/webextension/_locales/sk/messages.json index f5095fa72..eb80872bf 100644 --- a/src/drivers/webextension/_locales/sk/messages.json +++ b/src/drivers/webextension/_locales/sk/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/tr/messages.json b/src/drivers/webextension/_locales/tr/messages.json index 832a58bc3..d2fcd6f25 100644 --- a/src/drivers/webextension/_locales/tr/messages.json +++ b/src/drivers/webextension/_locales/tr/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/uk/messages.json b/src/drivers/webextension/_locales/uk/messages.json index a6f7d5daa..227e361ad 100644 --- a/src/drivers/webextension/_locales/uk/messages.json +++ b/src/drivers/webextension/_locales/uk/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Відповідність файлам cookie" }, "categoryName68": { "message": "Доступність" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/uz/messages.json b/src/drivers/webextension/_locales/uz/messages.json index af62b2567..2dece6787 100644 --- a/src/drivers/webextension/_locales/uz/messages.json +++ b/src/drivers/webextension/_locales/uz/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility"}, "categoryName69": { "message": "Social login"}, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/zh_CN/messages.json b/src/drivers/webextension/_locales/zh_CN/messages.json index 8a37b95ca..633fe8aa3 100644 --- a/src/drivers/webextension/_locales/zh_CN/messages.json +++ b/src/drivers/webextension/_locales/zh_CN/messages.json @@ -86,5 +86,6 @@ "categoryName67": { "message": "Cookie 合规" }, "categoryName68": { "message": "辅助功能"}, "categoryName69": { "message": "社交登录"}, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/_locales/zh_TW/messages.json b/src/drivers/webextension/_locales/zh_TW/messages.json index 575a241f0..a43a4271f 100644 --- a/src/drivers/webextension/_locales/zh_TW/messages.json +++ b/src/drivers/webextension/_locales/zh_TW/messages.json @@ -88,5 +88,6 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program"} } diff --git a/src/drivers/webextension/css/styles.css b/src/drivers/webextension/css/styles.css index 45bd52ab3..7e88a4aed 100644 --- a/src/drivers/webextension/css/styles.css +++ b/src/drivers/webextension/css/styles.css @@ -186,6 +186,19 @@ a:hover { margin-bottom: .2rem; } +.technology__open-in-new { + color: var(--color-primary); + visibility: hidden; + height: 1.1rem; + margin-left: .1rem; + vertical-align: middle; + width: 1.1rem; +} + +.technology__heading:hover .technology__open-in-new { + visibility: visible; +} + .technology__icon { height: 16px; margin-right: .5rem; @@ -195,6 +208,17 @@ a:hover { .technology__link { color: var(--color-text); + display: block; + width: 100%; +} + +.technology__link:hover { + text-decoration: none; +} + +.technology__link:hover .technology__name { + border-bottom: 1px solid var(--color-primary); + color: var(--color-primary); } .technology__confidence { @@ -208,7 +232,7 @@ a:hover { border-radius: 3px; font-size: .7rem; padding: .1rem .3rem; - margin-left: .4rem; + margin-left: .2rem; vertical-align: middle; } diff --git a/src/drivers/webextension/html/popup.html b/src/drivers/webextension/html/popup.html index 2d3d2b84e..c41b3d9d3 100644 --- a/src/drivers/webextension/html/popup.html +++ b/src/drivers/webextension/html/popup.html @@ -51,15 +51,21 @@