diff --git a/.eslintrc.js b/.eslintrc.js index 8ff448efd..f91394cce 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,19 +2,18 @@ module.exports = { root: true, env: { browser: true, - node: true + node: true, }, parserOptions: { - parser: 'babel-eslint' + parser: 'babel-eslint', }, extends: [ '@nuxtjs', 'prettier', 'prettier/vue', 'plugin:prettier/recommended', - 'plugin:nuxt/recommended' - ], - plugins: [ - 'prettier' + 'plugin:nuxt/recommended', + 'plugin:json/recommended', ], + plugins: ['prettier'], } diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 000000000..e9041ab8f --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,25 @@ +name: Validate +on: + push: + pull_request: + +jobs: + validate: + name: Validate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.3.3 + - uses: actions/setup-node@v2.1.2 + with: + node-version: '14' + - name: Restore npm cache + uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }} + restore-keys: | + ${{ runner.os }}-node- + - name: Install dependencies + run: npm install + - name: Validate + run: npm run validate diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..253e4a11f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing + +Wappalyzer is an [MIT-licensed](https://github.com/aliasio/wappalyzer/blob/master/LICENSE), open source project written in JavaScript. Anyone is welcome to contribute. + +## Getting started + +To get started, see the [README](https://github.com/aliasio/wappalyzer/blob/master/README.md). + +## Submitting changes + +- First, run `yarn run validate` to identify any issues. +- Use descriptive commit messages, e.g. 'Add WordPress detection'. +- Push your commits to a new branch on your own fork. +- Finally, submit a [pull request](https://help.github.com/articles/about-pull-requests/) and describe your changes. + +## Adding a new technology + +Wappalyzer uses [regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) to fingerprint technologies. Refer to the [specification](https://github.com/AliasIO/wappalyzer/blob/master/README.md#specification) for detail. + +- Add a new block to [`src/technologies.json`](https://github.com/aliasio/wappalyzer/blob/master/src/technologies.json). +- Add an icon to [`src/drivers/webextension/images/icons`](https://github.com/aliasio/wappalyzer/tree/master/src/drivers/webextension/images/icons). The image must be square, either SVG or PNG (32 x 32 pixels). + +Only widely used technologies are accepted. When creating a pull request, include ten or more links to websites that use the application, a GitHub page with at least 1,000 stars or anything that will help establish the size of the user base. + +## Adding a new category + +Please [open an issue on GitHub](https://github.com/aliasio/wappalyzer/issues) first to discuss the need for a new category. + +To add a category, edit [`src/technologies.json`](https://github.com/aliasio/wappalyzer/blob/master/src/technologies.json) and update every [locale](https://github.com/aliasio/wappalyzer/tree/master/src/drivers/webextension/_locales). You may use the English category name in all of them. + +## Adding a new translation + +To add a new translation, copy the `en` folder in [`src/drivers/webextension/_locales`](https://github.com/aliasio/wappalyzer/tree/master/src/drivers/webextension/_locales), rename it to the relevant two-letter country code and update the containing `messages.json` file. + +## Adding a new feature + +Please [open an issue on GitHub](https://github.com/aliasio/wappalyzer/issues) first. New features and large changes are rarely accepted without prior discussion. diff --git a/README.md b/README.md index 97fdd7fc3..024c403fd 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,19 @@ # Wappalyzer [![Travis](https://travis-ci.org/aliasio/wappalyzer.svg?branch=master)](https://travis-ci.org/aliasio/wappalyzer/) -[Wappalyzer](https://www.wappalyzer.com) identifies technologies on websites. -It detects content management systems, ecommerce platforms, JavaScript frameworks, -analytics tools and [much more](https://www.wappalyzer.com/technologies). +[Wappalyzer](https://www.wappalyzer.com) identifies technologies on websites, including content management systems, ecommerce platforms, JavaScript frameworks, analytics tools and [much more](https://www.wappalyzer.com/technologies). * [wappalyzer on NPM](https://www.npmjs.com/package/wappalyzer) * [wappalyzer-core on NPM](https://www.npmjs.com/package/wappalyzer-core) * [Chrome extension](https://chrome.google.com/webstore/detail/wappalyzer/gppongmhjkpfnbhagpmjfkannfbllamg) * [Firefox add-on](https://addons.mozilla.org/en-US/firefox/addon/wappalyzer/) * [Edge extension](https://microsoftedge.microsoft.com/addons/detail/mnbndgmknlpdjdnjfmfcdjoegcckoikn) -* [Bookmarklet](https://www.wappalyzer.com/download) -* [wappalyzer/cli on Docker Hub](https://hub.docker.com/r/wappalyzer/cli/) * [Wappalyzer REST APIs](https://www.wappalyzer.com/api/) -## Documentation +## Prerequisites -Please read the [developer documentation](https://www.wappalyzer.com/docs). +- [Git](https://git-scm.com) +- [Node.js](https://nodejs.org) version 12 or higher +- [Yarn](https://yarnpkg.com) ## Quick start @@ -46,3 +44,423 @@ node src/drivers/npm/cli.js https://example.com * Go go `about:debugging#/runtime/this-firefox` * Click 'Load Temporary Add-on' * Select `src/drivers/webextension/manifest.json` + +## Specification + +A long list of [regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) is used to identify technologies on web pages. Wappalyzer inspects HTML code, as well as JavaScript variables, response headers and more. + +Patterns (regular expressions) are kept in [`src/technologies.json`](https://github.com/aliasio/wappalyzer/blob/master/src/technologies.json). The following is an example of an application fingerprint. + +#### Example + +```json +"Example": { + "description": "A short description of the technology.", + "cats": [ + "1" + ], + "cookies": { + "cookie_name": "Example" + }, + "dom": { + "#example-id": { + "attributes": { + "class": "example-class" + }, + "properties": { + "example-property": "" + }, + "text": "Example text content" + } + }, + "dns": { + "MX": [ + "example\\.com" + ] + }, + "js": { + "Example.method": "" + }, + "excludes": "Example", + "headers": { + "X-Powered-By": "Example" + }, + "html": "]example\\.css", + "css": "\\.example-class", + "robots": "Disallow: /unique-path/", + "implies": "PHP\\;confidence:50", + "meta": { + "generator": "(?:Example|Another Example)" + }, + "script": "example-([0-9.]+)\\.js\\;confidence:50\\;version:\\1", + "url": ".+\\.example\\.com", + "oss": true, + "saas": true, + "pricing": ["low", "medium", "high", "freemium", "onetime", "recurring", "poa"], + "website": "https://example.com", +} +``` + +## JSON fields + +Find the JSON schema at [`schema.json`](https://github.com/aliasio/wappalyzer/blob/master/schema.json). + +### Required properties + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescriptionExample
catsArray + One or more category IDs. + [1, 6]
websiteStringURL of the application's website. + "https://example.com" +
+ +### Optional properties + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescriptionExample
descriptionString + A short description of the technology in British English (max. + 250 characters). Write in a neutral, factual tone; not like an + ad. + "A short description."
iconStringApplication icon filename."WordPress.svg"
cpeString + The + CPE + is a structured naming scheme for applications, see the + specification. + "cpe:/a:apache:http_server"
saasBoolean + The technology is offered as a Software-as-a-Service (SaaS), i.e. hosted or cloud-based. + true
ossBoolean + The technology has an open-source license. + true
pricingArray +Cost indicator (based on a typical plan or average monthly price) and available pricing models. For paid products only. + +One of: +
    +
  • low Up to US 100 / mo
  • +
  • mid Up US 1,000 / mo
  • +
  • high More than 10,000 / mo
  • +
+ +Plus any of: +
    +
  • freemium Free plan available
  • +
  • onetime One-time payments accepted
  • +
  • recurring Subscriptions available
  • +
  • poa Price on asking
  • +
  • payg Pay as you go (e.g. commissions or usage-based fees)
  • +
+
["low", "freemium"]
+ +### Implies and excludes (optional) + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescriptionExample
impliesString | Array + The presence of one application can imply the presence of + another, e.g. WordpPress means PHP is also in use. + "PHP"
excludesString | Array + Opposite of implies. The presence of one application can exclude + the presence of another. + "Apache"
+ +### Patterns (optional) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldTypeDescriptionExample
cookiesObjectCookies.{ "cookie_name": "Cookie value" }
domObject + Uses a + query selector + to inspect element properties, attributes and text content. + + { "#example-id": { "property": { "example-prop": "" } } + } +
dnsObject + DNS records: supports MX, TXT, SOA and NS (NPM driver only). + + { "MX": "example\\.com" } +
jsObject + JavaScript properties (case sensitive). Avoid short property + names to prevent matching minified code. + { "jQuery.fn.jquery": "" }
headersObjectHTTP response headers.{ "X-Powered-By": "^WordPress$" }
htmlString | Array + HTML source code. Patterns must include an HTML opening tag to + avoid matching plain text. For performance reasons, avoid + html where possible and use + dom instead. + "<a [^>]*href=\"index.html"
cssString | Array + CSS rules. Unavailable when a website enforces a same-origin + policy. For performance reasons, only a portion of the available + CSS rules are used to find matches. + "\\.example-class"
robotsString | Array + Robots.txt contents. + "Disallow: /unique-path/"
urlStringFull URL of the page."^https?//.+\\.wordpress\\.com"
metaObjectHTML meta tags, e.g. generator.{ "generator": "^WordPress$" }
scriptsString | Array + URLs of JavaScript files included on the page. + "jquery\\.js"
+ +## Patterns + +Patterns are essentially JavaScript regular expressions written as strings, but with some additions. + +### Quirks and pitfalls + +- Because of the string format, the escape character itself must be escaped when using special characters such as the dot (`\\.`). Double quotes must be escaped only once (`\"`). Slashes do not need to be escaped (`/`). +- Flags are not supported. Regular expressions are treated as case-insensitive. +- Capture groups (`()`) are used for version detection. In other cases, use non-capturing groups (`(?:)`). +- Use start and end of string anchors (`^` and `$`) where possible for optimal performance. +- Short or generic patterns can cause applications to be identified incorrectly. Try to find unique strings to match. + +### Tags + +Tags (a non-standard syntax) can be appended to patterns (and implies and excludes, separated by `\\;`) to store additional information. + + + + + + + + + + + + + + + + + + + + + + +
TagDescriptionExample
confidence + Indicates a less reliable pattern that may cause false + positives. The aim is to achieve a combined confidence of 100%. + Defaults to 100% if not specified. + + "js": { "Mage": "\\;confidence:50" } +
version + Gets the version number from a pattern match using a special + syntax. + + "scripts": "jquery-([0-9.]+)\.js\\;version:\\1" +
+ +### Version syntax + +Application version information can be obtained from a pattern using a capture group. A condition can be evaluated using the ternary operator (`?:`). + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExampleDescription
\\1Returns the first match.
\\1?a: + Returns a if the first match contains a value, nothing + otherwise. +
\\1?a:b + Returns a if the first match contains a value, b otherwise. +
\\1?:b + Returns nothing if the first match contains a value, b + otherwise. +
foo\\1 + Returns foo with the first match appended. +
diff --git a/bin/validate.js b/bin/validate.js index 2b9274734..7c9c9b238 100755 --- a/bin/validate.js +++ b/bin/validate.js @@ -6,171 +6,167 @@ const { technologies, categories } = JSON.parse( fs.readFileSync('./src/technologies.json') ) -try { - Object.keys(technologies).forEach((name) => { - const technology = technologies[name] - - // Validate regular expressions - ;['url', 'html', 'meta', 'headers', 'cookies', 'script', 'js'].forEach( - (type) => { - if (technology[type]) { - const keyed = - typeof technology[type] === 'string' || - Array.isArray(technology[type]) - ? { _: technology[type] } - : technology[type] - - Object.keys(keyed).forEach((key) => { - const patterns = Array.isArray(keyed[key]) - ? keyed[key] - : [keyed[key]] - - patterns.forEach((pattern, index) => { - const id = `${name}: ${type}[${key === '_' ? `${index}` : key}]` - - const [regex, ...flags] = pattern.split('\\;') - - let maxGroups = 0 - - flags.forEach((flag) => { - const [key, value] = flag.split(':') - - if (key === 'version') { - const refs = value.match(/\\(\d+)/g) - - if (refs) { - maxGroups = refs.reduce((max, ref) => - Math.max(max, parseInt(refs[1] || 0)) - ) - } - } else if (key === 'confidence') { - if ( - !/^\d+$/.test(value) || - parseInt(value, 10) < 0 || - parseInt(value, 10) > 99 - ) { - throw new Error( - `Confidence value must a number between 0 and 99: ${value} (${id})` - ) - } - } else { - throw new Error(`Invalid flag: ${key} (${id})`) - } - }) - - // Validate regular expression - try { - // eslint-disable-next-line no-new - new RegExp(regex) - } catch (error) { - throw new Error(`${error.message} (${id})`) - } - - // Count capture groups - const groups = new RegExp(`${regex}|`).exec('').length - 1 +Object.keys(technologies).forEach((name) => { + const technology = technologies[name] - if (groups > maxGroups) { - throw new Error( - `Too many non-capturing groups, expected ${maxGroups}: ${regex} (${id})` - ) - } + // Validate regular expressions + ;['url', 'html', 'meta', 'headers', 'cookies', 'script', 'js'].forEach( + (type) => { + if (technology[type]) { + const keyed = + typeof technology[type] === 'string' || + Array.isArray(technology[type]) + ? { _: technology[type] } + : technology[type] - if (type === 'html' && !/[<>]/.test(regex)) { - throw new Error( - `HTML pattern must include < or >: ${regex} (${id})` - ) - } - }) - }) - } - } - ) + Object.keys(keyed).forEach((key) => { + const patterns = Array.isArray(keyed[key]) ? keyed[key] : [keyed[key]] - // Validate categories - technology.cats.forEach((id) => { - if (!categories[id]) { - throw new Error(`No such category: ${id} (${name})`) - } - }) + patterns.forEach((pattern, index) => { + const id = `${name}: ${type}[${key === '_' ? `${index}` : key}]` - // Validate icons - if (technology.icon && !fs.existsSync(`${iconPath}/${technology.icon}`)) { - throw new Error(`No such icon: ${technology.icon} (${name})`) - } + const [regex, ...flags] = pattern.split('\\;') - // Validate website URLs - try { - // eslint-disable-next-line no-new - const { protocol } = new URL(technology.website) + let maxGroups = 0 - if (protocol !== 'http:' && protocol !== 'https:') { - throw new Error('Invalid protocol') - } - } catch (error) { - throw new Error(`Invalid website URL: ${technology.website} (${name})`) - } + flags.forEach((flag) => { + const [key, value] = flag.split(':') - // Validate implies and excludes - const { implies, excludes } = technology + if (key === 'version') { + const refs = value.match(/\\(\d+)/g) - if (implies) { - ;(Array.isArray(implies) ? implies : [implies]).forEach((implied) => { - const [_name, ...flags] = implied.split('\\;') + if (refs) { + maxGroups = refs.reduce((max, ref) => + Math.max(max, parseInt(refs[1] || 0)) + ) + } + } else if (key === 'confidence') { + if ( + !/^\d+$/.test(value) || + parseInt(value, 10) < 0 || + parseInt(value, 10) > 99 + ) { + throw new Error( + `Confidence value must a number between 0 and 99: ${value} (${id})` + ) + } + } else { + throw new Error(`Invalid flag: ${key} (${id})`) + } + }) - const id = `${name}: implies[${implied}]` + // Validate regular expression + try { + // eslint-disable-next-line no-new + new RegExp(regex) + } catch (error) { + throw new Error(`${error.message} (${id})`) + } - if (!technologies[_name]) { - throw new Error(`Implied technology does not exist: ${_name} (${id})`) - } + // Count capture groups + const groups = new RegExp(`${regex}|`).exec('').length - 1 - flags.forEach((flag) => { - const [key, value] = flag.split(':') + if (groups > maxGroups) { + throw new Error( + `Too many non-capturing groups, expected ${maxGroups}: ${regex} (${id})` + ) + } - if (key === 'confidence') { - if ( - !/^\d+$/.test(value) || - parseInt(value, 10) < 0 || - parseInt(value, 10) > 99 - ) { + if (type === 'html' && !/[<>]/.test(regex)) { throw new Error( - `Confidence value must a number between 0 and 99: ${value} (${id})` + `HTML pattern must include < or >: ${regex} (${id})` ) } - } else { - throw new Error(`Invalid flag: ${key} (${id})`) - } + }) }) - }) + } } + ) - if (excludes) { - ;(Array.isArray(excludes) ? excludes : [excludes]).forEach((excluded) => { - const id = `${name}: excludes[${excluded}]` - - if (!technologies[excluded]) { - throw new Error( - `Excluded technology does not exist: ${excluded} (${id})` - ) - } - }) + // Validate categories + technology.cats.forEach((id) => { + if (!categories[id]) { + throw new Error(`No such category: ${id} (${name})`) } }) // Validate icons - fs.readdirSync(iconPath).forEach((file) => { - const filePath = `${iconPath}/${file}` + if (technology.icon && !fs.existsSync(`${iconPath}/${technology.icon}`)) { + throw new Error(`No such icon: ${technology.icon} (${name})`) + } + + // Validate website URLs + try { + // eslint-disable-next-line no-new + const { protocol } = new URL(technology.website) + + if (protocol !== 'http:' && protocol !== 'https:') { + throw new Error('Invalid protocol') + } + } catch (error) { + throw new Error(`Invalid website URL: ${technology.website} (${name})`) + } + + // Validate implies and excludes + const { implies, excludes } = technology + + if (implies) { + ;(Array.isArray(implies) ? implies : [implies]).forEach((implied) => { + const [_name, ...flags] = implied.split('\\;') + + const id = `${name}: implies[${implied}]` - if (fs.statSync(filePath).isFile() && !file.startsWith('.')) { - if (!/^(png|svg)$/i.test(file.split('.').pop())) { - throw new Error(`Incorrect file type, expected PNG or SVG: ${filePath}`) + if (!technologies[_name]) { + throw new Error(`Implied technology does not exist: ${_name} (${id})`) } - if (!Object.values(technologies).some(({ icon }) => icon === file)) { - throw new Error(`Extraneous file: ${filePath}}`) + flags.forEach((flag) => { + const [key, value] = flag.split(':') + + if (key === 'confidence') { + if ( + !/^\d+$/.test(value) || + parseInt(value, 10) < 0 || + parseInt(value, 10) > 99 + ) { + throw new Error( + `Confidence value must a number between 0 and 99: ${value} (${id})` + ) + } + } else { + throw new Error(`Invalid flag: ${key} (${id})`) + } + }) + }) + } + + if (excludes) { + ;(Array.isArray(excludes) ? excludes : [excludes]).forEach((excluded) => { + const id = `${name}: excludes[${excluded}]` + + if (!technologies[excluded]) { + throw new Error( + `Excluded technology does not exist: ${excluded} (${id})` + ) } + }) + } +}) + +// Validate icons +fs.readdirSync(iconPath).forEach((file) => { + const filePath = `${iconPath}/${file}` + + if (fs.statSync(filePath).isFile() && !file.startsWith('.')) { + if (!/^(png|svg)$/i.test(file.split('.').pop())) { + throw new Error(`Incorrect file type, expected PNG or SVG: ${filePath}`) } - }) -} catch (error) { - // eslint-disable-next-line no-console - console.error(error.message) -} + + if ( + !Object.values(technologies).some(({ icon }) => icon === file) && + file !== 'default.svg' + ) { + throw new Error(`Extraneous file: ${filePath}}`) + } + } +}) diff --git a/package.json b/package.json index c24bd1c32..6e6ff2e39 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,17 @@ "@nuxtjs/eslint-config": "^3.1.0", "@nuxtjs/eslint-module": "^2.0.0", "babel-eslint": "^10.1.0", - "eslint": "^7.7.0", - "eslint-config-prettier": "^6.11.0", + "eslint": "^7.13.0", + "eslint-config-prettier": "^6.15.0", + "eslint-plugin-json": "^2.1.2", "eslint-plugin-nuxt": "^1.0.0", "eslint-plugin-prettier": "^3.1.4", - "prettier": "^2.1.0" + "prettier": "^2.1.2" }, "scripts": { "link": "node ./bin/link.js", - "lint": "eslint --fix src/**/*.js", + "lint": "eslint src/**/*.{js,json}", + "lint:fix": "eslint --fix src/**/*.{js,json}", "validate": "yarn run lint && jsonlint -qV ./schema.json ./src/technologies.json && node ./bin/validate.js", "convert": "cd ./src/drivers/webextension/images/icons ; cp *.svg converted ; cd converted ; convert-svg-to-png *.svg --width 32 --height 32 ; rm *.svg", "prettify": "jsonlint -si --trim-trailing-commas --enforce-double-quotes ./src/technologies.json", diff --git a/schema.json b/schema.json index 21ad68335..82f938070 100644 --- a/schema.json +++ b/schema.json @@ -41,6 +41,19 @@ "type": "string", "pattern": "^.{0,500}$" }, + "oss": { + "type": "boolean" + }, + "saas": { + "type": "boolean" + }, + "pricing": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(low|mid|high|freemium|poa|payg|onetime|recurring)$" + } + }, "cats": { "type": "array", "items": { @@ -73,6 +86,22 @@ } } }, + "dom": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + } + } + }, + "dns": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^.+$": { + } + } + }, "headers": { "type": "object", "additionalProperties": false, diff --git a/src/drivers/npm/README.md b/src/drivers/npm/README.md index 9bba60c30..7c1d950e6 100644 --- a/src/drivers/npm/README.md +++ b/src/drivers/npm/README.md @@ -66,9 +66,9 @@ const options = { htmlMaxRows: 2000, }; -;(async function() { - const wappalyzer = await new Wappalyzer(options) +const wappalyzer = new Wappalyzer(options) +;(async function() { try { await wappalyzer.init() @@ -98,9 +98,9 @@ const Wappalyzer = require('wappalyzer'); const urls = ['https://www.wappalyzer.com', 'https://www.example.com'] -;(async function() { - const wappalyzer = await new Wappalyzer() +const wappalyzer = new Wappalyzer() +;(async function() { try { await wappalyzer.init() diff --git a/src/drivers/npm/cli.js b/src/drivers/npm/cli.js index 1cab50af9..9e2644bdb 100755 --- a/src/drivers/npm/cli.js +++ b/src/drivers/npm/cli.js @@ -77,7 +77,7 @@ Options: } ;(async function () { - const wappalyzer = await new Wappalyzer(options) + const wappalyzer = new Wappalyzer(options) try { await wappalyzer.init() diff --git a/src/drivers/npm/driver.js b/src/drivers/npm/driver.js index 8e9a62156..52f40d865 100644 --- a/src/drivers/npm/driver.js +++ b/src/drivers/npm/driver.js @@ -1,5 +1,6 @@ const { URL } = require('url') const fs = require('fs') +const dns = require('dns').promises const path = require('path') const http = require('http') const https = require('https') @@ -67,12 +68,41 @@ 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 return new Promise((resolve, reject) => - get(url.href, (response) => { + get(url, { rejectUnauthorized: false }, (response) => { if (response.statusCode >= 400) { return reject( new Error(`${response.statusCode} ${response.statusMessage}`) @@ -86,7 +116,7 @@ function get(url) { response.on('data', (data) => (body += data)) response.on('error', (error) => reject(new Error(error.message))) response.on('end', () => resolve(body)) - }) + }).on('error', (error) => reject(new Error(error.message))) ) } else { throw new Error(`Invalid protocol: ${url.protocol}`) @@ -127,6 +157,8 @@ class Driver { try { this.browser = await puppeteer.launch({ + ignoreHTTPSErrors: true, + acceptInsecureCerts: true, args: chromiumArgs, executablePath: await chromiumBin, }) @@ -135,10 +167,15 @@ class Driver { this.log('Browser disconnected') if (!this.destroyed) { - await this.init() + try { + await this.init() + } catch (error) { + this.log(error.toString()) + } } }) } catch (error) { + console.log('a', error) throw new Error(error.toString()) } } @@ -154,6 +191,7 @@ class Driver { this.log('Browser closed') } catch (error) { + console.log('b', error) throw new Error(error.toString()) } } @@ -173,7 +211,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, @@ -185,6 +227,7 @@ class Site { try { this.originalUrl = new URL(url) } catch (error) { + console.log('c', error) throw new Error(error.toString()) } @@ -194,6 +237,8 @@ class Site { this.listeners = {} this.pages = [] + + this.dns = [] } log(message, source = 'driver', type = 'log') { @@ -263,7 +308,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() @@ -299,6 +348,7 @@ class Site { request.continue({ headers }) } } catch (error) { + console.log('d', error) this.error(error) } }) @@ -337,10 +387,11 @@ class Site { this.onDetect(analyze({ headers, certIssuer })) - await this.emit('response', { page, response }) + await this.emit('response', { page, response, headers, certIssuer }) } } } catch (error) { + console.log('e', error) this.error(error) } }) @@ -357,6 +408,8 @@ class Site { await sleep(1000) + // page.on('console', (message) => this.log(message.text())) + // Links const links = await this.promiseTimeout( ( @@ -480,11 +533,88 @@ class Site { ) ) + // 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 nodes = document.querySelectorAll(selector) + + if (!nodes.length) { + return + } + + dom[selector].forEach(({ text, properties, attributes }) => { + nodes.forEach((node) => { + if (text) { + const value = node.textContent.trim() + + if (value) { + technologies.push({ + name, + selector, + text: value, + }) + } + } + + 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)) { + const value = node.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( (cookies, { name, value }) => ({ ...cookies, - [name]: [value], + [name.toLowerCase()]: [value], }), {} ) @@ -513,8 +643,63 @@ class Site { html = batches.join('\n') } + // DNS + if (!Object.keys(this.dns).length) { + const records = {} + const resolve = (func, hostname) => { + return this.promiseTimeout( + func(hostname).catch((error) => { + if (error.code !== 'ENODATA') { + this.error(error) + } + + return [] + }) + ) + } + + const domain = url.hostname.replace(/^www\./, '') + + ;[ + records.cname, + records.ns, + records.mx, + records.txt, + records.soa, + ] = await Promise.all([ + resolve(dns.resolveCname, url.hostname), + resolve(dns.resolveNs, domain), + resolve(dns.resolveMx, domain), + resolve(dns.resolveTxt, domain), + resolve(dns.resolveSoa, domain), + ]) + + this.dns = Object.keys(records).reduce((dns, type) => { + dns[type] = dns[type] || [] + + Array.prototype.push.apply( + dns[type], + Array.isArray(records[type]) + ? records[type].map((value) => { + return typeof value === 'object' + ? Object.values(value).join(' ') + : value + }) + : [Object.values(records[type]).join(' ')] + ) + + return dns + }, {}) + + this.onDetect(analyze({ dns: this.dns })) + } + // Validate response - if (url.protocol !== 'file:' && !this.analyzedUrls[url.href].status) { + if ( + url.protocol !== 'file:' && + this.analyzedUrls[url.href] && + !this.analyzedUrls[url.href].status + ) { await page.close() this.log('Page closed') @@ -522,8 +707,8 @@ class Site { throw new Error('No response from server') } + this.onDetect(analyzeDom(dom)) this.onDetect(analyzeJs(js)) - this.onDetect( analyze({ url, @@ -546,9 +731,8 @@ class Site { ) && link.protocol && link.protocol.match(/https?:/) && - link.rel !== 'nofollow' && link.hostname === url.hostname && - extensions.test(link.pathname) + extensions.test(link.pathname.slice(-5)) ) { results.push(new URL(link.href.split('#')[0])) } @@ -567,6 +751,7 @@ class Site { meta, js, links: reducedLinks, + dns: this.dns, }) await page.close() @@ -575,10 +760,15 @@ class Site { return reducedLinks } catch (error) { + console.log('f', error) if (error.constructor.name === 'TimeoutError') { throw new Error('The website took too long to respond') } + if (error.message.includes('net::ERR_NAME_NOT_RESOLVED')) { + throw new Error('Hostname could not be resolved') + } + throw new Error(error.message) } } @@ -599,6 +789,7 @@ class Site { await this.batch(links.slice(0, this.options.maxUrls), depth + 1) } } catch (error) { + console.log('g', error) this.analyzedUrls[url.href] = { status: 0, error: error.message || error.toString(), diff --git a/src/drivers/npm/package.json b/src/drivers/npm/package.json index f156672ac..b80ce1a4b 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.6", + "version": "6.5.22", "author": "Wappalyzer", "license": "MIT", "repository": { @@ -25,7 +25,7 @@ "url": "https://github.com/sponsors/aliasio" }, { - "url": "https://paypal.me/aliasio" + "url": "https://paypal.me/elbertalias" } ], "main": "driver.js", @@ -42,4 +42,4 @@ "dependencies": { "puppeteer": "^5.3.0" } -} +} \ No newline at end of file diff --git a/src/drivers/webextension/_locales/ca/messages.json b/src/drivers/webextension/_locales/ca/messages.json index c5053ff24..6c155e8a4 100644 --- a/src/drivers/webextension/_locales/ca/messages.json +++ b/src/drivers/webextension/_locales/ca/messages.json @@ -1,24 +1,26 @@ { - "github": { "message": "Fork Wappalyzer a GitHub!" }, + "github": { "message": "Bifurcar Wappalyzer a GitHub" }, "twitter": { "message": "Seguir Wappalyzer a Twitter" }, "website": { "message": "Anar a wappalyzer.com" }, "options": { "message": "Opcions" }, "optionsSave": { "message": "Desar opcions" }, "optionsSaved": { "message": "Desat" }, - "optionUpgradeMessage": { "message": "Avisar-me quan hi hagi una actualització disponible" }, + "optionUpgradeMessage": { "message": "Notificar les actualitzacions disponibles" }, "optionDynamicIcon": { "message": "Utilitzar la icona de la tecnologia enlloc del logotip de Wappalyzer" }, "optionTracking": { "message": "Enviar les tecnologies identificades de forma anònima a wappalyzer.com" }, - "optionThemeMode": { "message": "Habilitar la compatibilitat de la manera fosc." }, - "optionBadge": { "message": "Show the number of identified technologies on the icon" }, - "disableOnDomain": { "message": "Disable on this website" }, - "clearCache": { "message": "Clear cached detections" }, + "optionThemeMode": { "message": "Habilitar la compatibilitat de l'aspecte fosc" }, + "optionBadge": { "message": "Mostrar el nombre de tecnologies identificades en la icona" }, + "disableOnDomain": { "message": "Desactivar en aquest web" }, + "clearCache": { "message": "Esborrar la memòria cau de les deteccions" }, "nothingToDo": { "message": "Res a fer aquí." }, "noAppsDetected": { "message": "No s'ha detectat cap tecnologia." }, "categoryPin": { "message": "Mostrar sempre la icona" }, - "termsAccept": { "message": "Acceptar" }, + "termsAccept": { "message": "M'està bé" }, + "termsDecline": { "message": "Desactivar" }, "termsContent": { "message": "Aquesta extensió envia informació anònima sobre els llocs web que visiteu, inclosos el nom de domini i les tecnologies identificades a wappalyzer.com. Això pot desactivar-se a Opcions." }, "privacyPolicy": { "message": "Política de privadesa" }, - "createAlert": { "message": "Create an alert for this website" }, + "createAlert": { "message": "Crear una alerta per aquest web" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Taulers de missatgeria" }, "categoryName3": { "message": "Gestor de bases de dades" }, @@ -35,7 +37,7 @@ "categoryName14": { "message": "Reproductors de vídeo" }, "categoryName15": { "message": "Sistemes de comentaris" }, "categoryName16": { "message": "Security" }, - "categoryName17": { "message": "Font Script" }, + "categoryName17": { "message": "Tipografies" }, "categoryName18": { "message": "Marcs web" }, "categoryName19": { "message": "Miscel·lània" }, "categoryName20": { "message": "Editors" }, @@ -50,7 +52,7 @@ "categoryName29": { "message": "Motors de cerca" }, "categoryName30": { "message": "Correu web" }, "categoryName31": { "message": "CDN" }, - "categoryName32": { "message": "Marketing Automation" }, + "categoryName32": { "message": "Automatitzacions de màrqueting" }, "categoryName33": { "message": "Extensions del servidor web" }, "categoryName34": { "message": "Bases de dades" }, "categoryName35": { "message": "Mapes" }, @@ -64,7 +66,7 @@ "categoryName43": { "message": "Paywall" }, "categoryName44": { "message": "Sistemes Build/CI" }, "categoryName45": { "message": "Sistemes SCADA" }, - "categoryName46": { "message": "Accés remot" }, + "categoryName46": { "message": "Accessos remots" }, "categoryName47": { "message": "Eines de desenvolupament" }, "categoryName48": { "message": "Emmagatzematge de xarxa" }, "categoryName49": { "message": "Lectors de canals" }, @@ -76,7 +78,7 @@ "categoryName55": { "message": "Comptabilitat" }, "categoryName56": { "message": "Cryptominer" }, "categoryName57": { "message": "Generadors de llocs estàtics" }, - "categoryName58": { "message": "User Onboarding" }, + "categoryName58": { "message": "Incorporacions d'usuaris" }, "categoryName59": { "message": "Llibreries JavaScript" }, "categoryName60": { "message": "Contenidors" }, "categoryName61": { "message": "SaaS" }, @@ -84,9 +86,14 @@ "categoryName63": { "message": "IaaS" }, "categoryName64": { "message": "Proxys invers" }, "categoryName65": { "message": "Balanceigs de càrrega" }, - "categoryName66": { "message": "UI Frameworks" }, + "categoryName66": { "message": "Marcs UI" }, "categoryName67": { "message": "Cookie compliance" }, - "categoryName68": { "message": "Accessibility"}, - "categoryName69": { "message": "Social login"}, - "categoryName70": { "message": "SSL/TLS certificate authority"} + "categoryName68": { "message": "Accesibilitat" }, + "categoryName69": { "message": "Inicis de sessió socials" }, + "categoryName70": { "message": "Autoritats de certificació SSL/TLS" }, + "categoryName71": { "message": "Programes d'afiliació" }, + "categoryName72": { "message": "Programacions de cites" }, + "categoryName73": { "message": "Enquestes" }, + "categoryName74": { "message": "Testeigs A/B" }, + "categoryName75": { "message": "Correus electrònics" } } diff --git a/src/drivers/webextension/_locales/de/messages.json b/src/drivers/webextension/_locales/de/messages.json index 631d7010d..1ebb87c26 100644 --- a/src/drivers/webextension/_locales/de/messages.json +++ b/src/drivers/webextension/_locales/de/messages.json @@ -1,92 +1,99 @@ { - "github": { "message": "Forke Wappalyzer bei GitHub!" }, - "twitter": { "message": "Folge Wappalyzer bei Twitter" }, - "website": { "message": "Gehe zu wappalyzer.com" }, - "options": { "message": "Optionen" }, - "optionsSave": { "message": "Optionen speichern" }, - "optionsSaved": { "message": "Gespeichert" }, - "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" }, - "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" }, - "categoryName1": { "message": "CMS" }, - "categoryName2": { "message": "Nachrichten Board" }, - "categoryName3": { "message": "Datenbankverwaltung" }, - "categoryName4": { "message": "Dokumentations Tool" }, - "categoryName5": { "message": "Widget" }, - "categoryName6": { "message": "Ecommerce" }, - "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" }, - "categoryName15": { "message": "Kommentarsystem" }, - "categoryName16": { "message": "Security" }, - "categoryName17": { "message": "Schrift Script" }, - "categoryName18": { "message": "Web Framework" }, - "categoryName19": { "message": "Sonstiges" }, - "categoryName20": { "message": "Editor" }, - "categoryName21": { "message": "LMS" }, - "categoryName22": { "message": "Web Server" }, - "categoryName23": { "message": "Cache Tool" }, - "categoryName24": { "message": "Rich Text Editor" }, - "categoryName25": { "message": "JavaScript Graphics" }, - "categoryName26": { "message": "Mobile Framework" }, - "categoryName27": { "message": "Programmiersprache" }, - "categoryName28": { "message": "Betriebssystem" }, - "categoryName29": { "message": "Suchmaschine" }, - "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" }, - "categoryName38": { "message": "Medienserver" }, - "categoryName39": { "message": "Webcam" }, - "categoryName40": { "message": "Drucker" }, - "categoryName41": { "message": "Zahlungsverarbeiter" }, - "categoryName42": { "message": "Schlagwort Manager" }, - "categoryName43": { "message": "Bezahlblockade" }, - "categoryName44": { "message": "Build/CI-System" }, - "categoryName45": { "message": "SCADA System" }, - "categoryName46": { "message": "Fernzugriff" }, - "categoryName47": { "message": "Entwicklungswerkzeug" }, - "categoryName48": { "message": "Netzwerkspeicher" }, - "categoryName49": { "message": "Feedleser" }, - "categoryName50": { "message": "Dokumentmanagementsysteme" }, - "categoryName51": { "message": "Startseitenersteller" }, - "categoryName52": { "message": "Live-Chat" }, - "categoryName53": { "message": "CRM" }, + "github": { "message": "Forke Wappalyzer bei GitHub!" }, + "twitter": { "message": "Folge Wappalyzer bei Twitter" }, + "website": { "message": "Gehe zu wappalyzer.com" }, + "options": { "message": "Optionen" }, + "optionsSave": { "message": "Optionen speichern" }, + "optionsSaved": { "message": "Gespeichert" }, + "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": "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 Applikationen gefunden" }, + "categoryPin": { "message": "Icon immer anzeigen" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, + "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" }, + "leadLists": { "message": "Lead generation tools" }, + "categoryName1": { "message": "CMS" }, + "categoryName2": { "message": "Nachrichten Board" }, + "categoryName3": { "message": "Datenbankverwaltung" }, + "categoryName4": { "message": "Dokumentations Tool" }, + "categoryName5": { "message": "Widget" }, + "categoryName6": { "message": "E-Commerce" }, + "categoryName7": { "message": "Fotogalerien" }, + "categoryName8": { "message": "Wikis" }, + "categoryName9": { "message": "Hosting-Panels" }, + "categoryName10": { "message": "Statistiken" }, + "categoryName11": { "message": "Blog" }, + "categoryName12": { "message": "JavaScript Frameworks" }, + "categoryName13": { "message": "Ticketsysteme" }, + "categoryName14": { "message": "Videoplayer" }, + "categoryName15": { "message": "Kommentarsystem" }, + "categoryName16": { "message": "Security" }, + "categoryName17": { "message": "Schrift Script" }, + "categoryName18": { "message": "Web Frameworks" }, + "categoryName19": { "message": "Sonstiges" }, + "categoryName20": { "message": "Editor" }, + "categoryName21": { "message": "LMS" }, + "categoryName22": { "message": "Web Server" }, + "categoryName23": { "message": "Cache Tool" }, + "categoryName24": { "message": "Rich Text Editor" }, + "categoryName25": { "message": "JavaScript Graphics" }, + "categoryName26": { "message": "Mobile Framework" }, + "categoryName27": { "message": "Programmiersprache" }, + "categoryName28": { "message": "Betriebssysteme" }, + "categoryName29": { "message": "Suchmaschinen" }, + "categoryName30": { "message": "Webmail" }, + "categoryName31": { "message": "CDN" }, + "categoryName32": { "message": "Marketing Automation" }, + "categoryName33": { "message": "Web Server Erweiterungen" }, + "categoryName34": { "message": "Datenbanken" }, + "categoryName35": { "message": "Karten" }, + "categoryName36": { "message": "Werbenetzwerke" }, + "categoryName37": { "message": "Netzwerkdienste" }, + "categoryName38": { "message": "Medienserver" }, + "categoryName39": { "message": "Web-Kameras" }, + "categoryName40": { "message": "Drucker" }, + "categoryName41": { "message": "Zahlungsverarbeiter" }, + "categoryName42": { "message": "Tag Manager" }, + "categoryName43": { "message": "Bezahlblockade" }, + "categoryName44": { "message": "CI-Systeme" }, + "categoryName45": { "message": "SCADA System" }, + "categoryName46": { "message": "Fernzugriff" }, + "categoryName47": { "message": "Entwicklungswerkzeuge" }, + "categoryName48": { "message": "Netzwerkspeicher" }, + "categoryName49": { "message": "Feedleser" }, + "categoryName50": { "message": "Dokumentmanagementsysteme" }, + "categoryName51": { "message": "Website Baukästen" }, + "categoryName52": { "message": "Live-Chat" }, + "categoryName53": { "message": "CRM" }, "categoryName54": { "message": "SEO" }, - "categoryName55": { "message": "Buchhaltung" }, - "categoryName56": { "message": "Cryptominer" }, - "categoryName57": { "message": "Statischer Seitengenerator" }, - "categoryName58": { "message": "Benutzer-Einbindung" }, - "categoryName59": { "message": "JavaScript Bibliotheken" }, - "categoryName60": { "message": "Containers" }, - "categoryName61": { "message": "SaaS" }, - "categoryName62": { "message": "PaaS" }, - "categoryName63": { "message": "IaaS" }, - "categoryName64": { "message": "Reverse Proxy" }, - "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"} + "categoryName55": { "message": "Buchhaltung" }, + "categoryName56": { "message": "Cryptominer" }, + "categoryName57": { "message": "Statischer Seitengenerator" }, + "categoryName58": { "message": "Benutzer-Onboarding" }, + "categoryName59": { "message": "JavaScript Bibliotheken" }, + "categoryName60": { "message": "Container" }, + "categoryName61": { "message": "SaaS" }, + "categoryName62": { "message": "PaaS" }, + "categoryName63": { "message": "IaaS" }, + "categoryName64": { "message": "Reverse Proxies" }, + "categoryName65": { "message": "Load Balancer" }, + "categoryName66": { "message": "UI Frameworks" }, + "categoryName67": { "message": "Cookie compliance" }, + "categoryName68": { "message": "Barrierefreiheit" }, + "categoryName69": { "message": "Social Login" }, + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Partnerprogram" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/el/messages.json b/src/drivers/webextension/_locales/el/messages.json index d96216826..da4d31f66 100644 --- a/src/drivers/webextension/_locales/el/messages.json +++ b/src/drivers/webextension/_locales/el/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Καμία ενέργεια." }, "noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Διαδικτυακό Φόρουμ" }, "categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" }, @@ -83,6 +85,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/en/messages.json b/src/drivers/webextension/_locales/en/messages.json index 453b284bb..f4430eef6 100644 --- a/src/drivers/webextension/_locales/en/messages.json +++ b/src/drivers/webextension/_locales/en/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Nothing to do here." }, "noAppsDetected": { "message": "No technologies detected." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Message boards" }, "categoryName3": { "message": "Database managers" }, @@ -85,6 +87,11 @@ "categoryName66": { "message": "UI frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/es/messages.json b/src/drivers/webextension/_locales/es/messages.json index b073fb5aa..8862e3e0c 100644 --- a/src/drivers/webextension/_locales/es/messages.json +++ b/src/drivers/webextension/_locales/es/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Nada que hacer aquí." }, "noAppsDetected": { "message": "Aplicaciones no detectadas." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "Gestor de Contenido" }, "categoryName2": { "message": "Foro" }, "categoryName3": { "message": "Gestor de Bases de Datos" }, @@ -55,7 +57,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" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/fa/messages.json b/src/drivers/webextension/_locales/fa/messages.json index a1e65d234..b4db06e3b 100644 --- a/src/drivers/webextension/_locales/fa/messages.json +++ b/src/drivers/webextension/_locales/fa/messages.json @@ -15,10 +15,12 @@ "clearCache": { "message": "پاکسازی شناسایی های کش شده" }, "noAppsDetected": { "message": "هیچ فن‌آوری شناسایی نشده است." }, "categoryPin": { "message": "همیشه نماد را نشان بده" }, - "termsAccept": { "message": "قبول" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "این افزونه اطلاعات وب‌سایت‌های بازدید شده توسط شما را به صورت ناشناس ارسال می‌کند، مانند آدرس سایت و تکنولوژی‌های استفاده شده در آن سایت را ارسال می‌کند. اطلاعات بیشتر در wappalyzer.com. شما می‌توانید این افزونه را غیرفعال کنید." }, "privacyPolicy": { "message": "سیاست حفظ حریم خصوصی" }, "createAlert": { "message": "ساخت یک هشدار برای این وبسایت" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "سیستم مدیریت محتوا" }, "categoryName2": { "message": "انجمن پیام" }, "categoryName3": { "message": "مدیریت پایگاه داده" }, @@ -88,5 +90,10 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "دسترسی" }, "categoryName69": { "message": "ورود به شبکه های اجتماعی" }, - "categoryName70": { "message": "صادر کننده SSL/TLS" } + "categoryName70": { "message": "صادر کننده SSL/TLS" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/fr/messages.json b/src/drivers/webextension/_locales/fr/messages.json index 9f58d9b7f..a2ec5644f 100644 --- a/src/drivers/webextension/_locales/fr/messages.json +++ b/src/drivers/webextension/_locales/fr/messages.json @@ -15,10 +15,12 @@ "twitter": { "message": "Suivre Wappalyzer sur Twitter" }, "website": { "message": "Aller sur wappalyzer.com" }, "categoryPin": { "message": " Toujours afficher l'icône" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Forum" }, "categoryName3": { "message": "Gestionnaire de base de données" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/gl_ES/messages.json b/src/drivers/webextension/_locales/gl_ES/messages.json index fb8fa26fd..fd48da1b0 100644 --- a/src/drivers/webextension/_locales/gl_ES/messages.json +++ b/src/drivers/webextension/_locales/gl_ES/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Nada que facer por aquí." }, "noAppsDetected": { "message": "Non se identificaron aplicativos." }, "categoryPin": { "message": "Amosar sempre icono" }, - "termsAccept": { "message": "Aceptar" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "Esta extensión envía anonimamente información acerca das webs que visitas, incluindo dominio e aplicativos identificados, a wappalyzer.com. Isto pode ser desactivado nas preferencias." }, "privacyPolicy": { "message": "Política de privacidade" }, "createAlert": { "message": "Create an alert for this website" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Taboleiro de mensaxes" }, "categoryName3": { "message": "Xestor de base de datos" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/gr/messages.json b/src/drivers/webextension/_locales/gr/messages.json index fcd10ad82..e46f38736 100644 --- a/src/drivers/webextension/_locales/gr/messages.json +++ b/src/drivers/webextension/_locales/gr/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Καμία ενέργεια." }, "noAppsDetected": { "message": "Δεν ανιχνεύθηκαν εφαρμογές." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Διαδικτυακό Φόρουμ" }, "categoryName3": { "message": "Διαχειριστής Βάσης Δεδομένων" }, @@ -83,6 +85,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/id/messages.json b/src/drivers/webextension/_locales/id/messages.json index a0d1b2897..bee46f188 100644 --- a/src/drivers/webextension/_locales/id/messages.json +++ b/src/drivers/webextension/_locales/id/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Tak ada yang dilakukan disini." }, "noAppsDetected": { "message": "Tidak ada aplikasi yang terdeteksi." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "Sistem Pengelola Konten" }, "categoryName2": { "message": "Papan Pesan" }, "categoryName3": { "message": "Pengelola Basis Data" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/it/messages.json b/src/drivers/webextension/_locales/it/messages.json index a80798521..2a066531b 100644 --- a/src/drivers/webextension/_locales/it/messages.json +++ b/src/drivers/webextension/_locales/it/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Niente da fare qui." }, "noAppsDetected": { "message": "Nessuna applicazione rilevata." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Forum" }, "categoryName3": { "message": "Gestore di Database" }, @@ -88,5 +90,10 @@ "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" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/ja/messages.json b/src/drivers/webextension/_locales/ja/messages.json index 656901314..c74f77a5e 100644 --- a/src/drivers/webextension/_locales/ja/messages.json +++ b/src/drivers/webextension/_locales/ja/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "ここでは特定出来ません。" }, "noAppsDetected": { "message": "技術は検出されませんでした。" }, "categoryPin": { "message": "常にアイコンを表示" }, - "termsAccept": { "message": "受諾する" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "この拡張機能は、ドメイン名や特定された技術など、アクセスしたWebサイトに関する匿名情報をwappalyzer.comに送信します。これは設定で無効にできます。" }, "privacyPolicy": { "message": "プライバシーポリシー" }, "createAlert": { "message": "Create an alert for this website" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "メッセージボード" }, "categoryName3": { "message": "データベースマネージャー" }, @@ -88,5 +90,10 @@ "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" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/ko/messages.json b/src/drivers/webextension/_locales/ko/messages.json index 86a217896..d981343b6 100644 --- a/src/drivers/webextension/_locales/ko/messages.json +++ b/src/drivers/webextension/_locales/ko/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "여기에는 할 일이 없네요." }, "noAppsDetected": { "message": "식별된 기술이 없습니다." }, "categoryPin": { "message": "항상 아이콘 보이기" }, - "termsAccept": { "message": "수락" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "이 확장 기능은 사이트의 도메인과 식별된 기술을 포함한 익명 정보를 wappalyzer.com에 전송합니다. 이 기능은 설정에서 비활성화 할 수 있습니다." }, "privacyPolicy": { "message": "개인정보처리방침" }, "createAlert": { "message": "이 웹 사이트에 대한 알림 받기" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "포럼 소프트웨어" }, "categoryName3": { "message": "데이터베이스 관리 도구" }, @@ -86,5 +88,10 @@ "categoryName67": { "message": "쿠키 동의" }, "categoryName68": { "message": "접근성" }, "categoryName69": { "message": "소셜 로그인" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/pl/messages.json b/src/drivers/webextension/_locales/pl/messages.json index d000e321f..98c81a5c7 100644 --- a/src/drivers/webextension/_locales/pl/messages.json +++ b/src/drivers/webextension/_locales/pl/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Nic tu nie ma." }, "noAppsDetected": { "message": "Nie wykryto żadnych aplikacji." }, "categoryPin": { "message": "Zawsze pokazuj tą ikonę" }, - "termsAccept": { "message": "Akceptuj" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "To rozszerzenie wysyła anonimowe informacje o stronach, które odwiedzasz, uwzględniając nazwy domen i zidentyfikowane technologie do wappalyzer.com. Opcja może zostać wyłączona w ustawieniach." }, "privacyPolicy": { "message": "Privacy policy" }, "createAlert": { "message": "Create an alert for this website" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "System zarządzania treścią" }, "categoryName2": { "message": "Forum" }, "categoryName3": { "message": "Menedżer baz danych" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/pt/messages.json b/src/drivers/webextension/_locales/pt/messages.json index c4c41277d..38569e6ea 100644 --- a/src/drivers/webextension/_locales/pt/messages.json +++ b/src/drivers/webextension/_locales/pt/messages.json @@ -15,10 +15,12 @@ "twitter": { "message": "Seguir Wappalyzer no Twitter" }, "website": { "message": "Ir para wappalyzer.com" }, "categoryPin": { "message": "Mostrar sempre ícone" }, - "termsAccept": { "message": "Aceitar" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "Esta extensão envia informações anónimas sobre os sites que visitas, incluindo o nome de domínio e as tecnologias identificadas, para o wappalyzer.com. Isso pode ser desativado nas configurações." }, "privacyPolicy": { "message": "Políticas de Privacidade" }, "createAlert": { "message": "Create an alert for this website" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Fórum" }, "categoryName3": { "message": "Gestor de Base de Dados" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/pt_BR/messages.json b/src/drivers/webextension/_locales/pt_BR/messages.json index 36f90a93f..5d36f4b55 100644 --- a/src/drivers/webextension/_locales/pt_BR/messages.json +++ b/src/drivers/webextension/_locales/pt_BR/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Nada a fazer aqui." }, "noAppsDetected": { "message": "Nenhuma tecnologia identificada." }, "categoryPin": { "message": "Sempre mostrar ícone" }, - "termsAccept": { "message": "Aceitar" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "Esta extensão envia informações anônimas sobre os sites que você visita, incluindo domínio e tecnologias identificadas para wappalyzer.com. Este comportamento pode ser desativado nas configurações." }, "privacyPolicy": { "message": "Privacy policy" }, "createAlert": { "message": "Create an alert for this website" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Fórum" }, "categoryName3": { "message": "Gestão de Banco de Dados" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/ro/messages.json b/src/drivers/webextension/_locales/ro/messages.json index fb621d40c..7bea53eee 100644 --- a/src/drivers/webextension/_locales/ro/messages.json +++ b/src/drivers/webextension/_locales/ro/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Nimic de făcut pe pagina curentă." }, "noAppsDetected": { "message": "Nici o aplicație detectată." }, "categoryPin": { "message": "Afișează icon tot timpul" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Forum de discuții" }, "categoryName3": { "message": "Manager baze de date" }, @@ -83,6 +85,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/ru/messages.json b/src/drivers/webextension/_locales/ru/messages.json index e1fd536ee..a1561c268 100644 --- a/src/drivers/webextension/_locales/ru/messages.json +++ b/src/drivers/webextension/_locales/ru/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Здесь нечего делать" }, "noAppsDetected": { "message": "Не удалось определить ни одну технологию" }, "categoryPin": { "message": "Всегда отображать эту категорию иконкой"}, - "termsAccept": { "message": "Принять" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "Расширение отправляет обезличенную статистику посещенных сайтов, включая доменное имя и распознанные технологии на wappalyzer.com. Это можно отключить в настройках." }, "privacyPolicy": { "message": "Политика конфиденциальности" }, "createAlert": { "message": "Отправить жалобу на этот сайт" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Форум" }, "categoryName3": { "message": "Менеджер БД" }, @@ -85,6 +87,11 @@ "categoryName66": { "message": "UI Фреймворк" }, "categoryName67": { "message": "Соответствие cookie" }, "categoryName68": { "message": "Доступность" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/sk/messages.json b/src/drivers/webextension/_locales/sk/messages.json index f5095fa72..d65f4b933 100644 --- a/src/drivers/webextension/_locales/sk/messages.json +++ b/src/drivers/webextension/_locales/sk/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Nie je tu čo robiť." }, "noAppsDetected": { "message": "Žiadne aplikácie neboli zistené." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Message Board" }, "categoryName3": { "message": "Správca databáz" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/tr/messages.json b/src/drivers/webextension/_locales/tr/messages.json index 832a58bc3..a69f197f9 100644 --- a/src/drivers/webextension/_locales/tr/messages.json +++ b/src/drivers/webextension/_locales/tr/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Burada yapacak birşey yok." }, "noAppsDetected": { "message": "Uygulamalar tespit edilemedi." }, "categoryPin": { "message": "Her zaman bu kategorinin ikonunu kullan" }, - "termsAccept": { "message": "Kabul Ediyorum" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "Bu eklenti, ziyaret ettiğiniz web site bilgilerini, alan adları ve tespit edilen teknolojiler ile beraber anonim olarak wappalyzer.com'a gönderir. Bunu, eklenti ayarlarından değiştirebilirsiniz." }, "privacyPolicy": { "message": "Privacy policy" }, "createAlert": { "message": "Create an alert for this website" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "İçerik Yönetim Sistemi" }, "categoryName2": { "message": "Mesaj Tahtası" }, "categoryName3": { "message": "Veritabanı Yöneticisi" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Frameworks" }, "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs"}, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/uk/messages.json b/src/drivers/webextension/_locales/uk/messages.json index a6f7d5daa..29dc12412 100644 --- a/src/drivers/webextension/_locales/uk/messages.json +++ b/src/drivers/webextension/_locales/uk/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Тут нічого робити." }, "noAppsDetected": { "message": "Нічого не знайдено." }, "categoryPin": { "message": "Завжди показувати іконку Wappalyzer" }, - "termsAccept": { "message": "Доступ" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "Це розширення надсилає на Wapplayzer.com анонімну інформацію про відвідувані вами веб-сайти, включаючи доменні імена та визначені технології. Це можна відключити в налаштуваннях." }, "privacyPolicy": { "message": "Політика приватності" }, "createAlert": { "message": "Поскаржитись на цей сайт" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS" }, "categoryName2": { "message": "Форум" }, "categoryName3": { "message": "Менеджер БД" }, @@ -87,6 +89,11 @@ "categoryName66": { "message": "UI Каркаси" }, "categoryName67": { "message": "Відповідність файлам cookie" }, "categoryName68": { "message": "Доступність" }, - "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/uz/messages.json b/src/drivers/webextension/_locales/uz/messages.json index af62b2567..7f1989fa2 100644 --- a/src/drivers/webextension/_locales/uz/messages.json +++ b/src/drivers/webextension/_locales/uz/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "Bu yerda tekshirib bolmaydi." }, "noAppsDetected": { "message": "Hech qanday dastur aniqlanmadi." }, "categoryPin": { "message": "Always show icon" }, - "termsAccept": { "message": "Accept" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "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" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "CMS (KBT)" }, "categoryName2": { "message": "Forum" }, "categoryName3": { "message": "MB boshqaruvi" }, @@ -86,7 +88,12 @@ "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" } + "categoryName68": { "message": "Accessibility" }, + "categoryName69": { "message": "Social logins" }, + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/zh_CN/messages.json b/src/drivers/webextension/_locales/zh_CN/messages.json index 8a37b95ca..280d0bdf3 100644 --- a/src/drivers/webextension/_locales/zh_CN/messages.json +++ b/src/drivers/webextension/_locales/zh_CN/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "这里无事可做。" }, "noAppsDetected": { "message": "未检测到任何技术。" }, "categoryPin": { "message": "总是显示图标" }, - "termsAccept": { "message": "接受" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "此扩展程序会匿名发送您访问的网站信息至 wappalyzer.com,包含域名和检测到的技术。这可以在设置中禁用。" }, "privacyPolicy": { "message": "隐私政策" }, "createAlert": { "message": "为该网站创建提醒" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "内容管理系统(CMS)" }, "categoryName2": { "message": "信息板" }, "categoryName3": { "message": "数据库管理器" }, @@ -84,7 +86,10 @@ "categoryName65": { "message": "负载均衡" }, "categoryName66": { "message": "用户界面(UI)框架" }, "categoryName67": { "message": "Cookie 合规" }, - "categoryName68": { "message": "辅助功能"}, - "categoryName69": { "message": "社交登录"}, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName68": { "message": "辅助功能" }, + "categoryName69": { "message": "社交登录" }, + "categoryName70": { "message": "SSL/TLS certificate authority" }, + "categoryName71": { "message": "Affiliate program" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/_locales/zh_TW/messages.json b/src/drivers/webextension/_locales/zh_TW/messages.json index 575a241f0..3a1950994 100644 --- a/src/drivers/webextension/_locales/zh_TW/messages.json +++ b/src/drivers/webextension/_locales/zh_TW/messages.json @@ -15,10 +15,12 @@ "nothingToDo": { "message": "這裡什麼也沒有。" }, "noAppsDetected": { "message": "未識別到技術。" }, "categoryPin": { "message": "永遠顯示圖示" }, - "termsAccept": { "message": "接受" }, + "termsAccept": { "message": "I'm ok with that" }, + "termsDecline": { "message": "Disable" }, "termsContent": { "message": "這個擴充功能將你所造訪網站的網域名稱和識別到的技術等資訊,匿名傳送至 wappalyzer.com。你可以在選項中停用。" }, "privacyPolicy": { "message": "Privacy policy" }, "createAlert": { "message": "Create an alert for this website" }, + "leadLists": { "message": "Lead generation tools" }, "categoryName1": { "message": "內容管理系統(CMS)" }, "categoryName2": { "message": "留言板/討論區" }, "categoryName3": { "message": "資料庫管理" }, @@ -88,5 +90,10 @@ "categoryName67": { "message": "Cookie compliance" }, "categoryName68": { "message": "Accessibility" }, "categoryName69": { "message": "Social login" }, - "categoryName70": { "message": "SSL/TLS certificate authority" } + "categoryName70": { "message": "SSL/TLS certificate authorities" }, + "categoryName71": { "message": "Affiliate programs" }, + "categoryName72": { "message": "Appointment scheduling" }, + "categoryName73": { "message": "Surveys" }, + "categoryName74": { "message": "A/B testing" }, + "categoryName75": { "message": "Email" } } diff --git a/src/drivers/webextension/css/styles.css b/src/drivers/webextension/css/styles.css index 45bd52ab3..74bd77181 100644 --- a/src/drivers/webextension/css/styles.css +++ b/src/drivers/webextension/css/styles.css @@ -44,6 +44,7 @@ a:hover { border-bottom: 1px solid var(--color-secondary); display: flex; height: 4.5rem; + justify-content: space-between; } .header__logo { @@ -67,19 +68,26 @@ a:hover { width: 100%; } -.alerts { +.footer__cta { + text-align: right; + margin-right: 1.5rem; white-space: nowrap; } -.alerts--hidden { - visibility: hidden; +.footer__cta a { + border-bottom: 1px solid var(--color-secondary); + text-decoration: none; +} + +.footer__cta a:hover { + border-bottom: 1px solid var(--color-primary); } -.alerts__icon { +.footer__open-in-new { color: var(--color-primary); height: 1.1rem; - margin-right: .5rem; - vertical-align: text-bottom; + margin-left: .1rem; + vertical-align: middle; width: 1.1rem; } @@ -195,6 +203,21 @@ a:hover { .technology__link { color: var(--color-text); + display: block; + width: 100%; +} + +.technology__link .technology__name { + border-bottom: 1px solid var(--color-secondary); +} + +.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 +231,7 @@ a:hover { border-radius: 3px; font-size: .7rem; padding: .1rem .3rem; - margin-left: .4rem; + margin-left: .2rem; vertical-align: middle; } @@ -218,7 +241,7 @@ a:hover { flex-direction: column; justify-content: center; padding: 1.5rem 1.5rem 1rem 1.5rem; - height: 12rem; + height: 14rem; width: 36rem; } @@ -244,18 +267,25 @@ a:hover { width: 80%; } -.terms__accept { +.terms__buttons { +} + +.terms__button { background-color: #4608ad; border: none; border-radius: 3px; color: white; cursor: pointer; font-size: .9rem; - padding: .8rem 3rem; + padding: 0 2rem; + margin: 0 .2rem; + height: 3rem; } -.terms__accept:hover { - background-color: #4107a1; +.terms__button--decline { + background-color: white; + border: 1px solid #4608ad; + color: #4608ad; } .terms__privacy { @@ -305,6 +335,11 @@ a:hover { color: #fff } + .theme-mode .technology__link:hover .technology__name { + border-bottom: 1px solid var(--color-text-dark); + color: var(--color-text-dark); + } + .theme-mode .technology__confidence { } @@ -321,7 +356,15 @@ a:hover { color: var(--color-text-dark); } - .theme-mode .alerts__icon { - color:var(--color-text-dark); + .theme-mode .footer__icon { + color: #fff; + } + + .theme-mode .footer__open-in-new { + color: #fff; + } + + .theme-mode .technology__open-in-new { + color: #fff; } } diff --git a/src/drivers/webextension/html/options.html b/src/drivers/webextension/html/options.html index 3dfc531d4..b07b09cab 100644 --- a/src/drivers/webextension/html/options.html +++ b/src/drivers/webextension/html/options.html @@ -43,7 +43,7 @@   - + diff --git a/src/drivers/webextension/html/popup.html b/src/drivers/webextension/html/popup.html index 2d3d2b84e..69f449810 100644 --- a/src/drivers/webextension/html/popup.html +++ b/src/drivers/webextension/html/popup.html @@ -13,7 +13,7 @@
- + @@ -26,9 +26,12 @@
- +
+ + +
- +
@@ -51,25 +54,27 @@
+ - +   - -   - + +   + -   +   +