NPM module has been rewritten, resulting in API-breaking changes. Using Puppeteer instead of Zombie.js, optimised for processing multiple websites in parallel.

main
Elbert Alias 5 years ago
parent 569030ce93
commit ebfef67608

@ -1,5 +0,0 @@
#!/bin/bash
cd "$(dirname $0)/.."
yarn run dev

@ -7,9 +7,6 @@ echo "Creating links..."
ln -nf apps.json drivers/npm
ln -nf wappalyzer.js drivers/npm
ln -nf apps.json drivers/puppeteer
ln -nf wappalyzer.js drivers/puppeteer
ln -nf apps.json drivers/webextension
ln -nf wappalyzer.js drivers/webextension/js

@ -1,11 +0,0 @@
--- src/drivers/npm/node_modules/zombie/lib/document.js
+++ src/drivers/npm/node_modules/zombie/lib/document.js
@@ -247,7 +247,7 @@ function setupWindow(window, args) {
browser._windowInScope = window;
let result;
if (typeof code == 'buffer' || code instanceof Buffer) code = code.toString();
- if (typeof code === 'string' || code instanceof String) result = VM.runInContext(code, context, { filename });else if (code) result = code.call(window);
+ if (typeof code === 'string' || code instanceof String) result = VM.runInContext(code, context, { filename, timeout: 1000 });else if (code) result = code.call(window);
browser.emit('evaluated', code, result, filename);
return result;
} catch (error) {

4
run

@ -15,11 +15,7 @@ $cmd sh -c "\
cd src/drivers/webextension; \
yarn install; \
cd ../npm; \
yarn install; \
cd ../puppeteer; \
yarn install"
$cmd sh -c "cat patches/*.patch | patch -p0"
$cmd ./bin/run links
$cmd ./bin/run $@

@ -4,31 +4,25 @@ MAINTAINER Wappalyzer <info@wappalyzer.com>
ENV WAPPALYZER_ROOT /opt/wappalyzer
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
ENV CHROME_BIN /usr/bin/chromium-browser
ENV CHROMIUM_BIN /usr/bin/chromium-browser
RUN apk update && apk add --no-cache \
RUN apk update && apk add -u --no-cache \
nodejs \
nodejs-npm \
udev \
chromium \
ttf-freefont
ttf-freefont \
yarn
RUN mkdir -p "$WAPPALYZER_ROOT/browsers"
WORKDIR "$WAPPALYZER_ROOT"
ADD apps.json .
ADD browser.js .
ADD browsers/zombie.js ./browsers
ADD browsers/puppeteer.js ./browsers
ADD cli.js .
ADD driver.js .
ADD index.js .
ADD package.json .
ADD wappalyzer.js .
RUN npm i && npm i puppeteer
RUN /usr/bin/chromium-browser --version
RUN yarn install
ENTRYPOINT ["node", "cli.js"]

@ -1,28 +1,17 @@
# Wappalyzer
[Wappalyzer](https://www.wappalyzer.com/) is a
[cross-platform](https://www.wappalyzer.com/nodejs) utility that uncovers the
technologies used on websites. It detects
[content management systems](https://www.wappalyzer.com/technologies/cms), [ecommerce platforms](https://www.wappalyzer.com/technologies/ecommerce), [web servers](https://www.wappalyzer.com/technologies/web-servers), [JavaScript frameworks](https://www.wappalyzer.com/technologies/javascript-frameworks),
[analytics tools](https://www.wappalyzer.com/technologies/analytics) and
[many more](https://www.wappalyzer.com/technologies).
[Wappalyzer](https://www.wappalyzer.com/) indentifies technologies on websites.
## Installation
```shell
$ npm i -g wappalyzer # Globally
$ npm i wappalyzer --save # As a dependency
```
## CLI
To use Puppeteer (headless Chrome browser), you must install the NPM package manually:
## Installation
```shell
$ npm i puppeteer@^2.0.0
$ npm i -g wappalyzer
```
## Run from the command line
## Usage
```
wappalyzer <url> [options]
@ -31,8 +20,7 @@ wappalyzer <url> [options]
### Options
```
-b, --browser=... Specify which headless browser to use (zombie or puppeteer)
-c, --chunk-size=... Process links in chunks
-b, --batch-size=... Process links in batches
-d, --debug Output debug messages
-t, --delay=ms Wait for ms milliseconds between requests
-h, --help This text
@ -41,16 +29,21 @@ wappalyzer <url> [options]
-D, --max-depth=... Don't analyse pages more than num levels deep
-m, --max-urls=... Exit when num URLs have been analysed
-w, --max-wait=... Wait no more than ms milliseconds for page resources to load
-p, --password=... Password to be used for basic HTTP authentication (zombie only)
-P, --pretty Pretty-print JSON output
--proxy=... Proxy URL, e.g. 'http://user:pass@proxy:8080' (zombie only)
-r, --recursive Follow links on pages (crawler)
-a, --user-agent=... Set the user agent string
-u, --username=... Username to be used for basic HTTP authentication (zombie only)
```
## Run from a script
## Dependency
## Installation
```shell
$ npm i wappalyzer
```
## Usage
```javascript
const Wappalyzer = require('wappalyzer');
@ -58,7 +51,6 @@ const Wappalyzer = require('wappalyzer');
const url = 'https://www.wappalyzer.com';
const options = {
// browser: 'puppeteer',
debug: false,
delay: 500,
maxDepth: 3,
@ -70,27 +62,53 @@ const options = {
htmlMaxRows: 2000,
};
const wappalyzer = new Wappalyzer(url, options);
;(async function() {
const wappalyzer = await new Wappalyzer()
try {
await wappalyzer.init()
const site = await wappalyzer.open(url)
// Optionally capture and output errors
site.on('error', console.error)
const results = site.analyze()
console.log(JSON.stringify(results, null, 2))
} catch (error) {
console.error(error)
}
await wappalyzer.destroy()
})()
```
Multiple URLs can be processed in parallel:
```javascript
const Wappalyzer = require('wappalyzer');
const urls = ['https://www.wappalyzer.com', 'https://www.example.com']
// Optional: capture log output
// wappalyzer.on('log', params => {
// const { message, source, type } = params;
// });
;(async function() {
const wappalyzer = await new Wappalyzer()
// Optional: do something on page visit
// wappalyzer.on('visit', params => {
// const { browser, pageUrl } = params;
// });
try {
await wappalyzer.init()
wappalyzer.analyze()
.then((json) => {
process.stdout.write(`${JSON.stringify(json, null, 2)}\n`);
const results = await Promise.all(
urls.map(async (url) => ({
url,
results: await wappalyzer.open(url).analyze()
}))
)
process.exit(0);
})
.catch((error) => {
process.stderr.write(`${error}\n`);
console.log(JSON.stringify(results, null, 2))
} catch (error) {
console.error(error)
}
process.exit(1);
});
await wappalyzer.destroy()
})()
```

@ -1,245 +0,0 @@
const { AWS_LAMBDA_FUNCTION_NAME, CHROME_BIN } = process.env
let chromium
let puppeteer
if (AWS_LAMBDA_FUNCTION_NAME) {
// eslint-disable-next-line global-require, import/no-unresolved
chromium = require('chrome-aws-lambda')
;({ puppeteer } = chromium)
} else {
// eslint-disable-next-line global-require
puppeteer = require('puppeteer')
}
const Browser = require('../browser')
function getJs() {
const dereference = (obj, level = 0) => {
try {
// eslint-disable-next-line no-undef
if (level > 5 || (level && obj === window)) {
return '[Removed]'
}
if (Array.isArray(obj)) {
obj = obj.map((item) => dereference(item, level + 1))
}
if (
typeof obj === 'function' ||
(typeof obj === 'object' && obj !== null)
) {
const newObj = {}
Object.keys(obj).forEach((key) => {
newObj[key] = dereference(obj[key], level + 1)
})
return newObj
}
return obj
} catch (error) {
return undefined
}
}
// eslint-disable-next-line no-undef
return dereference(window)
}
class PuppeteerBrowser extends Browser {
constructor(options) {
options.maxWait = options.maxWait || 60
super(options)
}
async visit(url) {
let done = false
let browser
try {
await new Promise(async (resolve, reject) => {
try {
browser = await puppeteer.launch(
chromium
? {
args: [...chromium.args, '--ignore-certificate-errors'],
defaultViewport: chromium.defaultViewport,
executablePath: await chromium.executablePath,
headless: chromium.headless
}
: {
args: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--ignore-certificate-errors'
],
executablePath: CHROME_BIN
}
)
browser.on('disconnected', () => {
if (!done) {
reject(new Error('browser: disconnected'))
}
})
const page = await browser.newPage()
page.setDefaultTimeout(this.options.maxWait * 1.1)
await page.setRequestInterception(true)
page.on('error', (error) =>
reject(new Error(`page error: ${error.message || error}`))
)
let responseReceived = false
page.on('request', (request) => {
try {
if (
responseReceived &&
request.isNavigationRequest() &&
request.frame() === page.mainFrame() &&
request.url() !== url
) {
this.log(`abort navigation to ${request.url()}`)
request.abort('aborted')
} else if (!done) {
if (!['document', 'script'].includes(request.resourceType())) {
request.abort()
} else {
request.continue()
}
}
} catch (error) {
reject(new Error(`page error: ${error.message || error}`))
}
})
page.on('response', (response) => {
try {
if (!this.statusCode) {
this.statusCode = response.status()
this.headers = {}
const headers = response.headers()
Object.keys(headers).forEach((key) => {
this.headers[key] = Array.isArray(headers[key])
? headers[key]
: [headers[key]]
})
this.contentType = headers['content-type'] || null
}
if (response.status() < 300 || response.status() > 399) {
responseReceived = true
}
} catch (error) {
reject(new Error(`page error: ${error.message || error}`))
}
})
page.on('console', ({ _type, _text, _location }) => {
if (!/Failed to load resource: net::ERR_FAILED/.test(_text)) {
this.log(
`${_text} (${_location.url}: ${_location.lineNumber})`,
_type
)
}
})
if (this.options.userAgent) {
await page.setUserAgent(this.options.userAgent)
}
try {
await Promise.race([
page.goto(url, { waitUntil: 'domcontentloaded' }),
// eslint-disable-next-line no-shadow
new Promise((resolve, reject) =>
setTimeout(
() => reject(new Error('timeout')),
this.options.maxWait
)
)
])
} catch (error) {
throw new Error(error.message || error.toString())
}
// eslint-disable-next-line no-undef
const links = await page.evaluateHandle(() =>
Array.from(document.getElementsByTagName('a')).map(
({ hash, hostname, href, pathname, protocol, rel }) => ({
hash,
hostname,
href,
pathname,
protocol,
rel
})
)
)
this.links = await links.jsonValue()
// eslint-disable-next-line no-undef
const scripts = await page.evaluateHandle(() =>
Array.from(document.getElementsByTagName('script')).map(
({ src }) => src
)
)
this.scripts = (await scripts.jsonValue()).filter((script) => script)
this.js = await page.evaluate(getJs)
this.cookies = (await page.cookies()).map(
({ name, value, domain, path }) => ({
name,
value,
domain,
path
})
)
this.html = await page.content()
resolve()
} catch (error) {
reject(new Error(`visit error: ${error.message || error}`))
}
})
} catch (error) {
this.log(`visit error: ${error.message || error} (${url})`, 'error')
throw new Error(error.message || error.toString())
} finally {
done = true
if (browser) {
try {
await browser.close()
this.log('browser close ok')
} catch (error) {
this.log(`browser close error: ${error.message || error}`, 'error')
}
}
}
this.log(`visit ok (${url})`)
}
}
module.exports = PuppeteerBrowser

@ -1,125 +0,0 @@
const Zombie = require('zombie');
const Browser = require('../browser');
class ZombieBrowser extends Browser {
constructor(options) {
super(options);
this.browser = new Zombie({
proxy: options.proxy,
silent: true,
strictSSL: false,
userAgent: options.userAgent,
waitDuration: options.maxWait,
});
this.browser.on('authenticate', (auth) => {
auth.username = this.options.username;
auth.password = this.options.password;
});
}
visit(url) {
return new Promise((resolve, reject) => {
try {
this.browser.visit(url, () => {
const resource = this.browser.resources.length
? this.browser.resources.filter(_resource => _resource.response).shift() : null;
this.window = this.browser.window;
this.document = this.browser.document;
this.headers = this.getHeaders();
this.statusCode = resource ? resource.response.status : 0;
this.contentType = this.headers['content-type'] ? this.headers['content-type'].shift() : null;
this.html = this.getHtml();
this.js = this.getJs();
this.links = this.getLinks();
this.scripts = this.getScripts();
this.cookies = this.getCookies();
resolve();
});
} catch (error) {
reject(error.message);
}
});
}
getHeaders() {
const headers = {};
const resource = this.browser.resources.length
? this.browser.resources.filter(_resource => _resource.response).shift() : null;
if (resource) {
// eslint-disable-next-line no-underscore-dangle
resource.response.headers._headers.forEach((header) => {
if (!headers[header[0]]) {
headers[header[0]] = [];
}
headers[header[0]].push(header[1]);
});
}
return headers;
}
getHtml() {
let html = '';
if (this.browser.document && this.browser.document.documentElement) {
try {
html = this.browser.html();
} catch (error) {
this.log(error.message, 'error');
}
}
return html;
}
getScripts() {
let scripts = [];
if (this.browser.document && this.browser.document.scripts) {
scripts = Array.prototype.slice
.apply(this.browser.document.scripts)
.filter(script => script.src)
.map(script => script.src);
}
return scripts;
}
getJs() {
return this.browser.window;
}
getLinks() {
let links = [];
if (this.browser.document) {
links = Array.from(this.browser.document.getElementsByTagName('a'));
}
return links;
}
getCookies() {
const cookies = [];
if (this.browser.cookies) {
this.browser.cookies.forEach(cookie => cookies.push({
name: cookie.key,
value: cookie.value,
domain: cookie.domain,
path: cookie.path,
}));
}
return cookies;
}
}
module.exports = ZombieBrowser;

@ -1,47 +1,51 @@
#!/usr/bin/env node
const Wappalyzer = require('./driver');
const Wappalyzer = require('./driver')
const args = process.argv.slice(2);
const args = process.argv.slice(2)
const options = {};
const options = {}
let url;
let arg;
let url
let arg
const aliases = {
a: 'userAgent',
b: 'browser',
c: 'chunkSize',
b: 'batchSize',
d: 'debug',
t: 'delay',
h: 'help',
D: 'maxDepth',
m: 'maxUrls',
p: 'password',
P: 'pretty',
r: 'recursive',
u: 'username',
w: 'maxWait',
};
w: 'maxWait'
}
while (true) { // eslint-disable-line no-constant-condition
arg = args.shift();
while (true) {
// eslint-disable-line no-constant-condition
arg = args.shift()
if (!arg) {
break;
break
}
const matches = /^-?-([^=]+)(?:=(.+)?)?/.exec(arg);
const matches = /^-?-([^=]+)(?:=(.+)?)?/.exec(arg)
if (matches) {
const key = aliases[matches[1]] || matches[1].replace(/-\w/g, _matches => _matches[1].toUpperCase());
const key =
aliases[matches[1]] ||
matches[1].replace(/-\w/g, (_matches) => _matches[1].toUpperCase())
// eslint-disable-next-line no-nested-ternary
const value = matches[2] ? matches[2] : args[0] && !args[0].match(/^-/) ? args.shift() : true;
const value = matches[2]
? matches[2]
: args[0] && !args[0].startsWith('-')
? args.shift()
: true
options[key] = value;
options[key] = value
} else {
url = arg;
url = arg
}
}
@ -51,12 +55,11 @@ if (!url || options.help) {
Examples:
wappalyzer https://www.example.com
node cli.js https://www.example.com -b puppeteer -r -D 3 -m 50
node cli.js https://www.example.com -r -D 3 -m 50
docker wappalyzer/cli https://www.example.com --pretty
Options:
-b, --browser=... Specify which headless browser to use (zombie or puppeteer)
-c, --chunk-size=... Process links in chunks
-b, --batch-size=... Process links in batches
-d, --debug Output debug messages
-t, --delay=ms Wait for ms milliseconds between requests
-h, --help This text
@ -65,30 +68,40 @@ Options:
-D, --max-depth=... Don't analyse pages more than num levels deep
-m, --max-urls=... Exit when num URLs have been analysed
-w, --max-wait=... Wait no more than ms milliseconds for page resources to load
-p, --password=... Password to be used for basic HTTP authentication (zombie only)
-P, --pretty Pretty-print JSON output
--proxy=... Proxy URL, e.g. 'http://user:pass@proxy:8080' (zombie only)
-r, --recursive Follow links on pages (crawler)
-a, --user-agent=... Set the user agent string
-u, --username=... Username to be used for basic HTTP authentication (zombie only)
`);
`)
process.exit(1);
process.exit(1)
}
// eslint-disable-next-line import/no-dynamic-require
const Browser = require(`./browsers/${options.browser || 'zombie'}`);
;(async function() {
const wappalyzer = await new Wappalyzer(options)
const wappalyzer = new Wappalyzer(Browser, url, options);
try {
await wappalyzer.init()
wappalyzer.analyze()
.then((json) => {
process.stdout.write(`${JSON.stringify(json, null, options.pretty ? 2 : null)}\n`);
const site = await wappalyzer.open(url)
process.exit(0);
site.on('error', (error) => {
process.stderr.write(`page error: ${error}\n`)
})
.catch((error) => {
process.stderr.write(`${error}\n`);
process.exit(1);
});
const results = await site.analyze()
process.stdout.write(
`${JSON.stringify(results, null, options.pretty ? 2 : null)}\n`
)
await wappalyzer.destroy()
process.exit(0)
} catch (error) {
process.stderr.write(error.toString())
await wappalyzer.destroy()
process.exit(1)
}
})()

@ -1,81 +1,129 @@
const url = require('url');
const fs = require('fs');
const path = require('path');
const LanguageDetect = require('languagedetect');
const Wappalyzer = require('./wappalyzer');
const { URL } = require('url')
const fs = require('fs')
const LanguageDetect = require('languagedetect')
const Wappalyzer = require('./wappalyzer')
const { AWS_LAMBDA_FUNCTION_NAME, CHROMIUM_BIN } = process.env
let puppeteer
if (AWS_LAMBDA_FUNCTION_NAME) {
// eslint-disable-next-line global-require, import/no-unresolved
;({
chromium: { puppeteer }
} = require('chrome-aws-lambda'))
} else {
// eslint-disable-next-line global-require
puppeteer = require('puppeteer')
}
const languageDetect = new LanguageDetect();
const languageDetect = new LanguageDetect()
languageDetect.setLanguageType('iso2');
languageDetect.setLanguageType('iso2')
const json = JSON.parse(fs.readFileSync(path.resolve(`${__dirname}/apps.json`)));
const json = JSON.parse(fs.readFileSync('./apps.json'))
const extensions = /^([^.]+$|\.(asp|aspx|cgi|htm|html|jsp|php)$)/;
const extensions = /^([^.]+$|\.(asp|aspx|cgi|htm|html|jsp|php)$)/
const errorTypes = {
RESPONSE_NOT_OK: 'Response was not ok',
NO_RESPONSE: 'No response from server',
NO_HTML_DOCUMENT: 'No HTML document',
};
NO_HTML_DOCUMENT: 'No HTML document'
}
function sleep(ms) {
return ms ? new Promise(resolve => setTimeout(resolve, ms)) : Promise.resolve();
return new Promise((resolve) => setTimeout(resolve, ms))
}
function getJs() {
const dereference = (obj, level = 0) => {
try {
// eslint-disable-next-line no-undef
if (level > 5 || (level && obj === window)) {
return '[Removed]'
}
if (Array.isArray(obj)) {
obj = obj.map((item) => dereference(item, level + 1))
}
if (
typeof obj === 'function' ||
(typeof obj === 'object' && obj !== null)
) {
const newObj = {}
Object.keys(obj).forEach((key) => {
newObj[key] = dereference(obj[key], level + 1)
})
return newObj
}
return obj
} catch (error) {
return undefined
}
}
// eslint-disable-next-line no-undef
return dereference(window)
}
function processJs(window, patterns) {
const js = {};
const js = {}
Object.keys(patterns).forEach((appName) => {
js[appName] = {};
js[appName] = {}
Object.keys(patterns[appName]).forEach((chain) => {
js[appName][chain] = {};
js[appName][chain] = {}
patterns[appName][chain].forEach((pattern, index) => {
const properties = chain.split('.');
const properties = chain.split('.')
let value = properties
.reduce((parent, property) => (parent && parent[property]
? parent[property] : null), window);
let value = properties.reduce(
(parent, property) =>
parent && parent[property] ? parent[property] : null,
window
)
value = typeof value === 'string' || typeof value === 'number' ? value : !!value;
value =
typeof value === 'string' || typeof value === 'number'
? value
: !!value
if (value) {
js[appName][chain][index] = value;
js[appName][chain][index] = value
}
});
});
});
})
})
})
return js;
return js
}
function processHtml(html, maxCols, maxRows) {
if (maxCols || maxRows) {
const chunks = [];
const rows = html.length / maxCols;
let i;
const batchs = []
const rows = html.length / maxCols
for (i = 0; i < rows; i += 1) {
for (let i = 0; i < rows; i += 1) {
if (i < maxRows / 2 || i > rows - maxRows / 2) {
chunks.push(html.slice(i * maxCols, (i + 1) * maxCols));
batchs.push(html.slice(i * maxCols, (i + 1) * maxCols))
}
}
html = chunks.join('\n');
html = batchs.join('\n')
}
return html;
return html
}
class Driver {
constructor(Browser, pageUrl, options) {
this.options = Object.assign({}, {
password: '',
proxy: null,
username: '',
chunkSize: 5,
constructor(options = {}) {
this.options = {
batchSize: 5,
debug: false,
delay: 500,
htmlMaxCols: 2000,
@ -84,252 +132,410 @@ class Driver {
maxUrls: 10,
maxWait: 5000,
recursive: false,
}, options || {});
...options
}
this.options.debug = Boolean(+this.options.debug);
this.options.recursive = Boolean(+this.options.recursive);
this.options.delay = this.options.recursive ? parseInt(this.options.delay, 10) : 0;
this.options.maxDepth = parseInt(this.options.maxDepth, 10);
this.options.maxUrls = parseInt(this.options.maxUrls, 10);
this.options.maxWait = parseInt(this.options.maxWait, 10);
this.options.htmlMaxCols = parseInt(this.options.htmlMaxCols, 10);
this.options.htmlMaxRows = parseInt(this.options.htmlMaxRows, 10);
this.options.debug = Boolean(+this.options.debug)
this.options.recursive = Boolean(+this.options.recursive)
this.options.delay = this.options.recursive
? parseInt(this.options.delay, 10)
: 0
this.options.maxDepth = parseInt(this.options.maxDepth, 10)
this.options.maxUrls = parseInt(this.options.maxUrls, 10)
this.options.maxWait = parseInt(this.options.maxWait, 10)
this.options.htmlMaxCols = parseInt(this.options.htmlMaxCols, 10)
this.options.htmlMaxRows = parseInt(this.options.htmlMaxRows, 10)
this.destroyed = false
}
this.origPageUrl = url.parse(pageUrl);
this.analyzedPageUrls = {};
this.apps = [];
this.meta = {};
this.listeners = {};
async init() {
this.log('Launching browser...')
this.Browser = Browser;
try {
this.browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--ignore-certificate-errors'
],
executablePath: CHROMIUM_BIN
})
this.browser.on('disconnected', async () => {
this.log('Browser disconnected')
if (!this.destroyed) {
await this.init()
}
})
} catch (error) {
throw new Error(error.toString())
}
}
this.wappalyzer = new Wappalyzer();
async destroy() {
this.destroyed = true
this.wappalyzer.apps = json.apps;
this.wappalyzer.categories = json.categories;
if (this.browser) {
try {
await sleep(1)
this.wappalyzer.parseJsPatterns();
await this.browser.close()
this.wappalyzer.driver.log = (message, source, type) => this.log(message, source, type);
this.wappalyzer.driver
.displayApps = (detected, meta, context) => this.displayApps(detected, meta, context);
this.log('Done')
} catch (error) {
throw new Error(error.toString())
}
}
on(event, callback) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event].push(callback);
open(url) {
return new Site(url, this)
}
emit(event, params) {
if (this.listeners[event]) {
this.listeners[event].forEach(listener => listener(params));
log(message, source = 'driver', type = 'debug') {
if (this.options.debug) {
// eslint-disable-next-line no-console
console.log(`${type.toUpperCase()} | ${source} | ${message}`)
}
}
}
analyze() {
this.time = {
start: new Date().getTime(),
last: new Date().getTime(),
};
class Site {
constructor(url, driver) {
;({ options: this.options, browser: this.browser } = driver)
return this.crawl(this.origPageUrl);
}
this.driver = driver
log(message, source, type) {
if (this.options.debug) {
// eslint-disable-next-line no-console
console.log(`[wappalyzer ${type}]`, `[${source}]`, message);
try {
this.originalUrl = new URL(url)
} catch (error) {
throw new Error(error.message || error.toString())
}
this.emit('log', { message, source, type });
}
this.wappalyzer = new Wappalyzer()
this.wappalyzer.apps = json.apps
this.wappalyzer.categories = json.categories
displayApps(detected, meta) {
this.meta = meta;
this.wappalyzer.parseJsPatterns()
Object.keys(detected).forEach((appName) => {
const app = detected[appName];
this.wappalyzer.driver.log = (message, source, type) =>
this.log(message, source, type)
this.wappalyzer.driver.displayApps = (detected, meta, context) =>
this.displayApps(detected, meta, context)
const categories = [];
this.analyzedUrls = {}
this.technologies = []
this.meta = {}
this.listeners = {}
this.headers = {}
}
app.props.cats.forEach((id) => {
const category = {};
async init() {}
category[id] = json.categories[id].name;
on(event, callback) {
if (!this.listeners[event]) {
this.listeners[event] = []
}
categories.push(category);
});
this.listeners[event].push(callback)
}
if (!this.apps.some(detectedApp => detectedApp.name === app.name)) {
this.apps.push({
name: app.name,
confidence: app.confidenceTotal.toString(),
version: app.version || null,
icon: app.props.icon || 'default.svg',
website: app.props.website,
cpe: app.props.cpe || null,
categories,
});
emit(event, params) {
if (this.listeners[event]) {
this.listeners[event].forEach((listener) => listener(params))
}
});
}
async fetch(pageUrl, index, depth) {
log(...args) {
this.emit('log', ...args)
this.driver.log(...args)
}
async fetch(url, index, depth) {}
async goto(url) {
// Return when the URL is a duplicate or maxUrls has been reached
if (
this.analyzedPageUrls[pageUrl.href]
|| this.analyzedPageUrls.length >= this.options.maxUrls
this.analyzedUrls[url.href] ||
Object.keys(this.analyzedUrls).length >= this.options.maxUrls
) {
return [];
return
}
this.analyzedPageUrls[pageUrl.href] = {
status: 0,
};
this.log(`Navigate to ${url}`, 'page')
this.analyzedUrls[url.href] = {
status: 0
}
if (!this.browser) {
throw new Error('Browser closed')
}
const timerScope = {
last: new Date().getTime(),
};
const page = await this.browser.newPage()
this.timer(`fetch; url: ${pageUrl.href}; depth: ${depth}; delay: ${this.options.delay * index}ms`, timerScope);
page.setDefaultTimeout(this.options.maxWait)
await sleep(this.options.delay * index);
await page.setRequestInterception(true)
page.on('error', (error) => this.emit('error', error))
let responseReceived = false
page.on('request', (request) => {
try {
return this.visit(pageUrl, timerScope);
if (
(responseReceived && request.isNavigationRequest()) ||
request.frame() !== page.mainFrame() ||
!['document', 'script'].includes(request.resourceType())
) {
request.abort('blockedbyclient')
} else {
request.continue()
}
} catch (error) {
throw new Error(error.message);
this.emit('error', error)
}
})
page.on('response', (response) => {
try {
if (response.url() === url.href) {
this.analyzedUrls[url.href] = {
status: response.status()
}
async visit(pageUrl, timerScope) {
const browser = new this.Browser(this.options);
const headers = response.headers()
browser.log = (message, type) => this.wappalyzer.log(message, 'browser', type);
Object.keys(headers).forEach((key) => {
this.headers[key] = [
...(this.headers[key] || []),
...(Array.isArray(headers[key]) ? headers[key] : [headers[key]])
]
})
this.timer(`visit start; url: ${pageUrl.href}`, timerScope);
this.contentType = headers['content-type'] || null
try {
await browser.visit(pageUrl.href);
if (response.status() >= 300 && response.status() < 400) {
if (this.headers.location) {
url = new URL(this.headers.location.slice(-1))
}
} else {
responseReceived = true
}
}
} catch (error) {
this.wappalyzer.log(error.message, 'browser', 'error');
this.emit('error', error)
}
})
throw new Error('RESPONSE_NOT_OK');
if (this.options.userAgent) {
await page.setUserAgent(this.options.userAgent)
}
this.timer(`visit end; url: ${pageUrl.href}`, timerScope);
try {
await Promise.race([
page.goto(url.href, { waitUntil: 'domcontentloaded' }),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error('Timeout')), this.options.maxWait)
)
])
} catch (error) {
this.emit('error', error)
}
this.analyzedPageUrls[pageUrl.href].status = browser.statusCode;
await sleep(1000)
const links = await (
await page.evaluateHandle(() =>
Array.from(document.getElementsByTagName('a')).map(
({ hash, hostname, href, pathname, protocol, rel }) => ({
hash,
hostname,
href,
pathname,
protocol,
rel
})
)
)
).jsonValue()
// eslint-disable-next-line no-undef
const scripts = (
await (
await page.evaluateHandle(() =>
Array.from(document.getElementsByTagName('script')).map(
({ src }) => src
)
)
).jsonValue()
).filter((script) => script)
const js = processJs(await page.evaluate(getJs), this.wappalyzer.jsPatterns)
const cookies = (await page.cookies()).map(
({ name, value, domain, path }) => ({
name,
value,
domain,
path
})
)
const html = processHtml(
await page.content(),
this.options.htmlMaxCols,
this.options.htmlMaxRows
)
// Validate response
if (!browser.statusCode) {
throw new Error('NO_RESPONSE');
if (!this.analyzedUrls[url.href].status) {
throw new Error('NO_RESPONSE')
}
const { cookies, headers, scripts } = browser;
const html = processHtml(browser.html, this.options.htmlMaxCols, this.options.htmlMaxRows);
const js = processJs(browser.js, this.wappalyzer.jsPatterns);
let language = null;
let language = null
try {
[[language]] = languageDetect.detect(html.replace(/<\/?[^>]+(>|$)/g, ' '), 1);
const [attrs] = languageDetect.detect(
html.replace(/<\/?[^>]+(>|$)/g, ' '),
1
)
if (attrs) {
;[language] = attrs
}
} catch (error) {
this.wappalyzer.log(`${error.message || error}; url: ${pageUrl.href}`, 'driver', 'error');
this.log(`${error} (${url.href})`, 'driver', 'error')
}
await this.wappalyzer.analyze(pageUrl, {
await this.wappalyzer.analyze(url, {
cookies,
headers,
headers: this.headers,
html,
js,
scripts,
language,
});
language
})
const reducedLinks = Array.prototype.reduce.call(
browser.links, (results, link) => {
links,
(results, link) => {
if (
results
&& Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(results), 'push')
&& link.protocol
&& link.protocol.match(/https?:/)
&& link.rel !== 'nofollow'
&& link.hostname === this.origPageUrl.hostname
&& extensions.test(link.pathname)
results &&
Object.prototype.hasOwnProperty.call(
Object.getPrototypeOf(results),
'push'
) &&
link.protocol &&
link.protocol.match(/https?:/) &&
link.rel !== 'nofollow' &&
link.hostname === url.hostname &&
extensions.test(link.pathname)
) {
link.hash = '';
results.push(url.parse(link.href));
results.push(new URL(link.href.split('#')[0]))
}
return results;
}, [],
);
return results
},
[]
)
this.emit('visit', { browser, pageUrl });
this.emit('goto', url)
return reducedLinks;
return reducedLinks
}
async crawl(pageUrl, index = 1, depth = 1) {
pageUrl.canonical = `${pageUrl.protocol}//${pageUrl.host}${pageUrl.pathname}`;
async analyze(url = this.originalUrl, index = 1, depth = 1) {
try {
await sleep(this.options.delay * index)
let links;
const links = await this.goto(url)
try {
links = await this.fetch(pageUrl, index, depth);
if (links && this.options.recursive && depth < this.options.maxDepth) {
await this.batch(links.slice(0, this.options.maxUrls), depth + 1)
}
} catch (error) {
const type = error.message && errorTypes[error.message] ? error.message : 'UNKNOWN_ERROR';
const message = error.message && errorTypes[error.message] ? errorTypes[error.message] : 'Unknown error';
this.analyzedPageUrls[pageUrl.href].error = {
const type =
error.message && errorTypes[error.message]
? error.message
: 'UNKNOWN_ERROR'
const message =
error.message && errorTypes[error.message]
? errorTypes[error.message]
: 'Unknown error'
this.analyzedUrls[url.href] = {
status: 0,
error: {
type,
message,
};
this.wappalyzer.log(`${message}; url: ${pageUrl.href}`, 'driver', 'error');
message
}
}
if (links && this.options.recursive && depth < this.options.maxDepth) {
await this.chunk(links.slice(0, this.options.maxUrls), depth + 1);
this.log(`${message} (${url.href})`, 'driver', 'error')
}
return {
urls: this.analyzedPageUrls,
applications: this.apps,
meta: this.meta,
};
urls: this.analyzedUrls,
applications: this.technologies,
meta: this.meta
}
}
async chunk(links, depth, chunk = 0) {
async batch(links, depth, batch = 0) {
if (links.length === 0) {
return;
return
}
const chunked = links.splice(0, this.options.chunkSize);
const batched = links.splice(0, this.options.batchSize)
await Promise.all(chunked.map((link, index) => this.crawl(link, index, depth)));
await Promise.all(
batched.map((link, index) => this.analyze(link, index, depth))
)
await this.chunk(links, depth, chunk + 1);
await this.batch(links, depth, batch + 1)
}
timer(message, scope) {
const time = new Date().getTime();
const sinceStart = `${Math.round((time - this.time.start) / 10) / 100}s`;
const sinceLast = `${Math.round((time - scope.last) / 10) / 100}s`;
this.wappalyzer.log(`[timer] ${message}; lapsed: ${sinceLast} / ${sinceStart}`, 'driver');
scope.last = time;
displayApps(technologies, meta) {
this.meta = meta
Object.keys(technologies).forEach((name) => {
const {
confidenceTotal: confidence,
version,
props: { cats, icon, website, cpe }
} = technologies[name]
const categories = cats.reduce((categories, id) => {
categories[id] = json.categories[id].name
return categories
}, {})
if (!this.technologies.some(({ name: _name }) => name === _name)) {
this.technologies.push({
name,
confidence,
version: version || null,
icon: icon || 'default.svg',
website,
cpe: cpe || null,
categories
})
}
})
}
}
module.exports = Driver;
module.exports = Driver
module.exports.processJs = processJs;
module.exports.processHtml = processHtml;
module.exports.processJs = processJs
module.exports.processHtml = processHtml

@ -1,12 +0,0 @@
const Driver = require('./driver');
class Wappalyzer {
constructor(pageUrl, options) {
// eslint-disable-next-line import/no-dynamic-require, global-require
const Browser = require(`./browsers/${options.browser || 'zombie'}`);
return new Driver(Browser, pageUrl, options);
}
}
module.exports = Wappalyzer;

@ -2,7 +2,7 @@
"name": "wappalyzer",
"description": "Identify technology on websites",
"homepage": "https://www.wappalyzer.com",
"version": "5.10.1",
"version": "6.0.0",
"author": "Wappalyzer",
"license": "MIT",
"repository": {
@ -12,12 +12,9 @@
"funding": {
"url": "https://github.com/sponsors/aliasio"
},
"main": "index.js",
"main": "driver.js",
"files": [
"apps.json",
"browser.js",
"browsers/zombie.js",
"browsers/puppeteer.js",
"cli.js",
"driver.js",
"index.js",
@ -28,9 +25,6 @@
},
"dependencies": {
"languagedetect": "^2.0.0",
"zombie": "^6.1.4"
},
"peerDependencies": {
"puppeteer": "^2.0.0"
}
}

@ -2,740 +2,295 @@
# yarn lockfile v1
abab@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a"
integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==
acorn-globals@^4.1.0:
version "4.3.4"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7"
integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==
dependencies:
acorn "^6.0.1"
acorn-walk "^6.0.1"
acorn-walk@^6.0.1:
version "6.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
acorn@^5.5.3:
version "5.7.4"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
acorn@^6.0.1:
version "6.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
ajv@^6.5.5:
version "6.12.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd"
integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"
array-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
asn1@~0.2.3:
version "0.2.4"
resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
dependencies:
safer-buffer "~2.1.0"
"@types/mime-types@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73"
integrity sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=
assert-plus@1.0.0, assert-plus@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
agent-base@5:
version "5.1.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
async-limiter@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
aws4@^1.8.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
babel-runtime@6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
tweetnacl "^0.14.3"
balanced-match "^1.0.0"
concat-map "0.0.1"
bluebird@^3.5.1:
version "3.7.2"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
browser-process-hrtime@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
core-js@^2.4.0:
version "2.6.11"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==
concat-stream@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
core-util-is@1.0.2:
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0":
version "0.3.8"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
cssstyle@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1"
integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==
dependencies:
cssom "0.3.x"
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
dependencies:
assert-plus "^1.0.0"
data-urls@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==
dependencies:
abab "^2.0.0"
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
debug@^4.1.0:
debug@4, debug@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
domexception@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
dependencies:
webidl-conversions "^4.0.2"
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
ms "2.0.0"
escodegen@^1.9.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457"
integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==
extract-zip@^1.6.6:
version "1.7.0"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
dependencies:
esprima "^4.0.1"
estraverse "^4.2.0"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.6.1"
esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
estraverse@^4.2.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
eventsource@^1.0.5:
version "1.0.7"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0"
integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==
dependencies:
original "^1.0.0"
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
concat-stream "^1.6.2"
debug "^2.6.9"
mkdirp "^0.5.4"
yauzl "^2.10.0"
extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
extsprintf@^1.2.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
fast-deep-equal@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
fast-json-stable-stringify@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
fast-levenshtein@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
dependencies:
assert-plus "^1.0.0"
pend "~1.2.0"
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
har-validator@~5.1.3:
version "5.1.3"
resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
glob@^7.1.3:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
https-proxy-agent@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b"
integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==
dependencies:
ajv "^6.5.5"
har-schema "^2.0.0"
agent-base "5"
debug "4"
html-encoding-sniffer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
dependencies:
whatwg-encoding "^1.0.1"
once "^1.3.0"
wrappy "1"
http-signature@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
dependencies:
assert-plus "^1.0.0"
jsprim "^1.2.2"
sshpk "^1.7.0"
iconv-lite@0.4.24, iconv-lite@^0.4.21:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
dependencies:
safer-buffer ">= 2.1.2 < 3"
inherits@2, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-typedarray@~1.0.0:
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jsdom@11.12.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8"
integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==
dependencies:
abab "^2.0.0"
acorn "^5.5.3"
acorn-globals "^4.1.0"
array-equal "^1.0.0"
cssom ">= 0.3.2 < 0.4.0"
cssstyle "^1.0.0"
data-urls "^1.0.0"
domexception "^1.0.1"
escodegen "^1.9.1"
html-encoding-sniffer "^1.0.2"
left-pad "^1.3.0"
nwsapi "^2.0.7"
parse5 "4.0.0"
pn "^1.1.0"
request "^2.87.0"
request-promise-native "^1.0.5"
sax "^1.2.4"
symbol-tree "^3.2.2"
tough-cookie "^2.3.4"
w3c-hr-time "^1.0.1"
webidl-conversions "^4.0.2"
whatwg-encoding "^1.0.3"
whatwg-mimetype "^2.1.0"
whatwg-url "^6.4.1"
ws "^5.2.0"
xml-name-validator "^3.0.0"
json-schema-traverse@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
json-schema@0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
dependencies:
assert-plus "1.0.0"
extsprintf "1.3.0"
json-schema "0.2.3"
verror "1.10.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
languagedetect@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/languagedetect/-/languagedetect-2.0.0.tgz#4b8fa2b7593b2a3a02fb1100891041c53238936c"
integrity sha512-AZb/liiQ+6ZoTj4f1J0aE6OkzhCo8fyH+tuSaPfSo8YHCWLFJrdSixhtO2TYdIkjcDQNaR4RmGaV2A5FJklDMQ==
left-pad@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==
levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
dependencies:
prelude-ls "~1.1.2"
type-check "~0.3.2"
lodash.sortby@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
lodash@^4.17.10, lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
mime-db@1.44.0:
version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
mime-types@^2.1.12, mime-types@~2.1.19:
mime-types@^2.1.25:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
dependencies:
mime-db "1.44.0"
mime@^2.3.1:
mime@^2.0.3:
version "2.4.6"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
nwsapi@^2.0.7:
version "2.2.0"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
oauth-sign@~0.9.0:
version "0.9.0"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
deep-is "~0.1.3"
fast-levenshtein "~2.0.6"
levn "~0.3.0"
prelude-ls "~1.1.2"
type-check "~0.3.2"
word-wrap "~1.2.3"
original@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f"
integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==
dependencies:
url-parse "^1.4.3"
parse5@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==
brace-expansion "^1.1.7"
performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
pn@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
psl@^1.1.28:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
punycode@^2.1.0, punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@~6.5.2:
version "6.5.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
querystringify@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
request-promise-core@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
integrity sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==
dependencies:
lodash "^4.17.15"
request-promise-native@^1.0.5:
version "1.0.8"
resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36"
integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==
dependencies:
request-promise-core "1.1.3"
stealthy-require "^1.1.1"
tough-cookie "^2.3.3"
request@^2.85.0, request@^2.87.0:
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
mkdirp@^0.5.4:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
aws-sign2 "~0.7.0"
aws4 "^1.8.0"
caseless "~0.12.0"
combined-stream "~1.0.6"
extend "~3.0.2"
forever-agent "~0.6.1"
form-data "~2.3.2"
har-validator "~5.1.3"
http-signature "~1.2.0"
is-typedarray "~1.0.0"
isstream "~0.1.2"
json-stringify-safe "~5.0.1"
mime-types "~2.1.19"
oauth-sign "~0.9.0"
performance-now "^2.1.0"
qs "~6.5.2"
safe-buffer "^5.1.2"
tough-cookie "~2.5.0"
tunnel-agent "^0.6.0"
uuid "^3.3.2"
requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
minimist "^1.2.5"
safe-buffer@^5.0.1, safe-buffer@^5.1.2:
version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sshpk@^1.7.0:
version "1.16.1"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
dependencies:
asn1 "~0.2.3"
assert-plus "^1.0.0"
bcrypt-pbkdf "^1.0.0"
dashdash "^1.12.0"
ecc-jsbn "~0.1.1"
getpass "^0.1.1"
jsbn "~0.1.0"
safer-buffer "^2.0.2"
tweetnacl "~0.14.0"
stealthy-require@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
symbol-tree@^3.2.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
psl "^1.1.28"
punycode "^2.1.1"
wrappy "1"
tr46@^1.0.1:
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
dependencies:
punycode "^2.1.0"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
dependencies:
safe-buffer "^5.0.1"
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
dependencies:
prelude-ls "~1.1.2"
progress@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
dependencies:
punycode "^2.1.0"
proxy-from-env@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
url-parse@^1.4.3:
version "1.4.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
uuid@^3.3.2:
version "3.4.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
puppeteer@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-2.1.1.tgz#ccde47c2a688f131883b50f2d697bd25189da27e"
integrity sha512-LWzaDVQkk1EPiuYeTOj+CZRIjda4k2s5w4MK4xoH2+kgWV/SDlkYHmxatDdtYrciHUKSXTsGgPgPP8ILVdBsxg==
dependencies:
assert-plus "^1.0.0"
core-util-is "1.0.2"
extsprintf "^1.2.0"
w3c-hr-time@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
"@types/mime-types" "^2.1.0"
debug "^4.1.0"
extract-zip "^1.6.6"
https-proxy-agent "^4.0.0"
mime "^2.0.3"
mime-types "^2.1.25"
progress "^2.0.1"
proxy-from-env "^1.0.0"
rimraf "^2.6.1"
ws "^6.1.0"
readable-stream@^2.2.2:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
rimraf@^2.6.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
browser-process-hrtime "^1.0.0"
safe-buffer "~5.1.0"
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
version "1.0.5"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
dependencies:
iconv-lite "0.4.24"
whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
whatwg-url@^6.4.1:
version "6.5.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==
dependencies:
lodash.sortby "^4.7.0"
tr46 "^1.0.1"
webidl-conversions "^4.0.2"
whatwg-url@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
dependencies:
lodash.sortby "^4.7.0"
tr46 "^1.0.1"
webidl-conversions "^4.0.2"
word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
ws@^5.2.0:
version "5.2.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
dependencies:
async-limiter "~1.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@^6.1.2:
ws@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
dependencies:
async-limiter "~1.0.0"
xml-name-validator@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
zombie@^6.1.4:
version "6.1.4"
resolved "https://registry.yarnpkg.com/zombie/-/zombie-6.1.4.tgz#9f0f53f3d9a032beb7f3fe5b382146a3475a4d47"
integrity sha512-yxNvKtyz3PP8lkr31AYh7vdbBD4is9hYXiOQKPp+k/7GiDiFQXX1Ex+peCl4ttodu/bHZcIluJ8lxMla5XefBQ==
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
dependencies:
babel-runtime "6.26.0"
bluebird "^3.5.1"
debug "^4.1.0"
eventsource "^1.0.5"
iconv-lite "^0.4.21"
jsdom "11.12.0"
lodash "^4.17.10"
mime "^2.3.1"
ms "^2.1.1"
request "^2.85.0"
tough-cookie "^2.3.4"
ws "^6.1.2"
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"

@ -0,0 +1,722 @@
/**
* Wappalyzer v5
*
* Created by Elbert Alias <elbert@alias.io>
*
* License: GPLv3 http://www.gnu.org/licenses/gpl-3.0.txt
*/
const validation = {
hostname: /(www.)?((.+?)\.(([a-z]{2,3}\.)?[a-z]{2,6}))$/,
hostnameBlacklist: /((local|dev(elopment)?|stag(e|ing)?|test(ing)?|demo(shop)?|admin|google|cache)\.|\/admin|\.local)/
}
/**
* Enclose string in array
*/
function asArray(value) {
return Array.isArray(value) ? value : [value]
}
/**
*
*/
function asyncForEach(iterable, iterator) {
return Promise.all(
(iterable || []).map(
(item) =>
new Promise((resolve) => setTimeout(() => resolve(iterator(item)), 1))
)
)
}
/**
* Mark application as detected, set confidence and version
*/
function addDetected(app, pattern, type, value, key) {
app.detected = true
// Set confidence level
app.confidence[`${type} ${key ? `${key} ` : ''}${pattern.regex}`] =
pattern.confidence === undefined ? 100 : parseInt(pattern.confidence, 10)
// Detect version number
if (pattern.version) {
const versions = []
const matches = pattern.regex.exec(value)
let { version } = pattern
if (matches) {
matches.forEach((match, i) => {
// Parse ternary operator
const ternary = new RegExp(`\\\\${i}\\?([^:]+):(.*)$`).exec(version)
if (ternary && ternary.length === 3) {
version = version.replace(ternary[0], match ? ternary[1] : ternary[2])
}
// Replace back references
version = version
.trim()
.replace(new RegExp(`\\\\${i}`, 'g'), match || '')
})
if (version && !versions.includes(version)) {
versions.push(version)
}
if (versions.length) {
// Use the longest detected version number
app.version = versions.reduce((a, b) => (a.length > b.length ? a : b))
}
}
}
}
function resolveExcludes(apps, detected) {
const excludes = []
const detectedApps = Object.assign({}, apps, detected)
// Exclude app in detected apps only
Object.keys(detectedApps).forEach((appName) => {
const app = detectedApps[appName]
if (app.props.excludes) {
asArray(app.props.excludes).forEach((excluded) => {
excludes.push(excluded)
})
}
})
// Remove excluded applications
Object.keys(apps).forEach((appName) => {
if (excludes.includes(appName)) {
delete apps[appName]
}
})
}
class Application {
constructor(name, props, detected) {
this.confidence = {}
this.confidenceTotal = 0
this.detected = Boolean(detected)
this.excludes = []
this.name = name
this.props = props
this.version = ''
}
/**
* Calculate confidence total
*/
getConfidence() {
let total = 0
Object.keys(this.confidence).forEach((id) => {
total += this.confidence[id]
})
this.confidenceTotal = Math.min(total, 100)
return this.confidenceTotal
}
}
class Wappalyzer {
constructor() {
this.apps = {}
this.categories = {}
this.driver = {}
this.jsPatterns = {}
this.detected = {}
this.hostnameCache = {}
this.adCache = []
this.config = {
websiteURL: 'https://www.wappalyzer.com/',
twitterURL: 'https://twitter.com/Wappalyzer',
githubURL: 'https://github.com/AliasIO/Wappalyzer'
}
}
/**
* Log messages to console
*/
log(message, source, type) {
if (this.driver.log) {
this.driver.log(message, source || '', type || 'debug')
}
}
analyze(url, data, context) {
const apps = {}
const promises = []
const startTime = new Date()
const { scripts, cookies, headers, js } = data
let { html } = data
if (this.detected[url.canonical] === undefined) {
this.detected[url.canonical] = {}
}
const metaTags = []
// Additional information
let language = null
if (html) {
if (typeof html !== 'string') {
html = ''
}
let matches = data.html.match(
new RegExp('<html[^>]*[: ]lang="([a-z]{2}((-|_)[A-Z]{2})?)"', 'i')
)
language = matches && matches.length ? matches[1] : data.language || null
// Meta tags
const regex = /<meta[^>]+>/gi
do {
matches = regex.exec(html)
if (!matches) {
break
}
metaTags.push(matches[0])
} while (matches)
}
Object.keys(this.apps).forEach((appName) => {
apps[appName] =
this.detected[url.canonical] && this.detected[url.canonical][appName]
? this.detected[url.canonical][appName]
: new Application(appName, this.apps[appName])
const app = apps[appName]
promises.push(this.analyzeUrl(app, url))
if (html) {
promises.push(this.analyzeHtml(app, html))
promises.push(this.analyzeMeta(app, metaTags))
}
if (scripts) {
promises.push(this.analyzeScripts(app, scripts))
}
if (cookies) {
promises.push(this.analyzeCookies(app, cookies))
}
if (headers) {
promises.push(this.analyzeHeaders(app, headers))
}
})
if (js) {
Object.keys(js).forEach((appName) => {
if (typeof js[appName] !== 'function') {
promises.push(this.analyzeJs(apps[appName], js[appName]))
}
})
}
return new Promise(async (resolve) => {
await Promise.all(promises)
Object.keys(apps).forEach((appName) => {
const app = apps[appName]
if (!app.detected || !app.getConfidence()) {
delete apps[app.name]
}
})
resolveExcludes(apps, this.detected[url])
this.resolveImplies(apps, url.canonical)
this.cacheDetectedApps(apps, url.canonical)
this.trackDetectedApps(apps, url, language)
this.log(
`Processing ${Object.keys(data).join(', ')} took ${(
(new Date() - startTime) /
1000
).toFixed(2)}s (${url.hostname})`,
'core'
)
if (Object.keys(apps).length) {
this.log(
`Identified ${Object.keys(apps).join(', ')} (${url.hostname})`,
'core'
)
}
this.driver.displayApps(
this.detected[url.canonical],
{ language },
context
)
return resolve()
})
}
/**
* Cache detected ads
*/
cacheDetectedAds(ad) {
this.adCache.push(ad)
}
/**
*
*/
robotsTxtAllows(url) {
return new Promise(async (resolve, reject) => {
const parsed = this.parseUrl(url)
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
return reject()
}
const robotsTxt = await this.driver.getRobotsTxt(
parsed.host,
parsed.protocol === 'https:'
)
if (
robotsTxt.some(
(disallowedPath) => parsed.pathname.indexOf(disallowedPath) === 0
)
) {
return reject()
}
return resolve()
})
}
/**
* Parse a URL
*/
parseUrl(url) {
const a = this.driver.document.createElement('a')
a.href = url
a.canonical = `${a.protocol}//${a.host}${a.pathname}`
return a
}
/**
*
*/
static parseRobotsTxt(robotsTxt) {
const disallow = []
let userAgent
robotsTxt.split('\n').forEach((line) => {
let matches = /^User-agent:\s*(.+)$/i.exec(line.trim())
if (matches) {
userAgent = matches[1].toLowerCase()
} else if (userAgent === '*' || userAgent === 'wappalyzer') {
matches = /^Disallow:\s*(.+)$/i.exec(line.trim())
if (matches) {
disallow.push(matches[1])
}
}
})
return disallow
}
/**
*
*/
ping() {
if (Object.keys(this.hostnameCache).length > 50) {
this.driver.ping(this.hostnameCache)
this.hostnameCache = {}
}
if (this.adCache.length > 50) {
this.driver.ping({}, this.adCache)
this.adCache = []
}
}
/**
* Parse apps.json patterns
*/
parsePatterns(patterns) {
if (!patterns) {
return []
}
let parsed = {}
// Convert string to object containing array containing string
if (typeof patterns === 'string' || Array.isArray(patterns)) {
patterns = {
main: asArray(patterns)
}
}
Object.keys(patterns).forEach((key) => {
parsed[key] = []
asArray(patterns[key]).forEach((pattern) => {
const attrs = {}
pattern.split('\\;').forEach((attr, i) => {
if (i) {
// Key value pairs
attr = attr.split(':')
if (attr.length > 1) {
attrs[attr.shift()] = attr.join(':')
}
} else {
attrs.string = attr
try {
attrs.regex = new RegExp(attr.replace('/', '/'), 'i') // Escape slashes in regular expression
} catch (error) {
attrs.regex = new RegExp()
this.log(`${error.message}: ${attr}`, 'error', 'core')
}
}
})
parsed[key].push(attrs)
})
})
// Convert back to array if the original pattern list was an array (or string)
if ('main' in parsed) {
parsed = parsed.main
}
return parsed
}
/**
* Parse JavaScript patterns
*/
parseJsPatterns() {
Object.keys(this.apps).forEach((appName) => {
if (this.apps[appName].js) {
this.jsPatterns[appName] = this.parsePatterns(this.apps[appName].js)
}
})
}
resolveImplies(apps, url) {
let checkImplies = true
const resolve = (appName) => {
const app = apps[appName]
if (app && app.props.implies) {
asArray(app.props.implies).forEach((implied) => {
;[implied] = this.parsePatterns(implied)
if (!this.apps[implied.string]) {
this.log(
`Implied application ${implied.string} does not exist`,
'core',
'warn'
)
return
}
if (!(implied.string in apps)) {
apps[implied.string] =
this.detected[url] && this.detected[url][implied.string]
? this.detected[url][implied.string]
: new Application(
implied.string,
this.apps[implied.string],
true
)
checkImplies = true
}
// Apply app confidence to implied app
Object.keys(app.confidence).forEach((id) => {
apps[implied.string].confidence[`${id} implied by ${appName}`] =
app.confidence[id] *
(implied.confidence === undefined ? 1 : implied.confidence / 100)
})
})
}
}
// Implied applications
// Run several passes as implied apps may imply other apps
while (checkImplies) {
checkImplies = false
Object.keys(apps).forEach(resolve)
}
}
/**
* Cache detected applications
*/
cacheDetectedApps(apps, url) {
Object.keys(apps).forEach((appName) => {
const app = apps[appName]
// Per URL
this.detected[url][appName] = app
Object.keys(app.confidence).forEach((id) => {
this.detected[url][appName].confidence[id] = app.confidence[id]
})
})
if (this.driver.ping instanceof Function) {
this.ping()
}
}
/**
* Track detected applications
*/
trackDetectedApps(apps, url, language) {
if (!(this.driver.ping instanceof Function)) {
return
}
const hostname = `${url.protocol}//${url.hostname}`
Object.keys(apps).forEach((appName) => {
const app = apps[appName]
if (this.detected[url.canonical][appName].getConfidence() >= 100) {
if (
validation.hostname.test(url.hostname) &&
!validation.hostnameBlacklist.test(url.hostname)
) {
if (!(hostname in this.hostnameCache)) {
this.hostnameCache[hostname] = {
applications: {},
meta: {}
}
}
if (!(appName in this.hostnameCache[hostname].applications)) {
this.hostnameCache[hostname].applications[appName] = {
hits: 0
}
}
this.hostnameCache[hostname].applications[appName].hits += 1
if (apps[appName].version) {
this.hostnameCache[hostname].applications[appName].version =
app.version
}
}
}
})
if (hostname in this.hostnameCache) {
this.hostnameCache[hostname].meta.language = language
}
this.ping()
}
/**
* Analyze URL
*/
analyzeUrl(app, url) {
const patterns = this.parsePatterns(app.props.url)
if (!patterns.length) {
return Promise.resolve()
}
return asyncForEach(patterns, (pattern) => {
if (pattern.regex.test(url.canonical)) {
addDetected(app, pattern, 'url', url.canonical)
}
})
}
/**
* Analyze HTML
*/
analyzeHtml(app, html) {
const patterns = this.parsePatterns(app.props.html)
if (!patterns.length) {
return Promise.resolve()
}
return asyncForEach(patterns, (pattern) => {
if (pattern.regex.test(html)) {
addDetected(app, pattern, 'html', html)
}
})
}
/**
* Analyze script tag
*/
analyzeScripts(app, scripts) {
const patterns = this.parsePatterns(app.props.script)
if (!patterns.length) {
return Promise.resolve()
}
return asyncForEach(patterns, (pattern) => {
scripts.forEach((uri) => {
if (pattern.regex.test(uri)) {
addDetected(app, pattern, 'script', uri)
}
})
})
}
/**
* Analyze meta tag
*/
analyzeMeta(app, metaTags) {
const patterns = this.parsePatterns(app.props.meta)
const promises = []
if (!app.props.meta) {
return Promise.resolve()
}
metaTags.forEach((match) => {
Object.keys(patterns).forEach((meta) => {
const r = new RegExp(`(?:name|property)=["']${meta}["']`, 'i')
if (r.test(match)) {
const content = match.match(/content=("|')([^"']+)("|')/i)
promises.push(
asyncForEach(patterns[meta], (pattern) => {
if (
content &&
content.length === 4 &&
pattern.regex.test(content[2])
) {
addDetected(app, pattern, 'meta', content[2], meta)
}
})
)
}
})
})
return Promise.all(promises)
}
/**
* Analyze response headers
*/
analyzeHeaders(app, headers) {
const patterns = this.parsePatterns(app.props.headers)
const promises = []
Object.keys(patterns).forEach((headerName) => {
if (typeof patterns[headerName] !== 'function') {
promises.push(
asyncForEach(patterns[headerName], (pattern) => {
headerName = headerName.toLowerCase()
if (headerName in headers) {
headers[headerName].forEach((headerValue) => {
if (pattern.regex.test(headerValue)) {
addDetected(app, pattern, 'headers', headerValue, headerName)
}
})
}
})
)
}
})
return promises ? Promise.all(promises) : Promise.resolve()
}
/**
* Analyze cookies
*/
analyzeCookies(app, cookies) {
const patterns = this.parsePatterns(app.props.cookies)
const promises = []
Object.keys(patterns).forEach((cookieName) => {
if (typeof patterns[cookieName] !== 'function') {
const cookieNameLower = cookieName.toLowerCase()
promises.push(
asyncForEach(patterns[cookieName], (pattern) => {
const cookie = cookies.find(
(_cookie) => _cookie.name.toLowerCase() === cookieNameLower
)
if (cookie && pattern.regex.test(cookie.value)) {
addDetected(app, pattern, 'cookies', cookie.value, cookieName)
}
})
)
}
})
return promises ? Promise.all(promises) : Promise.resolve()
}
/**
* Analyze JavaScript variables
*/
analyzeJs(app, results) {
const promises = []
Object.keys(results).forEach((string) => {
if (typeof results[string] !== 'function') {
promises.push(
asyncForEach(Object.keys(results[string]), (index) => {
const pattern = this.jsPatterns[app.name][string][index]
const value = results[string][index]
if (pattern && pattern.regex.test(value)) {
addDetected(app, pattern, 'js', value, string)
}
})
)
}
})
return promises ? Promise.all(promises) : Promise.resolve()
}
}
if (typeof module === 'object') {
module.exports = Wappalyzer
}

@ -1,3 +0,0 @@
/apps.json
/wappalyzer.js
/node_modules

@ -1,34 +0,0 @@
FROM node:12-alpine
MAINTAINER Wappalyzer <info@wappalyzer.com>
ENV WAPPALYZER_ROOT /opt/wappalyzer
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true
ENV CHROME_BIN /usr/bin/chromium-browser
RUN apk update && apk add --no-cache \
nodejs \
nodejs-npm \
udev \
chromium \
ttf-freefont
RUN mkdir -p "$WAPPALYZER_ROOT/browsers"
WORKDIR "$WAPPALYZER_ROOT"
ADD apps.json .
ADD browser.js .
ADD browsers/zombie.js ./browsers
ADD browsers/puppeteer.js ./browsers
ADD cli.js .
ADD driver.js .
ADD index.js .
ADD package.json .
ADD wappalyzer.js .
RUN npm i && npm i puppeteer
RUN /usr/bin/chromium-browser --version
ENTRYPOINT ["node", "cli.js"]

@ -1,94 +0,0 @@
# Wappalyzer
[Wappalyzer](https://www.wappalyzer.com/) is a
[cross-platform](https://www.wappalyzer.com/nodejs) utility that uncovers the
technologies used on websites. It detects
[content management systems](https://www.wappalyzer.com/technologies/cms), [ecommerce platforms](https://www.wappalyzer.com/technologies/ecommerce), [web servers](https://www.wappalyzer.com/technologies/web-servers), [JavaScript frameworks](https://www.wappalyzer.com/technologies/javascript-frameworks),
[analytics tools](https://www.wappalyzer.com/technologies/analytics) and
[many more](https://www.wappalyzer.com/technologies).
## Installation
```shell
$ npm i -g wappalyzer # Globally
$ npm i wappalyzer --save # As a dependency
```
To use Puppeteer (headless Chrome browser), you must install the NPM package manually:
```shell
$ npm i puppeteer@^2.0.0
```
## Run from the command line
```
wappalyzer <url> [options]
```
### Options
```
-b, --batch-size=... Process links in batches
-d, --debug Output debug messages
-t, --delay=ms Wait for ms milliseconds between requests
-h, --help This text
--html-max-cols=... Limit the number of HTML characters per line processed
--html-max-rows=... Limit the number of HTML lines processed
-D, --max-depth=... Don't analyse pages more than num levels deep
-m, --max-urls=... Exit when num URLs have been analysed
-w, --max-wait=... Wait no more than ms milliseconds for page resources to load
-P, --pretty Pretty-print JSON output
-r, --recursive Follow links on pages (crawler)
-a, --user-agent=... Set the user agent string
```
## Run from a script
```javascript
const Wappalyzer = require('wappalyzer');
const url = 'https://www.wappalyzer.com';
const options = {
debug: false,
delay: 500,
maxDepth: 3,
maxUrls: 10,
maxWait: 5000,
recursive: true,
userAgent: 'Wappalyzer',
htmlMaxCols: 2000,
htmlMaxRows: 2000,
};
;(async function() {
const wappalyzer = await new Wappalyzer(options)
try {
await wappalyzer.init()
const site = await wappalyzer.open(url)
site.on('error', (error) => {
process.stderr.write(`error: ${error}\n`)
})
const results = await site.analyze()
process.stdout.write(`${JSON.stringify(results, null, 2)}\n`)
await wappalyzer.destroy()
process.exit(0)
} catch (error) {
process.stderr.write(error.toString())
await wappalyzer.destroy()
process.exit(1)
}
})()

@ -1,20 +0,0 @@
class Browser {
constructor(options) {
this.options = options;
this.window = null;
this.document = null;
this.statusCode = null;
this.contentType = null;
this.headers = null;
this.statusCode = null;
this.contentType = null;
this.html = null;
this.js = null;
this.links = null;
this.scripts = null;
this.cookies = null;
}
}
module.exports = Browser;

@ -1,107 +0,0 @@
#!/usr/bin/env node
const Wappalyzer = require('./driver')
const args = process.argv.slice(2)
const options = {}
let url
let arg
const aliases = {
a: 'userAgent',
b: 'batchSize',
d: 'debug',
t: 'delay',
h: 'help',
D: 'maxDepth',
m: 'maxUrls',
P: 'pretty',
r: 'recursive',
w: 'maxWait'
}
while (true) {
// eslint-disable-line no-constant-condition
arg = args.shift()
if (!arg) {
break
}
const matches = /^-?-([^=]+)(?:=(.+)?)?/.exec(arg)
if (matches) {
const key =
aliases[matches[1]] ||
matches[1].replace(/-\w/g, (_matches) => _matches[1].toUpperCase())
// eslint-disable-next-line no-nested-ternary
const value = matches[2]
? matches[2]
: args[0] && !args[0].startsWith('-')
? args.shift()
: true
options[key] = value
} else {
url = arg
}
}
if (!url || options.help) {
process.stdout.write(`Usage:
wappalyzer <url> [options]
Examples:
wappalyzer https://www.example.com
node cli.js https://www.example.com -r -D 3 -m 50
docker wappalyzer/cli https://www.example.com --pretty
Options:
-b, --batch-size=... Process links in batches
-d, --debug Output debug messages
-t, --delay=ms Wait for ms milliseconds between requests
-h, --help This text
--html-max-cols=... Limit the number of HTML characters per line processed
--html-max-rows=... Limit the number of HTML lines processed
-D, --max-depth=... Don't analyse pages more than num levels deep
-m, --max-urls=... Exit when num URLs have been analysed
-w, --max-wait=... Wait no more than ms milliseconds for page resources to load
-P, --pretty Pretty-print JSON output
-r, --recursive Follow links on pages (crawler)
-a, --user-agent=... Set the user agent string
`)
process.exit(1)
}
;(async function() {
const wappalyzer = await new Wappalyzer(options)
try {
await wappalyzer.init()
const site = await wappalyzer.open(url)
site.on('error', (error) => {
process.stderr.write(`page error: ${error}\n`)
})
const results = await site.analyze()
process.stdout.write(
`${JSON.stringify(results, null, options.pretty ? 2 : null)}\n`
)
await wappalyzer.destroy()
process.exit(0)
} catch (error) {
process.stderr.write(error.toString())
await wappalyzer.destroy()
process.exit(1)
}
})()

@ -1,540 +0,0 @@
const { URL } = require('url')
const fs = require('fs')
const LanguageDetect = require('languagedetect')
const Wappalyzer = require('./wappalyzer')
const { AWS_LAMBDA_FUNCTION_NAME } = process.env
let puppeteer
if (AWS_LAMBDA_FUNCTION_NAME) {
// eslint-disable-next-line global-require, import/no-unresolved
;({
chromium: { puppeteer }
} = require('chrome-aws-lambda'))
} else {
// eslint-disable-next-line global-require
puppeteer = require('puppeteer')
}
const languageDetect = new LanguageDetect()
languageDetect.setLanguageType('iso2')
const json = JSON.parse(fs.readFileSync('./apps.json'))
const extensions = /^([^.]+$|\.(asp|aspx|cgi|htm|html|jsp|php)$)/
const errorTypes = {
RESPONSE_NOT_OK: 'Response was not ok',
NO_RESPONSE: 'No response from server',
NO_HTML_DOCUMENT: 'No HTML document'
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
function getJs() {
const dereference = (obj, level = 0) => {
try {
// eslint-disable-next-line no-undef
if (level > 5 || (level && obj === window)) {
return '[Removed]'
}
if (Array.isArray(obj)) {
obj = obj.map((item) => dereference(item, level + 1))
}
if (
typeof obj === 'function' ||
(typeof obj === 'object' && obj !== null)
) {
const newObj = {}
Object.keys(obj).forEach((key) => {
newObj[key] = dereference(obj[key], level + 1)
})
return newObj
}
return obj
} catch (error) {
return undefined
}
}
// eslint-disable-next-line no-undef
return dereference(window)
}
function processJs(window, patterns) {
const js = {}
Object.keys(patterns).forEach((appName) => {
js[appName] = {}
Object.keys(patterns[appName]).forEach((chain) => {
js[appName][chain] = {}
patterns[appName][chain].forEach((pattern, index) => {
const properties = chain.split('.')
let value = properties.reduce(
(parent, property) =>
parent && parent[property] ? parent[property] : null,
window
)
value =
typeof value === 'string' || typeof value === 'number'
? value
: !!value
if (value) {
js[appName][chain][index] = value
}
})
})
})
return js
}
function processHtml(html, maxCols, maxRows) {
if (maxCols || maxRows) {
const batchs = []
const rows = html.length / maxCols
for (let i = 0; i < rows; i += 1) {
if (i < maxRows / 2 || i > rows - maxRows / 2) {
batchs.push(html.slice(i * maxCols, (i + 1) * maxCols))
}
}
html = batchs.join('\n')
}
return html
}
class Driver {
constructor(options = {}) {
this.options = {
batchSize: 5,
debug: false,
delay: 500,
htmlMaxCols: 2000,
htmlMaxRows: 3000,
maxDepth: 3,
maxUrls: 10,
maxWait: 5000,
recursive: false,
...options
}
this.options.debug = Boolean(+this.options.debug)
this.options.recursive = Boolean(+this.options.recursive)
this.options.delay = this.options.recursive
? parseInt(this.options.delay, 10)
: 0
this.options.maxDepth = parseInt(this.options.maxDepth, 10)
this.options.maxUrls = parseInt(this.options.maxUrls, 10)
this.options.maxWait = parseInt(this.options.maxWait, 10)
this.options.htmlMaxCols = parseInt(this.options.htmlMaxCols, 10)
this.options.htmlMaxRows = parseInt(this.options.htmlMaxRows, 10)
this.destroyed = false
}
async init() {
this.log('Launching browser...')
try {
this.browser = await puppeteer.launch({
args: [
'--no-sandbox',
'--headless',
'--disable-gpu',
'--ignore-certificate-errors'
]
})
this.browser.on('disconnected', async () => {
this.log('Browser disconnected')
if (!this.destroyed) {
await this.init()
}
})
} catch (error) {
throw new Error(error.toString())
}
}
async destroy() {
this.destroyed = true
if (this.browser) {
try {
await sleep(1)
await this.browser.close()
this.log('Done')
} catch (error) {
throw new Error(error.toString())
}
}
}
open(url) {
return new Site(url, this)
}
log(message, source = 'driver', type = 'debug') {
if (this.options.debug) {
// eslint-disable-next-line no-console
console.log(`${type.toUpperCase()} | ${source} | ${message}`)
}
}
}
class Site {
constructor(url, driver) {
;({ options: this.options, browser: this.browser } = driver)
this.driver = driver
try {
this.originalUrl = new URL(url)
} catch (error) {
throw new Error(error.message || error.toString())
}
this.wappalyzer = new Wappalyzer()
this.wappalyzer.apps = json.apps
this.wappalyzer.categories = json.categories
this.wappalyzer.parseJsPatterns()
this.wappalyzer.driver.log = (message, source, type) =>
this.log(message, source, type)
this.wappalyzer.driver.displayApps = (detected, meta, context) =>
this.displayApps(detected, meta, context)
this.analyzedUrls = {}
this.technologies = []
this.meta = {}
this.listeners = {}
this.headers = {}
}
async init() {}
on(event, callback) {
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event].push(callback)
}
emit(event, params) {
if (this.listeners[event]) {
this.listeners[event].forEach((listener) => listener(params))
}
}
log(...args) {
this.emit('log', ...args)
this.driver.log(...args)
}
async fetch(url, index, depth) {}
async goto(url) {
// Return when the URL is a duplicate or maxUrls has been reached
if (
this.analyzedUrls[url.href] ||
Object.keys(this.analyzedUrls).length >= this.options.maxUrls
) {
return
}
this.log(`Navigate to ${url}`, 'page')
this.analyzedUrls[url.href] = {
status: 0
}
if (!this.browser) {
throw new Error('Browser closed')
}
const page = await this.browser.newPage()
page.setDefaultTimeout(this.options.maxWait)
await page.setRequestInterception(true)
page.on('error', (error) => this.emit('error', error))
let responseReceived = false
page.on('request', (request) => {
try {
if (
(responseReceived && request.isNavigationRequest()) ||
request.frame() !== page.mainFrame() ||
!['document', 'script'].includes(request.resourceType())
) {
request.abort('blockedbyclient')
} else {
request.continue()
}
} catch (error) {
this.emit('error', error)
}
})
page.on('response', (response) => {
try {
if (response.url() === url.href) {
this.analyzedUrls[url.href] = {
status: response.status()
}
const headers = response.headers()
Object.keys(headers).forEach((key) => {
this.headers[key] = [
...(this.headers[key] || []),
...(Array.isArray(headers[key]) ? headers[key] : [headers[key]])
]
})
this.contentType = headers['content-type'] || null
if (response.status() >= 300 && response.status() < 400) {
if (this.headers.location) {
url = new URL(this.headers.location.slice(-1))
}
} else {
responseReceived = true
}
}
} catch (error) {
this.emit('error', error)
}
})
if (this.options.userAgent) {
await page.setUserAgent(this.options.userAgent)
}
try {
await Promise.race([
page.goto(url.href, { waitUntil: 'domcontentloaded' }),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error('Timeout')), this.options.maxWait)
)
])
} catch (error) {
this.emit('error', error)
}
await sleep(1000)
const links = await (
await page.evaluateHandle(() =>
Array.from(document.getElementsByTagName('a')).map(
({ hash, hostname, href, pathname, protocol, rel }) => ({
hash,
hostname,
href,
pathname,
protocol,
rel
})
)
)
).jsonValue()
// eslint-disable-next-line no-undef
const scripts = (
await (
await page.evaluateHandle(() =>
Array.from(document.getElementsByTagName('script')).map(
({ src }) => src
)
)
).jsonValue()
).filter((script) => script)
const js = processJs(await page.evaluate(getJs), this.wappalyzer.jsPatterns)
const cookies = (await page.cookies()).map(
({ name, value, domain, path }) => ({
name,
value,
domain,
path
})
)
const html = processHtml(
await page.content(),
this.options.htmlMaxCols,
this.options.htmlMaxRows
)
// Validate response
if (!this.analyzedUrls[url.href].status) {
throw new Error('NO_RESPONSE')
}
let language = null
try {
const [attrs] = languageDetect.detect(
html.replace(/<\/?[^>]+(>|$)/g, ' '),
1
)
if (attrs) {
;[language] = attrs
}
} catch (error) {
this.log(`${error} (${url.href})`, 'driver', 'error')
}
await this.wappalyzer.analyze(url, {
cookies,
headers: this.headers,
html,
js,
scripts,
language
})
const reducedLinks = Array.prototype.reduce.call(
links,
(results, link) => {
if (
results &&
Object.prototype.hasOwnProperty.call(
Object.getPrototypeOf(results),
'push'
) &&
link.protocol &&
link.protocol.match(/https?:/) &&
link.rel !== 'nofollow' &&
link.hostname === url.hostname &&
extensions.test(link.pathname)
) {
results.push(new URL(link.href.split('#')[0]))
}
return results
},
[]
)
this.emit('goto', url)
return reducedLinks
}
async analyze(url = this.originalUrl, index = 1, depth = 1) {
try {
await sleep(this.options.delay * index)
const links = await this.goto(url)
if (links && this.options.recursive && depth < this.options.maxDepth) {
await this.batch(links.slice(0, this.options.maxUrls), depth + 1)
}
} catch (error) {
const type =
error.message && errorTypes[error.message]
? error.message
: 'UNKNOWN_ERROR'
const message =
error.message && errorTypes[error.message]
? errorTypes[error.message]
: 'Unknown error'
this.analyzedUrls[url.href] = {
status: 0,
error: {
type,
message
}
}
this.log(`${message} (${url.href})`, 'driver', 'error')
}
return {
urls: this.analyzedUrls,
applications: this.technologies,
meta: this.meta
}
}
async batch(links, depth, batch = 0) {
if (links.length === 0) {
return
}
const batched = links.splice(0, this.options.batchSize)
await Promise.all(
batched.map((link, index) => this.analyze(link, index, depth))
)
await this.batch(links, depth, batch + 1)
}
displayApps(technologies, meta) {
this.meta = meta
Object.keys(technologies).forEach((name) => {
const {
confidenceTotal: confidence,
version,
props: { cats, icon, website, cpe }
} = technologies[name]
const categories = cats.reduce((categories, id) => {
categories[id] = json.categories[id].name
return categories
}, {})
if (!this.technologies.some(({ name: _name }) => name === _name)) {
this.technologies.push({
name,
confidence,
version: version || null,
icon: icon || 'default.svg',
website,
cpe: cpe || null,
categories
})
}
})
}
}
module.exports = Driver
module.exports.processJs = processJs
module.exports.processHtml = processHtml

@ -1,12 +0,0 @@
const Driver = require('./driver');
class Wappalyzer {
constructor(pageUrl, options) {
// eslint-disable-next-line import/no-dynamic-require, global-require
const Browser = require(`./browsers/${options.browser || 'zombie'}`);
return new Driver(Browser, pageUrl, options);
}
}
module.exports = Wappalyzer;

@ -1,30 +0,0 @@
{
"name": "wappalyzer",
"description": "Identify technology on websites",
"homepage": "https://www.wappalyzer.com",
"version": "6.0.0",
"author": "Wappalyzer",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/aliasio/wappalyzer"
},
"funding": {
"url": "https://github.com/sponsors/aliasio"
},
"main": "index.js",
"files": [
"apps.json",
"cli.js",
"driver.js",
"index.js",
"wappalyzer.js"
],
"bin": {
"wappalyzer": "./cli.js"
},
"dependencies": {
"languagedetect": "^2.0.0",
"puppeteer": "^2.0.0"
}
}

@ -1,296 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/mime-types@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73"
integrity sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM=
agent-base@5:
version "5.1.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
async-limiter@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
balanced-match@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
buffer-crc32@~0.2.3:
version "0.2.13"
resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
concat-stream@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
debug@4, debug@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
dependencies:
ms "^2.1.1"
debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
dependencies:
ms "2.0.0"
extract-zip@^1.6.6:
version "1.7.0"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==
dependencies:
concat-stream "^1.6.2"
debug "^2.6.9"
mkdirp "^0.5.4"
yauzl "^2.10.0"
fd-slicer@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=
dependencies:
pend "~1.2.0"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
glob@^7.1.3:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
https-proxy-agent@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b"
integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==
dependencies:
agent-base "5"
debug "4"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
languagedetect@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/languagedetect/-/languagedetect-2.0.0.tgz#4b8fa2b7593b2a3a02fb1100891041c53238936c"
integrity sha512-AZb/liiQ+6ZoTj4f1J0aE6OkzhCo8fyH+tuSaPfSo8YHCWLFJrdSixhtO2TYdIkjcDQNaR4RmGaV2A5FJklDMQ==
mime-db@1.44.0:
version "1.44.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
mime-types@^2.1.25:
version "2.1.27"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
dependencies:
mime-db "1.44.0"
mime@^2.0.3:
version "2.4.5"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.5.tgz#d8de2ecb92982dedbb6541c9b6841d7f218ea009"
integrity sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==
minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
dependencies:
brace-expansion "^1.1.7"
minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
mkdirp@^0.5.4:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
dependencies:
minimist "^1.2.5"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
ms@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
pend@~1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
progress@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
proxy-from-env@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
puppeteer@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-2.1.1.tgz#ccde47c2a688f131883b50f2d697bd25189da27e"
integrity sha512-LWzaDVQkk1EPiuYeTOj+CZRIjda4k2s5w4MK4xoH2+kgWV/SDlkYHmxatDdtYrciHUKSXTsGgPgPP8ILVdBsxg==
dependencies:
"@types/mime-types" "^2.1.0"
debug "^4.1.0"
extract-zip "^1.6.6"
https-proxy-agent "^4.0.0"
mime "^2.0.3"
mime-types "^2.1.25"
progress "^2.0.1"
proxy-from-env "^1.0.0"
rimraf "^2.6.1"
ws "^6.1.0"
readable-stream@^2.2.2:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
dependencies:
core-util-is "~1.0.0"
inherits "~2.0.3"
isarray "~1.0.0"
process-nextick-args "~2.0.0"
safe-buffer "~5.1.1"
string_decoder "~1.1.1"
util-deprecate "~1.0.1"
rimraf@^2.6.1:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
string_decoder@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
dependencies:
safe-buffer "~5.1.0"
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@^6.1.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
dependencies:
async-limiter "~1.0.0"
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=
dependencies:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"

File diff suppressed because it is too large Load Diff