diff --git a/.babelrc b/.babelrc index 36f1fe1f..4a786039 100644 --- a/.babelrc +++ b/.babelrc @@ -14,7 +14,7 @@ "./view", "./tests", "./logs", - "./build", - "./bin.js" + "./dist", + "./build" ] } diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..0de0cb63 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 + target-branch: dev \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..c9685fb4 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,117 @@ +name: Main + +on: + push: + branches: [ "*" ] + pull_request: + branches: [ "*" ] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + + - name: Set up Javascript/Node + uses: actions/setup-node@v2 + with: + node-version: '12' + + - name: Check out code + uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Cache multiple paths + uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Build + run: | + npm install + npm run compile + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + if: ${{ always() }} + with: + name: logs + path: ~/.npm/_logs/* + retention-days: 14 + + test: + name: Test + runs-on: ubuntu-latest + steps: + + - name: Set up Javascript/Node + uses: actions/setup-node@v2 + with: + node-version: '12' + + - name: Check out code + uses: actions/checkout@v2 + with: + fetch-depth: '0' + + - name: Cache multiple paths + uses: actions/cache@v2 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install + run: | + npm install + + - name: Tests + run: | + npm run test-core + npm run test-generate + npm run test-reports + npm run test-neighbor + npm run test-dump + + - name: Tests RPKI + run: | + rm -f -R .cache/ + ./node_modules/.bin/mocha --exit tests/rpki_tests/tests.default.js --require @babel/register + ./node_modules/.bin/mocha --exit tests/rpki_tests/tests.external.js --require @babel/register + rm -f -R .cache/ && ./node_modules/.bin/mocha --exit tests/rpki_tests/tests.external-roas.js --require @babel/register + + - name: Tests Proxy + run: | + npm install -g anyproxy + nohup anyproxy --port 8001 & + ANYPROXY_PID=$! + npm run test-proxy + kill $ANYPROXY_PID + + - name: Tests NPM + run: | + npm run test-npm + + - name: Tests Kafka + run: | + sudo apt-get -y install tar + sudo apt-get -y install wget + wget https://mirror.lyrahosting.com/apache/kafka/2.6.0/kafka_2.13-2.6.0.tgz + tar -xzf kafka_2.13-2.6.0.tgz + nohup ./kafka_2.13-2.6.0/bin/zookeeper-server-start.sh ./kafka_2.13-2.6.0/config/zookeeper.properties & + nohup ./kafka_2.13-2.6.0/bin/kafka-server-start.sh ./kafka_2.13-2.6.0/config/server.properties & + nohup ./kafka_2.13-2.6.0/bin/kafka-topics.sh --create --topic bgpalerter --bootstrap-server 0.0.0.0:9092 & + sleep 20 && npm run test-kafka + + - name: Upload Artifact + uses: actions/upload-artifact@v2 + if: ${{ always() }} + with: + name: logs + path: ~/.npm/_logs/* + retention-days: 14 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1017c54d..dd91032f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,22 @@ config.yml prefixes.yml +groups.yml .idea/ node_modules/ bin/ +dist/ build/ logs/ .DS_Store bgpalerter.pid alertdata/ +.npmrc +.cache/ +volumetests/ +tests/rpki_tests/vrp.json +tests/rpki_tests/roas.json +tests/generate_tests/prefixes.yml +export.json +tests/kafka_tests/kafka_2.13-2.6.0/ +tests/kafka_tests/kafka_2.13-2.6.0.tgz +tests/kafka_tests/nohup.out diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000..839bbc82 --- /dev/null +++ b/.npmignore @@ -0,0 +1,27 @@ +config.yml +prefixes.yml +groups.yml +.idea/ +node_modules/ +bin/ +build/ +src/ +logs/ +.DS_Store +bgpalerter.pid +alertdata/ +.npmrc +.cache/ +volumetests/ +tests/ +Dockerfile +.travis.yml +docs/ +prefixes.yml.example +config.yml.example +build.sh +index.js +.github/ +.hound.yml +.eslintrc.json +.babelrc diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 83da7957..00000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: node_js -node_js: - - 10 - -cache: - yarn: true - directories: - - node_modules - diff --git a/AUTHORS b/AUTHORS index bfd34bfe..abb3d4ff 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,24 +1,26 @@ -BGPalerter was originally created at the beginning of 2019 at NTT Ltd. - -Here is a list of authors and contributors who patched or extended the code. -If this list is not up to date, please contact NTT or one of the authors. - -- AUTHORS - - - Massimo Candela - NTT - https://massimocandela.com/ - -- CONTRIBUTORS - - Damian Zaremba, Fastly - Mircea Ulinic, DigitalOcean - Alan Haynes, Harbin Clinic - - -A special THANK YOU goes to: - - Job Snijders for pushing for this project to happen. - - The RIPE RIS project for the amazing real-time streaming service. - - +BGPalerter was originally created at the beginning of 2019 at NTT Ltd. + +Here is a list of authors and contributors who patched or extended the code. +If this list is not up to date, please contact NTT or one of the authors. + +- AUTHORS - + + Massimo Candela + NTT + https://massimocandela.com/ + +- CONTRIBUTORS - + Damian Zaremba, Fastly + Mircea Ulinic, DigitalOcean + Alan Haynes, Harbin Clinic + Florian Domain, Criteo + Louis Poinsignon, Cloudflare + + +A special THANK YOU goes to: + + Job Snijders for pushing for this project to happen. + + The RIPE RIS project for the amazing real-time streaming service. + + diff --git a/Dockerfile b/Dockerfile index ad17b41f..7dfcdd21 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,14 @@ -FROM ubuntu +# -- trivial container for BGPalerter +FROM node:14-alpine as build COPY setup_build_environment.sh /app/setup_build_environment.sh RUN /app/setup_build_environment.sh WORKDIR /app +# Makes the final image respect /etc/timezone configuration +RUN apk add --no-cache tzdata + +RUN npm ci + +ENTRYPOINT ["npm"] +CMD ["run", "serve"] diff --git a/README.md b/README.md index 39a35f23..1cf948b9 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,41 @@ -[![Build Status](https://travis-ci.org/nttgin/BGPalerter.svg?branch=master)](https://travis-ci.org/nttgin/BGPalerter) -![Dependabot Status](https://badgen.net/dependabot/nttgin/BGPalerter/?icon=dependabot) +[![Build Status](https://github.com/nttgin/BGPalerter/workflows/Main/badge.svg)](https://github.com/nttgin/BGPalerter/actions?query=workflow%3AMain) [![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com) # BGPalerter -Real-time BGP monitoring tool, pre-configured for visibility loss and hijacks detection. +Self-configuring BGP monitoring tool, which allows you to monitor in **real-time** if: +* any of your prefixes loses visibility; +* any of your prefixes is hijacked; +* your AS is announcing RPKI invalid prefixes (e.g., not matching prefix length); +* your AS is announcing prefixes not covered by ROAs; +* any of your ROAs is expiring; +* ROAs covering your prefixes are no longer reachable; +* RPKI Trust Anchors malfunctions; +* a ROA involving any of your prefixes or ASes was deleted/added/edited; +* your AS is announcing a new prefix that was never announced before; +* an unexpected upstream (left-side) AS appears in an AS path; +* an unexpected downstream (right-side) AS appears in an AS path; +* one of the AS paths used to reach your prefix matches a specific condition defined by you. -You just run it. You don't need to provide any data source or connect it to anything in your network since it connects to public repos. +You just run it. You don't need to provide any data source or connect it to anything in your network since it connects to [public repos](docs/datasets.md). -It can deliver alerts on files, by email, on slack, and more. +It can deliver alerts on files, email, kafka, slack, and more. -![BGPalerter](https://massimocandela.com/img/bgpalerter_github_image.png) +![BGPalerter](http://massimocandela.com/img/bgpalerter_github_image.png) +> BGPalerter connects to public BGP data repos (not managed by NTT), and the entire monitoring is done directly in the application (there are no NTT servers involved). + ## TL;DR (1 minute setup) > This section is useful if you don't care about the source code but you just want to run the monitor. Instead, if you want to run the source code (which is completely open) or develop, please read directly the documentation. 1. Download the binary [here](https://github.com/nttgin/BGPalerter/releases) (be sure to select the one for your OS) -2. Execute the binary (e.g. `chmod 700 bgpalerter-linux-x64 && ./bgpalerter-linux-x64`) +2. Execute the binary (e.g., `chmod +x bgpalerter-linux-x64 && ./bgpalerter-linux-x64`) The first time you run it, the auto-configuration will start. -If something happens (e.g. a hijack) you will see the alerts in `logs/reports-YYYY-MM-DD.log`. -In `config.yml` you can find other reporting mechanisms (e.g. email and slack) in addition to logging on files. +If something happens (e.g., a hijack) you will see the alerts in `logs/reports.log`. +In `config.yml` you can find other reporting mechanisms (e.g., email, Slack, Kafka) in addition to logging on files. Please uncomment the related section and configure according to your needs. If the installation doesn't go smoothly, read [here](docs/installation.md). @@ -34,22 +47,49 @@ Read the documentation below for more options. - [Run from binary](docs/installation.md#running-bgpalerter-from-binaries) - [Run from source code](docs/installation.md#running-bgpalerter-from-the-source-code) - [Run in Docker](docs/installation.md#running-bgpalerter-in-docker) + - [Run as a Linux service](docs/linux-service.md) + - [Command line options](docs/installation.md#bgpalerter-parameters) - [Monitored prefixes list](docs/prefixes.md#prefixes) - [Generate prefix list](docs/prefixes.md#generate) - [Prefix attributes description](docs/prefixes.md#prefixes-fields) - [Configuration](docs/configuration.md) - [Composition](docs/configuration.md#composition) - - [Connectors](docs/configuration.md#connectors) - - [Monitors](docs/configuration.md#monitors) - - [Reports](docs/configuration.md#reports) - - [reportFile](docs/configuration.md#reportfile) - - [reportEmail](docs/configuration.md#reportemail) - - [reportSlack](docs/configuration.md#reportslack) - - [reportKafka](docs/configuration.md#reportkafka) - - [reportSyslog](docs/configuration.md#reportsyslog) - - [reportAlerta](docs/configuration.md#reportalerta) - - [reportWebex](docs/configuration.md#reportwebex) + - [Monitor for](docs/configuration.md#monitors) + - [Hijacks](docs/configuration.md#monitorhijack) + - [Path neighbors (downstream/upstream peers)](docs/path-neighbors.md) + - [Visibility loss](docs/configuration.md#monitorvisibility) + - [RPKI invalid announcements](docs/configuration.md#monitorrpki) + - [RPKI ROAs diffs, ROAs expirations, and TA malfunctions](docs/configuration.md#monitorroas) + - [Announcements of more specifics](docs/configuration.md#monitornewprefix) + - [Announcements of new prefixes](docs/configuration.md#monitoras) + - [Path matching](docs/configuration.md#monitorpath) + - [Send alerts to](docs/reports.md#reports) + - [File](docs/reports.md#reportfile) + - [E-mail](docs/reports.md#reportemail) + - [Slack](docs/reports.md#reportslack) + - [Kafka](docs/reports.md#reportkafka) + - [Syslog](docs/reports.md#reportsyslog) + - [Alerta dashboard](docs/reports.md#reportalerta) + - [Webex](docs/reports.md#reportwebex) + - [HTTP URL (push)](docs/reports.md#reporthttp) + - [Telegram](docs/reports.md#reporttelegram) + - [Mattermost](docs/reports.md#mattermost) + - [Pushover](docs/report-http.md#pushover) + - [Microsoft Teams](docs/report-http.md#ms-teams) + - [REST API (pull)](docs/reports.md#reportpullapi) + - [Test report configuration](docs/installation.md#bgpalerter-parameters) - [Process/Uptime monitoring](docs/process-monitors.md) + - [Notification user groups](docs/usergroups.md) + - [RPKI configuration](docs/rpki.md) + - [Staging/testing ROAs](docs/rpki.md#stagingtesting-roas) + - [HTTP/HTTPS proxy](docs/http-proxy.md) +- [Update to latest version](docs/update.md) - [More information for developers](docs/develop.md) - [All npm commands](docs/develop.md#all-npm-commands) + - [Reports/alerts templates](docs/context.md) + - [Release process and Git flow](docs/release-process.md) +- [BGPalerter for researchers](docs/research.md) + + +If you are using BGPalerter, feel free to sign here: [Who is using BGPalerter](docs/friends.md) diff --git a/build.sh b/build.sh index e82506db..8ea3c0f3 100755 --- a/build.sh +++ b/build.sh @@ -3,21 +3,16 @@ rm -rf bin mkdir bin -npm run babel . -- --ignore node_modules --out-dir build +npm ci --silent -cp config.yml.example build/config.yml +npm run compile -cp package.json build/package.json +./node_modules/.bin/pkg ./dist/package.json --targets node14-win-x64 --output bin/bgpalerter-win-x64 --loglevel=error -cd build +./node_modules/.bin/pkg ./dist/package.json --targets node14-linux-x64 --output bin/bgpalerter-linux-x64 --loglevel=error -npm install --silent +./node_modules/.bin/pkg ./dist/package.json --targets node14-macos-x64 --output bin/bgpalerter-macos-x64 --loglevel=error -./node_modules/.bin/pkg . --targets node12-win-x64 --output ../bin/bgpalerter-win-x64 +echo "--> BGPalerter compiled in bin/ (ignore the warnings about files that cannot be resolved)." -./node_modules/.bin/pkg . --targets node12-linux-x64 --output ../bin/bgpalerter-linux-x64 - -./node_modules/.bin/pkg . --targets node12-macos-x64 --output ../bin/bgpalerter-macos-x64 - -cd ../ -rm -rf build +rm -rf dist diff --git a/config.yml.example b/config.yml.example index 208a276b..d33d31b4 100644 --- a/config.yml.example +++ b/config.yml.example @@ -3,8 +3,9 @@ connectors: name: ris params: carefulSubscription: true - url: wss://ris-live.ripe.net/v1/ws/ + url: ws://ris-live.ripe.net/v1/ws/ perMessageDeflate: true + authorizationHeader: null subscription: moreSpecific: true type: UPDATE @@ -12,36 +13,66 @@ connectors: socketOptions: includeRaw: false +# - file: connectorRISDump +# name: dmp + monitors: - file: monitorHijack channel: hijack name: basic-hijack-detection params: - thresholdMinPeers: 2 + thresholdMinPeers: 3 - - file: monitorNewPrefix - channel: newprefix - name: prefix-detection - params: - thresholdMinPeers: 2 +# - file: monitorNewPrefix +# channel: newprefix +# name: prefix-detection +# params: +# thresholdMinPeers: 3 - file: monitorPath channel: path name: path-matching params: - thresholdMinPeers: 0 + thresholdMinPeers: 1 - file: monitorVisibility channel: visibility name: withdrawal-detection params: - thresholdMinPeers: 10 + thresholdMinPeers: 40 - file: monitorAS channel: misconfiguration name: asn-monitor params: - thresholdMinPeers: 2 + thresholdMinPeers: 3 + + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + thresholdMinPeers: 3 + checkUncovered: false + checkDisappearing: false + + - file: monitorROAS + channel: rpki + name: rpki-diff + params: + enableDiffAlerts: true + enableExpirationAlerts: true + enableExpirationCheckTA: true + enableDeletedCheckTA: true + roaExpirationAlertHours: 2 + checkOnlyASns: true + toleranceDeletedRoasTA: 20 + toleranceExpiredRoasTA: 20 + + - file: monitorPathNeighbors + channel: path + name: path-neighbors + params: + thresholdMinPeers: 3 reports: - file: reportFile @@ -51,6 +82,7 @@ reports: - visibility - path - misconfiguration + - rpki params: persistAlertData: false alertDataDirectory: alertdata/ @@ -62,6 +94,7 @@ reports: # - visibility # - path # - misconfiguration +# - rpki # params: # showPaths: 5 # Amount of AS_PATHs to report in the alert # senderEmail: bgpalerter@xxxx @@ -81,8 +114,10 @@ reports: # rejectUnauthorized: true # Reject unauthorized certificates # notifiedEmails: # default: +# - admin@example.org +# noc: # - joe@example.org -# - noc@example.org +# - seb@example.org # - file: reportSlack # channels: @@ -91,6 +126,7 @@ reports: # - visibility # - path # - misconfiguration +# - rpki # params: # showPaths: 0 # Amount of AS_PATHs to report in the alert # colors: @@ -98,8 +134,10 @@ reports: # newprefix: '#fa9548' # visibility: '#fad648' # path: '#42cbf5' +# rpki: '#d892f0' # hooks: # default: _YOUR_SLACK_WEBHOOK_URL_ +# noc: _YOUR_SLACK_WEBHOOK_URL_ # - file: reportKafka # channels: @@ -108,6 +146,7 @@ reports: # - visibility # - path # - misconfiguration +# - rpki # params: # host: localhost # port: 9092 @@ -122,10 +161,12 @@ reports: # - path # - asn-monitor # - misconfiguration +# - rpki # params: -# host: localhost +# host: 127.0.0.1 # port: 514 -# templates: +# transport: udp +# templates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md # default: "++BGPalerter-3-${type}: ${summary}|${earliest}|${latest}" # hijack: "++BGPalerter-5-${type}: ${summary}|${prefix}|${description}|${asn}|${newprefix}|${neworigin}|${earliest}|${latest}|${peers}" # newprefix: "++BGPalerter-4-${type}: ${summary}|${prefix}|${description}|${asn}|${newprefix}|${neworigin}|${earliest}|${latest}|${peers}" @@ -139,19 +180,21 @@ reports: # - visibility # - path # - misconfiguration +# - rpki # params: # severity: # hijack: critical # newprefix: informational # visibility: debug # path: trace -# resourceTemplates: +# resourceTemplates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md # default: "${type}" # hijack: "hijack::${prefix}@@${asn}" # newprefix: "newprefix::${prefix}@@${asn}" # visibility: "visibility::${prefix}@@${asn}" # urls: # default: _YOUR_ALERTA_API_URL_ +# noc: _YOUR_ALERTA_API_URL_ # - file: reportWebex # channels: @@ -160,32 +203,93 @@ reports: # - visibility # - path # - misconfiguration +# - rpki +# params: # hooks: # default: _YOUR_WEBEX_WEBHOOK_URL_ +# noc: _YOUR_WEBEX_WEBHOOK_URL_ +# - file: reportHTTP +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# templates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md +# default: '{"text": "${summary}"}' +# headers: +# isTemplateJSON: true +# showPaths: 0 # Amount of AS_PATHs to report in the alert +# hooks: +# default: _YOUR_WEBHOOK_URL_ +# noc: _YOUR_WEBHOOK_URL_ + +# - file: reportTelegram +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# showPaths: 0 # Amount of AS_PATHs to report in the alert +# botUrl: https://api.telegram.org/bot<_BOT_ID_>/sendMessage +# chatIds: +# default: _CHAT_ID_ +# noc: _CHAT_ID_ + +# - file: reportPullAPI +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# maxAlertsAmount: 25 ############################ # Notification settings: # - notificationIntervalSeconds # Defines the amount of seconds after which an alert can be repeated. An alert is repeated only if the event that # triggered it is not yet solved. -# +# - persistStatus +# Persist the status of BGPalerter. If the process is restarted, the list of alerts already sent is recovered +# and they are not repeated. The process must be able to write on disc, this option will create a file inside .cache/ + +notificationIntervalSeconds: 86400 +persistStatus: true -notificationIntervalSeconds: 14400 +############################ +# REST API settings: +# - rest.host +# The IP address the server will listen. The default value is localhost, this means the API will not be reachable +# from another host. To make it public use null or 0.0.0.0. +# - rest.port +# The port the server will listen + +rest: + host: localhost + port: 8011 logging: directory: logs logRotatePattern: YYYY-MM-DD - backlogSize: 1000 #Advanced option, read the doc maxRetainedFiles: 10 maxFileSizeMB: 15 compressOnRotation: false + useUTC: true checkForUpdatesAtBoot: false checkForUpdatesInterval: 432000000 # 1000 * 3600 * 24 * 5 // Check every 5 days checkForUpdates: false - +generatePrefixListEveryDays: 5000 ############################ # Process monitoring settings: @@ -195,8 +299,6 @@ checkForUpdates: false # - file: uptimeApi # params: # useStatusCodes: true -# host: null -# port: 8011 # # - file: uptimeHealthcheck # params: @@ -216,6 +318,38 @@ checkForUpdates: false monitoredPrefixesFiles: - prefixes.yml +############################ +# The file containing the user groups. +# User groups can be specified +# 1) directly above, in each report module; or +# 2) inside an external file, as specified below (disabled by default). +# Using an external file allows BGPalerter to auto-reload the user group definitions +# when the external file is changed. + +# groupsFile: groups.yml.example + +############################ +# HTTP proxy setting: +# Allow to run BGPalerter behind an HTTP/HTTPS proxy. +# You can also specify which module can bypass the proxy. +# More information here: https://github.com/nttgin/BGPalerter/blob/main/docs/http-proxy.md + +# httpProxy: http://username:password@127.0.0.1:9000 + + +############################ +# RPKI settings: +# Global RPKI settings shared across all monitors requiring RPKI data +# More information here: https://github.com/nttgin/BGPalerter/blob/main/docs/rpki.md +# +# 29/06/2021: rpkiclient has been set as default vrpProvider in this example since is the only one supporting +# ROAs expiration data. See what expiration data enables you to do: https://github.com/nttgin/BGPalerter/releases/tag/v1.28.1 + +rpki: + vrpProvider: rpkiclient + preCacheROAs: true + refreshVrpListMinutes: 15 + markDataAsStaleAfterMinutes: 120 ############################ @@ -229,4 +363,5 @@ pidFile: bgpalerter.pid maxMessagesPerSecond: 6000 multiProcess: false environment: production +configVersion: 2 diff --git a/docs/configuration.md b/docs/configuration.md index 875ae22a..49d8092d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -7,38 +7,48 @@ The following are common parameters which it is possible to specify in the confi | Parameter | Description | Expected format | Example | Required | |---|---|---|---|---| |notificationIntervalSeconds|Defines the amount of seconds after which an alert can be repeated. An alert is repeated only if the event that triggered it is not yet solved. Please, don't set this value to Infinity, use instead alertOnlyOnce. | An integer | 1800 | Yes | -|monitoredPrefixesFiles| The [list](docs/prefixes.md#array) of files containing the prefixes to monitor. See [here](docs/prefixes.md#prefixes) for more informations. | A list of strings (valid .yml files) | -prefixes.yml | Yes | +|monitoredPrefixesFiles| The [list](prefixes.md#array) of files containing the prefixes to monitor. See [here](prefixes.md#prefixes) for more informations. | A list of strings (valid .yml files) | -prefixes.yml | Yes | |logging| A dictionary of parameters containing the configuration for the file logging. | || Yes| |logging.directory| The directory where the log files will be generated. The directory will be created if not existent. | A string | logs | Yes | |logging.logRotatePattern| A pattern with date placeholders indicating the name of the file. This pattern will also indicate when a log file is rotated. | A string with date placeholders (YYYY, MM, DD, ss, hh) | YYYY-MM-DD | Yes | |logging.compressOnRotation| Indicates if when a file gets rotates it has to be compressed or not. | A boolean | true | Yes | |logging.maxFileSizeMB| Indicates the maximum file size in MB allowed before to be rotated. This allows to rotate files when logRotatePattern still the same but the file is too big | An integer | 15 | Yes | |logging.maxRetainedFiles| Indicates the maximum amount of log files retained. When this threshold is passed, files are deleted. | An integer | 10 | Yes | -|checkForUpdatesAtBoot| Indicates if at each booth the application should check for updates. If an update is available, a notification will be sent to the default group. If you restart the process often (e.g. debugging, experimenting etc.) set this to false to avoid notifications. Anyway, BGPalerter checks for updates every 10 days.| A boolean | true | Yes | -|processMonitors| A list of modules allowing various ways to check for the status of BGPalerter (e.g. API, heartbeat). See [here](process-monitors.md) for more information. | | | No | +|logging.useUTC| If set to true, logs will be reported in UTC time. Is set to false or missing, the timezone of the machine will be used. This parameter affects only the timestamp reported at the beginning of each log entry, it doesn't affect the time reported in the data/alerts which is always in UTC. | A boolean | true | No | +|checkForUpdatesAtBoot| Indicates if at each booth the application should check for updates. If an update is available, a notification will be sent to the default group. If you restart the process often (e.g., debugging, experimenting etc.) set this to false to avoid notifications. Anyway, BGPalerter checks for updates every 10 days.| A boolean | true | Yes | +|processMonitors| A list of modules allowing various ways to check for the status of BGPalerter (e.g., API, heartbeat). See [here](process-monitors.md) for more information. | | | No | +|httpProxy| Defines the HTTP/HTTPS proxy server to be used by BGPalerter and its submodules (reporters/connectors/monitors). See [here](http-proxy.md) for more information. | A string | http://usr:psw@ prxy.org:8080 | No | +|volume| Defines a directory that will contain the data that needs persistence. For example, configuration files and logs will be created in such directory (default to "./"). | A string | /home/bgpalerter/ | No | +|persistStatus| If set to true, when BGPalerter is restarted the list of alerts already sent is recovered. This avoids duplicated alerts. The process must be able to write on disc inside `.cache/`. | A boolean | true | No | +|generatePrefixListEveryDays| This parameter allows to automatically re-generate the prefix list after the specified amount of days. Set to 0 to disable it. It works only if you have one prefix list file and if you have used BGPalerter to automatically generate the file (and not if you edited prefixes.yml manually). | An integer | 0 | No | +|rpki| A dictionary containing the RPKI configuration (see [here](rpki.md) for more details). | | | Yes | +|groupsFile| A file containing user groups definition (see [here](usergroups.md) for more details). | A string | groups.yml | No | +|rest| A dictionary containing the parameters to run the server for all APIs provided by BGPalerter. | | | No | +|rest.host| The IP/host on which the APIs will be reachable. The default value is localhost, this means the API will not be reachable from another host. To make it public use null or 0.0.0.0. | A string or null | localhost | No | +|rest.port| The port of the REST API. The default value is 8011. | An integer | 8011 | No | The following are advanced parameters, please don't touch them if you are not doing research/experiments. | Parameter | Description | Expected format | Example | Required | |---|---|---|---|---| -|environment| You can specify various environments. The values "production" (not verbose) and "development" (verbose) will affect the verbosity of the error/debug logs. Other values don't affect the functionalities, they will be used to identify from which environment the log is coming from. | A string | production | Yes | +|environment| You can specify various environments. The values "production" (not verbose) and "development" (verbose) will affect the verbosity of the error/debug logs. The value "research" is explained [here](research.md). Other values don't affect the functionalities, they will be used to identify from which environment the log is coming from. | A string | production | Yes | |alertOnlyOnce| A boolean that, if set to true, will prevent repetitions of the same alert in the future (which it doesn't make sense for production purposes). In this case notificationIntervalSeconds will be ignored. If set to true, the signature of all alerts will be cached in order to recognize if they already happened in the past. This may lead to a memory leak if the amount of alerts is considerable. | A boolean | false | No | -|pidFile| A file where the PID of the BGP alerter master process is recorded. | A string | bgpalerter.pid | No | -|logging.backlogSize| Indicates the buffer dimension (number of alerts) before flushing it on the disk. This parameter plays a role only when receiving thousand of alerts per second in order to prevent IO starvation, in all other cases (e.g. production monitoring) it is irrelevant. | An integer | 15 | Yes | +|pidFile| A file where the PID of the BGP alerter main process is recorded. | A string | bgpalerter.pid | No | |maxMessagesPerSecond| A cap to the BGP messages received, over such cap the messages will be dropped. The default value is way above any production rate. Changing this value may be useful only for research measurements on the entire address space. | An integer | 6000 | No | |multiProcess| If set to true, the processing of the BGP messages will be distributed on two processes. This may be useful for research measurements on the entire address space. It is discouraged to set this to true for normal production monitoring. | A boolean | false | No | -|fadeOffSeconds| If an alert is generated but cannot be yet squashed (e.g. not reached yet the `thresholdMinPeers`), it is inserted in a temporary list which is garbage collected after the amount of seconds expressed in `fadeOffSeconds`. Due to BGP propagation times, values below 5 minutes can result in false negatives.| An integer | 360 | No | +|fadeOffSeconds| If an alert is generated but cannot be yet squashed (e.g., not reached yet the `thresholdMinPeers`), it is inserted in a temporary list which is garbage collected after the amount of seconds expressed in `fadeOffSeconds`. Due to BGP propagation times, values below 5 minutes can result in false negatives.| An integer | 360 | No | |checkFadeOffGroupsSeconds| Amount of seconds after which the process checks for fading off alerts. | An integer | 30 | No | + ## Composition You can compose the tool with 3 main components: connectors, monitors, and reports. * Connectors retrieve/listen to the data from different sources and transform them to a common format. * Monitors analyze the data flow and produce alerts. Different monitors try to detect different issues. -* Reports send/store the alerts, e.g. by email or to a file. +* Reports send/store the alerts, e.g., by email or to a file. Reports can also provide the data triggering such alerts. > In config.yml.example there are all the possible components declarations (similar to the one of the example below). You can enable the various components by uncommenting the related block. @@ -79,6 +89,7 @@ Each monitor declaration is composed of: | channel | The name of the channel that will be used by the monitor to dispatch messages. If the inserted name doesn't correspond to an already existent channel, a new channel is created.| |name| The name associated to the monitor. Multiple monitors with the same implementation can be loaded with different names. This name will be used to annotate messages in order track from where they are coming from.| |params| A dictionary of parameters that can be useful for the functioning of the monitor. Different monitors with the same implementation can be initialized with different parameters. | +|params.noProxy| If there is a global proxy configuration (see [here](http-proxy.md)), this parameter if set to true allows the single module to bypass the proxy. | Each report declaration is composed of: @@ -86,7 +97,8 @@ Each report declaration is composed of: |---|---| | file | Name of the file containing the report implementation. Report implementations are in the `reports` directory. | |params| A dictionary of parameters that can be useful for the functioning of the report. It is common to have group declarations among the parameters. Different reports with the same implementation can be initialized with different parameters. | -|channels| A [list](docs/prefixes.md#array) of channels the monitor will listen (never write). Different reports with the same implementation can be initialized with a different list of channels to listen.| +|params.noProxy| If there is a global proxy configuration (see [here](http-proxy.md)), this parameter if set to true allows the single module to bypass the proxy. | +|channels| A [list](docs/prefixes.md#array) of channels the report module will listen (never write). Different reports with the same implementation can be initialized with a different list of channels to listen.| Each connector is composed of: @@ -95,13 +107,18 @@ Each connector is composed of: |---|---| |file|Name of the file containing the connector implementation. Report implementations are in the `connectors` directory. | |params| A dictionary of parameters that can be useful for the functioning of the connector. E.g. the data source url, password, socket options| +|params.noProxy| If there is a global proxy configuration (see [here](http-proxy.md)), this parameter if set to true allows the single module to bypass the proxy. | |name| The name that will be used to identify this connector and to annotate logs and messages. | + > Connectors will always send the BGP updates to all the channels. The BGP updates have all the same format. ### Connectors +Connectors retrieve/listen to the data from different sources and transform them to a common format. + +Possible connectors are: #### connectorRIS It connects to RIPE RIS https://ris-live.ripe.net/ and receives BGP updates coming from 600+ peers. @@ -110,8 +127,20 @@ Parameters for this connector module: |Parameter| Description| |---|---| -|url| WebSocket end-point of RIS, which currently is `wss://ris-live.ripe.net/v1/ws/` | +|url| WebSocket end-point of RIS, which currently is `ws://ris-live.ripe.net/v1/ws/` | |subscription| Dictionary containing the parameters required by RIS. Refer to the [official documentation](https://ris-live.ripe.net/) for details.| +|carefulSubscription| If this parameter is set to true (default), the RIS server will stream only the data related to our prefix. This is an advanced parameter useful only for research purposes. | +|perMessageDeflate| Enable gzip compression on the connection. | + +#### connectorRISDump +It connects to the RIPEstat's BGPlay API and retrieves a RIS dump about the monitored resources. The retrieved dump is 2 hours old, due to limitations on the API side. + +Without this connector, when you start BGPalerter the monitoring will start based on new BGP updates. This means that you will not receive alerts before a new BGP update is propagated; e.g., if one of your prefixes is already hijacked when you start BGPalerter, you will not get notified immediately. + +This connector runs only in the two following conditions: +- you enable it in the config.yml (commented out by default); +- you didn't start BGPalerter in the last two hours. + #### connectorTest @@ -119,6 +148,9 @@ Connector used for testing purposes, it provokes all types of alerting. Needed t ### Monitors +Monitors analyze the data flow and produce alerts. Different monitors try to detect different issues. + +Possible monitors are: #### monitorHijack @@ -164,18 +196,24 @@ This monitor detects BGP updates containing AS_PATH which match particular regul > The prefixes list of BGPalerter has an entry such as: > ```yaml > 165.254.255.0/24: -> asn: 15562 -> description: an example on path matching -> ignoreMorespecifics: false -> path: -> match: ".*2194,1234$" -> notMatch: ".*5054.*" -> matchDescription: detected scrubbing center +> asn: 15562 +> description: an example on path matching +> ignoreMorespecifics: false +> path: +> - match: ".*2194,1234$" +> notMatch: ".*5054.*" +> matchDescription: detected scrubbing center +> - match: ".*123$" +> notMatch: ".*5056.*" +> matchDescription: other match > ``` -> An alert will be generated when a BGP announcements for 165.254.255.0/24 or a more specific contains an AS_PATH -> terminating in 2194,1234 but not containing 5054. The generated alert will report the matchDescription field. -More path matching options are available, see the entire list [here](prefixes.md#prefixes-fields) +Path is a list of matching rules, in this way multiple matching rules can be defined for the same prefix (rules are in OR). + +More about path matching [here](path-matching.md). + +> An alert will be generated for example when a BGP announcements for 165.254.255.0/24 or a more specific contains an AS_PATH +> terminating in 2194,1234 but not containing 5054. The generated alert will report the matchDescription field. Example of alert: > Matched "an example on path matching" on prefix 98.5.4.3/22 (including length violation) 1 times @@ -204,7 +242,7 @@ In particular, it will monitor for all the declared prefixes and will trigger an > description: an example > ignoreMorespecifics: false > ``` -> If in config.yml monitorNewPrefix is enabled you will receive alerts every time a more specific prefix (e.g. 50.82.4.0/24) is announced by AS58302. +> If in config.yml monitorNewPrefix is enabled you will receive alerts every time a more specific prefix (e.g., 50.82.4.0/24) is announced by AS58302. Example of alert: @@ -221,7 +259,7 @@ Parameters for this monitor module: #### monitorAS This monitor will listen for all announcements produced by the monitored Autonomous Systems and will detect when a prefix, which is not in the monitored prefixes list, is announced. -This is useful if you want to be alerted in case your AS starts announcing something you didn't intend to announce (e.g. misconfiguration, typo). +This is useful if you want to be alerted in case your AS starts announcing something you didn't intend to announce (e.g., misconfiguration, typo). > Example: @@ -239,9 +277,9 @@ This is useful if you want to be alerted in case your AS starts announcing somet > ``` > If in config.yml monitorAS is enabled, you will receive alerts every time a prefix not already part of the prefixes list is announced by AS58302. > ->If AS58302 starts announcing 45.230.23.0/24 an alert will be triggered. This happens because such prefix is not already monitored (it's not a sub prefix of 50.82.0.0/20). +> If AS58302 starts announcing 45.230.23.0/24 an alert will be triggered. This happens because such prefix is not already monitored (it's not a sub prefix of 50.82.0.0/20). -You can generate the options block in the prefixes list automatically. Refer to the options `-s` and `-m` in the [auto genere prefixes documentation](prefixes.md#generate). +You can generate the options block in the prefixes list automatically. Refer to the options `-s` and `-m` of the [auto configuration](prefixes.md#generate). Example of alert: @@ -254,107 +292,131 @@ Parameters for this monitor module: |thresholdMinPeers| Minimum number of peers that need to see the BGP update before to trigger an alert. | |maxDataSamples| Maximum number of collected BGP messages for each alert which doesn't reach yet the `thresholdMinPeers`. Default to 1000. As soon as the `thresholdMinPeers` is reached, the collected BGP messages are flushed, independently from the value of `maxDataSamples`.| -### Reports - -Possible reports are: - -#### reportFile + +#### monitorRPKI -This report module is the default one. It sends the alerts as verbose logs. -To configure the logs see the [configuration introduction](configuration.md). +This monitor will listen for all announcements produced by the monitored Autonomous Systems and for all the announcements +involving any of the monitored prefixes (independently of who is announcing them), and it will trigger an alert if any of the announcements is RPKI invalid or not covered by ROAs (optional). -Parameters for this report module: - -|Parameter| Description| -|---|---| -|persistAlertData| If set to true, the BGP messages that triggered an alert will be collected in JSON files. The default is false.| -|alertDataDirectory| If persistAlertData is set to true, this field must contain the directory where the JSON files with the BGP messages will be stored. | +This monitor is particularly useful when: +* Before RPKI deployment, since it will let you test what announcements will be invalid after singing the ROAs. +* During RPKI deployment, since it will let you know if any of your announcements are invalid. +* After you deployed RPKI, in order to be sure all future BGP configurations will be covered by ROAs. +> Example: +> The prefixes list of BGPalerter has the following entries: +> ```yaml +> 103.21.244.0/24: +> asn: 13335 +> description: an example +> ignoreMorespecifics: false +> +> options: +> monitorASns: +> 13335: +> group: default +> ``` +> If in config.yml monitorRPKI is enabled, you will receive alerts every time: +> * 103.21.244.0/24 is announced and it is not covered by ROAs, or the announcement is RPKI invalid; +> * AS13335 announces something that is not covered by ROAs, or the announcement is RPKI invalid; +> * A prefix you announce used to be covered by a ROA but such ROA is no longer available (e.g., RPKI repositories past failures: [ARIN](https://www.arin.net/announcements/20200813/), [RIPE NCC](https://www.ripe.net/support/service-announcements/accidental-roa-deletion)) -#### reportEmail +Examples of alerts: +> The route 103.21.244.0/24 announced by AS13335 is not RPKI valid +> The route 1.2.3.4/24 announced by AS1234 is not covered by a ROA +> The route 1.2.3.4/24 announced by AS1234 is no longer covered by a ROA -This report module sends the alerts by email. +You need to configure your RPKI data source as described [here](rpki.md). -Parameters for this report module: +Parameters for this monitor module: |Parameter| Description| |---|---| -|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | -|senderEmail| The email address that will be used as sender for the alerts. | -|smtp| A dictionary containing the SMTP configuration. Some parameters are described in `config.yml.example`. For all the options refer to the [nodemailer documentation](https://nodemailer.com/smtp/). | -|notifiedEmails| A dictionary containing email addresses grouped by user groups. (key: group, value: list of emails)| -|notifiedEmails.default| The default user group. Each user group is a [list](prefixes.md#array) of emails. This group should contain at least the admin. | +|checkUncovered| If set to true, the monitor will alert also for prefixes not covered by ROAs in addition of RPKI invalid prefixes. | +|checkDisappearing| If set to true, the monitor will check also for disappearing ROAs. Important: set this feature to false if you have monitorROAS enabled; monitorROAS provides diffs including disappearing ROAs. | +|thresholdMinPeers| Minimum number of peers that need to see the BGP update before to trigger an alert. | +|maxDataSamples| Maximum number of collected BGP messages for each alert which doesn't reach yet the `thresholdMinPeers`. Default to 1000. As soon as the `thresholdMinPeers` is reached, the collected BGP messages are flushed, independently from the value of `maxDataSamples`.| +|cacheValidPrefixesSeconds| Amount of seconds ROAs get cached in order to identify RPKI repository malfunctions (e.g., disappearing ROAs). Default to 7 days. | +#### monitorROAS -#### reportSlack +This monitor will periodically check the ROAs involving any of your ASes or prefixes. +In particular, it will report about: ROAs involving your resources being edited, added or removed; expiring ROAs; TA malfunctions. +You need to configure your RPKI data source as described [here](rpki.md). +Note, while BGPalerter will perform the check near real time, many RIRs have delayed ROAs publication times. -This report module sends alerts on Slack. -Parameters for this report module: +> Example: +> The prefixes list of BGPalerter has the following entries: +> ```yaml +> 1.2.3.4/24: +> asn: 1234 +> description: an example +> ignoreMorespecifics: false +> group: noc1 +> +> options: +> monitorASns: +> 2914: +> group: noc2 +> ``` +> If in config.yml monitorROAS is enabled, you will receive alerts every time: +> * A ROA that is, or was, involving 1.2.3.4/24 is added/edited/removed (based on the prefix `1.2.3.4/24` matching rule). +> * A ROA that is, or was, involving AS2914 is added/edited/removed (based on the `monitorASns` section). -|Parameter| Description| -|---|---| -|colors| A dictionary having as key the event channel and as value a hex color (string). These colors will be used to make messages in Slack distinguishable. | -|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | -|hooks| A dictionary containing Slack WebHooks grouped by user group (key: group, value: WebHook).| -|hooks.default| The WebHook (URL) of the default user group.| +**Important 1:** for a complete monitoring, configure also the `monitorASns` section. Setting only prefix matching rules is not sufficient: prefix matching rules are based on the longest prefix match, less specific ROAs impacting the prefix will NOT be matched. On the other side, setting only the `monitorASns` section is instead perfectly fine for ROA monitoring purposes. +**Important 2:** prefix matching rules have always priorities on `monitorASns` rules. If an alert matches both a prefix rule and an AS rule, it will be sent only to the prefix rule, except if the `checkOnlyASns` params is set to true (see parameters below). In the example above, a ROA change impacting `1.2.3.4/24` is only sent to the user group `noc1` and not to `noc2`; whatever other ROA change impacting a prefix not in the list (no prefix matching rule) will be sent to `noc2` instead. -#### reportKafka +**Important 3:** alerts about the generic health status of TAs are generated according to the provided VRP file. This types of alerts are not necessarily related to the monitored resources and they are send to the `default` user group. -This report sends the alerts (including the BGP messages triggering them) to Kafka. By default it creates a topic `bgpalerter`. +Example of alerts: +> ROAs change detected: removed <1.2.3.4/24, 1234, 25, apnic>; added <5.5.3.4/24, 1234, 25, apnic> +> +> Possible TA malfunction: 24% of the ROAs disappeared from APNIC -Parameters for this report module: +**This monitor also alerts about ROAs expiration.** -|Parameter| Description| -|---|---| -|host| Host of the Kafka instance/broker (e.g. localhost).| -|port| Port of the Kafka instance/broker (e.g. 9092).| -|topics| A dictionary containing a mapping from BGPalerter channels to Kafka topics (e.g. `hijack: hijack-topic`). By default all channels are sent to the topic `bgpalerter` (`default: bgpalerter`) | - -#### reportSyslog - -This report module sends the alerts on Syslog. +**Note:** This feature requires a vrp file having an `expires` field for each vrp, currently supported only by [rpki-client](https://www.rpki-client.org/). To enable this feature, provide a file generated with rpki-client (version 7.1 and newer) or use `vrpProvider: rpkiclient` in your rpki configuration ([more info](rpki.md)). -Parameters for this report module: +ROAs are affected by a series of expiration times: +* Certificate Authority's "notAfter" date; +* each CRL's "nextUpdate" date; +* each manifest's EE certificate notAfter, and each manifests eContent "nextUpdate"; +* the ROA's own EE certificate "notAfter". -|Parameter| Description| -|---|---| -|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | -|host| Host of the Syslog server (e.g. localhost).| -|port| Port of the Syslog server (e.g. 514).| -|templates| A dictionary containing string templates for each BGPalerter channels. If a channel doesn't have a template defined, the `default` template will be used (see `config.yml.example` for more details). | +The field `expire` must be the closest expiration time of all of the above. -#### reportAlerta +Example of alerts: +> The following ROAs will expire in less than 2 hours: <1.2.3.4/24, 1234, 25, apnic>; <5.5.3.4/24, 1234, 25, apnic> +> +> Possible TA malfunction: 24% of the ROAs are expiring in APNIC -This report module sends alerts to [Alerta](https://alerta.io/). -Alerta is an open-source and easy to install dashboard that allows you to collect and monitor color-coded alerts. -Parameters for this report module: +Parameters for this monitor module: -|Parameter| Description | +|Parameter| Description| |---|---| -|severity| The alert severity, e.g., ``critical``. See https://docs.alerta.io/en/latest/api/alert.html#alert-severities for the list of possible values. | -|environment| The Alerta environment name. If not specified, it'll use the BGPalerter environment name. | -|key| Optional, the Alerta API key to use for authenticated requests. | -|token| Optional value used when executing HTTP requests to the Alerta API with bearer authentication. | -|resource_templates| A dictionary of string templates for each BGPalerter channels to generate the content of the `resource` field for the alert. If a channel doesn't have a template defined, the `default` template will be used (see `config.yml.example` for more details). | -|urls| A dictionary containing Alerta API URLs grouped by user group (key: group, value: API URL). | -|urls.default| The Alerta API URL of the default user group. | +|enableDiffAlerts| Enables alerts showing edits impacting ROAs for the monitored resources. Default true| +|enableExpirationAlerts| Enables alerts about expiring ROAs. Default true.| +|enableExpirationCheckTA| Enables alerts about TA malfunctions detected when too many ROAs expire in the same TA. Default true.| +|enableDeletedCheckTA| Enables alerts about TA malfunctions detected when too many ROAs are deleted in the same TA. Default true.| +|roaExpirationAlertHours| If a ROA is expiring in less than this amount of hours, an alert will be triggered. The default is 2 hours. I strongly suggest to keep this value, ROAs are almost expiring every day, read above what this expiration time means. | +|checkOnlyASns| If set to true (default), ROAs diff alerts will be generated based only on the ASns contained in the `monitorASns` of `prefixes.yml`. This means that no ROA diffs will be matched against prefix matching rules (see example above). If you are monitoring the origin AS of your prefixes, leave this option to true to avoid noise.| +|toleranceExpiredRoasTA|The percentage of expiring ROAs in a single TA tolerated before triggering a TA malfunction alert. Default 20.| +|toleranceDeletedRoasTA|The percentage of deleted ROAs in a single TA tolerated before triggering a TA malfunction alert. Default 20.| -> If you receive a 403 error in the BGPalerter error logs, try to check if you correctly set the ALLOWED_ENVIRONMENTS in /etc/alertad.conf. -> In particular set ALLOWED_ENVIRONMENTS=['Production','Development']. +#### monitorPathNeighbors -#### reportWebex +The component `monitorPathNeighbors` allows to monitor for unexpected neighbor ASes in AS paths. The list of neighbors can be specified in `prefixes.yml` inside the `monitorASns` sections. -This report module sends alerts on [Webex Teams](https://teams.webex.com). +Refer to the [documentation for this monitor](path-neighbors.md). -Parameters for this report module: -|Parameter| Description| -|---|---| -|hooks| A dictionary containing Webex Teams WebHooks grouped by user group (key: group, value: WebHook).| -|hooks.default| The WebHook (URL) of the default user group.| +# Reports +Reports send/store the alerts, e.g., by email or to a file. Reports can also provide the data triggering such alerts. +Refer to the [report's documentation](reports.md). + diff --git a/docs/context.md b/docs/context.md new file mode 100644 index 00000000..41a3ab7a --- /dev/null +++ b/docs/context.md @@ -0,0 +1,26 @@ +# Report context + +All the report modules inherit the method `getContext` from the super class `Report`. This method returns a dictionary with some pre-computed tags useful for composing textual reports. +Such tags are reported in the table below. + +| Tag | Description | +|---|---| +| summary | The summary of the alert | +| earliest | The date of the earliest event that triggered the alert (format YYYY-MM-DD HH:mm:ss)| +| latest | The date of the last event that triggered the alert (format YYYY-MM-DD HH:mm:ss)| +| channel | The channel where the alert is coming from | +| type | The name of the monitor that triggered the alert | +| prefix | The monitored prefix involved in the alert | +| description | The description of the prefix involved in the alert | +| asn | The monitored AS involved in the alert | +| paths | The AS Paths involved in the alert | +| pathNumber | The count of AS Paths in the alert | +| peers | The number of peers that were able to see the issue | +| neworigin | The AS announcing the monitored prefix (e.g., in case of a hijack, `neworigin` will contain the hijacker, `asn` will contain the usual origin) | +| newprefix | The prefix announced (e.g., in case of a hijack, `newprefix` will contain the more specific prefix used for the hijack, `prefix` will contain the usual prefix) | +| bgplay | The link to BGPlay on RIPEstat | + + +Usage example: `The alert involves ${prefix} in ${earliest}` will be translated in something like `The alert involves 1.2.3.4/24 in 2020-04-14 04:02:13`. + +> The same approach must be used to populate the templates available in config.yml. If you are writing a template for an API call, convert the JSON to string (e.g., '{"text": "${summary}"}'). diff --git a/docs/datasets.md b/docs/datasets.md new file mode 100644 index 00000000..d40fdee9 --- /dev/null +++ b/docs/datasets.md @@ -0,0 +1,7 @@ +# List of datasets used by BGPalerter + +* [RIPE RIS live](https://ris-live.ripe.net/) +* [RIPEstat](http://stat.ripe.net/) +* [NTT RPKI VRPs](https://rpki.gin.ntt.net/api/export.json) +* [Cloudflare VRPs](https://rpki.cloudflare.com/) +* [rpki-client.org VRPs](https://www.rpki-client.org/) \ No newline at end of file diff --git a/docs/develop.md b/docs/develop.md index 89f038fb..e9c2091e 100644 --- a/docs/develop.md +++ b/docs/develop.md @@ -5,9 +5,7 @@ To start development see how to install the source [here](installation.md#runnin ## All npm commands -* `npm run watch-and-serve` to run the application from source code and monitor for file changes - -* `npm run serve` to run the application from the source +* `npm run serve` to run the application from the source code * `npm run test` to run the tests diff --git a/docs/friends.md b/docs/friends.md new file mode 100644 index 00000000..002ec901 --- /dev/null +++ b/docs/friends.md @@ -0,0 +1,34 @@ +# Who is using BGPalerter + +> This page is new, so many names are missing. +Please, let me know so I can add your company name here. + +* Seattle Internet Exchange (SIX) +* Food and Agriculture Organization of the United Nations (FAO) +* Latin America and Caribbean Network Information Centre (LACNIC) +* DigitalOcean (DO) +* Freethought Internet (AS41000) +* MTLNOG +* EBOX (AS1403) +* Cloudflare (AS13335) +* Vocus (AS4826 & AS9443) +* HEAnet (AS1213) +* Tech Futures (AS394256) +* Fastly (AS54113) +* EDGOO Networks (AS47787) +* IT.Gate (AS12779) +* Sky (AS5607) +* SBTAP (AS59715) +* WiscNet (AS2381) +* Artfiles GmbH (AS8893) +* Namex IXP (AS24796) +* Zero Attack Vector LLC (AS212996 & AS398549) +* 12128489 Canada Inc dba Accuris Hosting (AS212934) +* Brennercom S.p.A. (AS20811) +* Solcon Internetdiensten B.V. (AS12414) +* Webair (AS27257) +* Genesis Cloud (AS209045) +* Suretec Systems Limited T/A SureVoIP (AS199659) +* TelcoSwitch Limited (AS45033) +* Ziron Limited (AS49344) +* Elite Limited (AS29611) diff --git a/docs/http-proxy.md b/docs/http-proxy.md new file mode 100644 index 00000000..02f4fb52 --- /dev/null +++ b/docs/http-proxy.md @@ -0,0 +1,34 @@ +# Run BGPalerter behind a proxy +BGPalerter can be run in a production environment requiring to access external resources via an HTTP/HTTPS proxy. +To enable this setting, in `config.yml` uncomment the following line and set the correct proxy URL: + +```yaml +httpProxy: http://username:password@proxy.example.org:8080 +``` +This will enable the proxy globally on all HTTP/HTTPS/WebSocket traffic generated by BGPalerter. + +## Bypass proxy for specific BGPalerter modules + +While the global configuration will send all requests to the proxy, you can specify which modules are able to bypass the proxy. +This is useful for example if you want to not proxy requests to internal apps or networks. +This can be set per module (i.e reporter/connector/monitor) by adding the `noProxy: true` parameter to the desired module(s) in `config.yml`. +For instance, the configuration below allows you to bypass your proxy server for your traffic towards an Alerta dashboard. + + +```yaml + - file: reportAlerta + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + noProxy: true + severity: + hijack: critical + newprefix: informational + visibility: debug + path: trace +``` \ No newline at end of file diff --git a/docs/img/bgpalerter_github_image.png b/docs/img/bgpalerter_github_image.png new file mode 100644 index 00000000..aa47a950 Binary files /dev/null and b/docs/img/bgpalerter_github_image.png differ diff --git a/docs/img/diagram_release.drawio b/docs/img/diagram_release.drawio new file mode 100644 index 00000000..c244b854 --- /dev/null +++ b/docs/img/diagram_release.drawio @@ -0,0 +1 @@ +5VfbctMwEP2aPGbGl9z6SNJQCqVDb4Q3RtgbW0TWprIcJ/161pFsx3Ua2jKFAg+JraO11nv27Eru+JNkfaLYMv6IIYiO54Trjn/c8TzXHXl0KZCNQYa+BSLFQ2tUA1f8DizoWDTjIaQNQ40oNF82wQClhEA3MKYU5k2zOYqm1yWLoAVcBUy00RkPdWzQkTes8XfAo7j07A6OzEzCSmMbSRqzEPMdyJ92/IlC1OYuWU9AFOSVvMxONzNxthicvL9Ib9nN+MP1+eeuWeztUx6pQlAg9bOXPs1v/PFl/3YxVV9HF1+ibO2uun0bmt6UfEFI9NkhKh1jhJKJaY2OFWYyhGJVh0a1zRnikkCXwO+g9cZqgWUaCYp1Iuys8Vk4upein8Rn7VLMVAAH7HwrM6Yi0AeCd6skkvoBE9BqQ88pEEzzVfPlmJVhVNnVVNONZfsJSbUvuWIis54uQQBLoagFJkMeMg2t7NTcF0TmMddwtWRbNnKq3ybPc5TaJsGlQMeRYGlq05ZqhYuqIgrrSt5OlaLHZmQFSsN6h682qXa2Z2vJNpNqnNel6Zb1Fu+UZWn3K2nYq4HBX14ADwv7EQXg/aYCOPSSOwXwSUFXlUXwR4XfEvRjk/Og8P1+75Up33u4Af1j3A+Gr63rDFvcUwROl3694rUAFildcV4cQ1ByjYrLqER0XOwR59fX9C9B56gWhT9piGJCbE9wOafQSuOlghXHLDXFbXeZOapir4khWJjVU82+ccE1xTfZWtpG4zmZJvyOmgJKM0cb1PbMlpBH8zBZZ0KnLfFQLnVTIUYFExTk3z+WKIueOudC3IOY4JGkYUC5B8LHhTI4neze2ImEh+G2Ie+TZLNJv5S03KN70hrtkVZvj7SOXkpao33SGltV7RWVUY7CMAtMgg/I7L9Kbt95ZnKf0TdoWH9JbOd2vsf86Q8= \ No newline at end of file diff --git a/docs/img/diagram_release.png b/docs/img/diagram_release.png new file mode 100644 index 00000000..8cc8267c Binary files /dev/null and b/docs/img/diagram_release.png differ diff --git a/docs/installation.md b/docs/installation.md index ca9ef786..6783eb65 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -4,7 +4,7 @@ 1. Download the binary [here](https://github.com/nttgin/BGPalerter/releases) (be sure to select the one for your OS) -2. Execute the binary (e.g. `chmod 700 bgpalerter-linux-x64 && ./bgpalerter-linux-x64`) +2. Execute the binary (e.g., `chmod +x bgpalerter-linux-x64 && ./bgpalerter-linux-x64`) The first time you run it, the auto-configuration will start. ## Running BGPalerter from binaries - All steps @@ -13,22 +13,24 @@ The first time you run it, the auto-configuration will start. 1. Download the binary: `wget https://github.com/nttgin/BGPalerter/releases/latest/download/bgpalerter-linux-x64` -2. Download [`config.yml.example`](https://raw.githubusercontent.com/nttgin/BGPalerter/master/config.yml.example) as `config.yml` (in the same directory of the binary) +2. Download [`config.yml.example`](https://raw.githubusercontent.com/nttgin/BGPalerter/main/config.yml.example) as `config.yml` (in the same directory of the binary) -3. Make the binary executable (e.g. `chmod 700 bgpalerter-linux-x64`) +3. Make the binary executable (e.g., `chmod +x bgpalerter-linux-x64`) 4. Auto-configure it: `./bgpalerter-linux-x64 generate -a _YOUR_ASN_ -o prefixes.yml -i -m` 5. Run it: `./bgpalerter-linux-x64` Or use `nohup ./bgpalerter-linux-x64 &` to leave it running after you close the terminal +Additionally, you can configure [BGPalerter to run as a Linux Serivce](linux-service.md) + #### Mac 1. Download the binary [here](https://github.com/nttgin/BGPalerter/releases/latest/download/bgpalerter-macos-x64). -2. Download [`config.yml.example`](https://raw.githubusercontent.com/nttgin/BGPalerter/master/config.yml.example) as `config.yml` (in the same directory of the binary) +2. Download [`config.yml.example`](https://raw.githubusercontent.com/nttgin/BGPalerter/main/config.yml.example) as `config.yml` (in the same directory of the binary) -3. Make the binary executable (e.g. `chmod 700 bgpalerter-macos-x64`) +3. Make the binary executable (e.g., `chmod +x bgpalerter-macos-x64`) 4. Auto-configure it: `./bgpalerter-macos-x64 generate -a _YOUR_ASN_ -o prefixes.yml -i -m` @@ -39,7 +41,7 @@ Or use `nohup ./bgpalerter-linux-x64 &` to leave it running after you close the 1. Download the binary [here](https://github.com/nttgin/BGPalerter/releases/latest/download/bgpalerter-win-x64.exe). -2. Download [`config.yml.example`](https://raw.githubusercontent.com/nttgin/BGPalerter/master/config.yml.example) as `config.yml` (in the same directory of the binary) +2. Download [`config.yml.example`](https://raw.githubusercontent.com/nttgin/BGPalerter/main/config.yml.example) as `config.yml` (in the same directory of the binary) 3. Open cmd (press `ctrl + R` and type `cmd`) and `cd` on the directory where you downloaded the binary (usually `cd C:\Users\_USER_\Downloads`) @@ -49,11 +51,11 @@ Or use `nohup ./bgpalerter-linux-x64 &` to leave it running after you close the 1. Git clone this repo. -2. Install Node.js (version >= 10.16) and npm ([installing node and npm](node.md)). +2. Install Node.js (version >= 12) and npm ([installing node and npm](node.md)). 3. Execute `npm install` to install all dependencies. -4. Run `npm run watch-and-serve` to run the application. At every file change it will self-reload. +4. Run `npm run serve` to run the application. ## Running BGPalerter in Docker @@ -64,4 +66,69 @@ There are two main builds: * `latest` stable version for production monitoring; * `dev` reflects the last commit in the `dev` branch. Use this only for development purposes. -Additionally, each release has its own build in case you want to revet back to an older version. +Additionally, each release has its own build in case you want to revert back to an older version. + +To run the latest stable version of BGPalerter in Docker, do: + +``` +docker run -i --name bgpalerter \ + -v $(pwd)/volume:/opt/bgpalerter/volume \ + nttgin/bgpalerter:latest run serve -- --d /opt/bgpalerter/volume/ +``` + +With this command, a new directory `./volume` will be created in the current position. +Such directory will contain all the persistent data that BGPalerter will generate, including configuration and alert logs. +You can specify another directory by changing the directory before the colon in the -v flag (e.g., `-v _LOCATION_YOU_WANT_/volume:/opt/bgpalerter/volume`). + +The command above runs BGPalerter in interactive mode (`-i` flag), which is necessary if you want to run the auto configuration. + +You should replace the flag `-i` with the flag `-d`, when: +* You already have the configuration files `config.yml` and `prefixes.yml`, or you plan to create them by hand. Just place them into the volume directory. +* You executed BGPalerter with the `-i` flag and the volume directory and the configuration files have been already generated. + +For production monitoring we suggest to monitor the uptime of BGPalerter. +In case you want to monitor the uptime by using the `uptimeApi` ([read more](process-monitors.md)), you need to map the port in the docker configuration with the following command: + +```bash +docker run -i --name bgpalerter \ + -v $(pwd)/volume:/opt/bgpalerter/volume \ + -p 8011:8011 \ + nttgin/bgpalerter:latest run serve -- --d /opt/bgpalerter/volume/ +``` + +The `uptimeApi` module has to be enabled in `volume/config.yml` as described [here](process-monitors.md). +Now you can monitor `http://127.0.0.1:8011/status` (e.g., in Nagios) to check the status of the BGPalerter monitoring. +Such API may return a negative result when there is a misconfiguration or when BGPalerter failed to connect to the data repository. + + + +Optionally, you can specify a health check in docker to auto-restart the container in case of prolonged failure. + +```bash +docker run -i --name bgpalerter \ + -v $(pwd)/volume:/opt/bgpalerter/volume \ + --health-cmd='wget --quiet --tries=1 --spider http://127.0.0.1:8011/status || exit 1' \ + --health-timeout=2s \ + --health-retries=15 \ + --health-interval=60s \ + --restart unless-stopped \ + -p 8011:8011 \ + nttgin/bgpalerter:latest run serve -- --d /opt/bgpalerter/volume/ +``` + +> This option does NOT replace [proper monitoring](process-monitors.md). +Just restarting the container will not assure you that the monitoring is working properly or that it will work again. You should always investigate failures and fix possible misconfiguration. + +## BGPalerter parameters + +The execution of BGPalerter supports some parameters + +| Parameter | Description | +|---|---| +| -v | Show version number | +| -h | Show help | +| -c | To specify the config file to load (default `./config.yml`) | +| -d | To specify a directory where configuration and data is persisted (see [volume](configuration.md)). | +| -t | To test the configuration by generating fake BGP updates. This will start sending alerts on all the reports listening the `hijack` channel. | + +You can also use the same parameters with npm (if you are running the source code), in the following format `npm run serve -- --h` (replace `h` with the parameter you need). diff --git a/docs/linux-service.md b/docs/linux-service.md new file mode 100644 index 00000000..fe2925fb --- /dev/null +++ b/docs/linux-service.md @@ -0,0 +1,156 @@ +## Run BGPalerter as a Linux Service +If you are interested in running this application as a service on a Linux server here is a basic guide covering how to do that. This process works for RHEL 7 based Linux installations. It will likely work very similiarly on other systemctl enabled installations. + +### Create directory for the application to reside +Create a user for BGPalerter + +```bash +adduser bgpalerter +sudo su bgpalerter +``` + +If this is a new installation, download the BGPalerter binary in the home of the newly created user and execute it: + +``` +cd /home/bgpalerter +wget https://github.com/nttgin/BGPalerter/releases/latest/download/bgpalerter-linux-x64 +chmod +x bgpalerter-linux-x64 +./bgpalerter-linux-x64 +``` +The auto-configuration will start at the end of which all the needed files will be created. + +If this is an existing install simply move the files of your existing install into this directory `mv -t /home/bgpalerter bgpalerter-linux-x64 bgpalerter.pid config.yml prefixes.yml` + +The application will also create `logs` and `src` subdirectories here if needed. + +### Create systemd service file +Next you need to create the systemd service file. + +`sudo vi /etc/systemd/system/bgpalerter.service` + +The contents of this file should be as follows: + +``` +[Unit] +Description=BGPalerter +After=network.target + +[Service] +Type=simple +Restart=on-failure +User=bgpalerter +WorkingDirectory=/home/bgpalerter +ExecStart=/home/bgpalerter/bgpalerter-linux-x64 + +[Install] +WantedBy=multi-user.target +``` + +### Reload systemd +Reload systemd to register the new configuration. + +`systemctl daemon-reload` + +### Enable and start the service +Enable BGPalerter to start at boot and then start the service. + +`systemctl enable bgpalerter` + +`systemctl start bgpalerter` + + +### Automatic Updates +Enable automatic updates. + +`cd /home/bgpalerter` + +`vi upgrade.sh` + +The file needs to be executable +``` +chmod +x upgrade.sh +chown bgpalerter:bgpalerter /home/bgpalerter/upgrade.sh +``` + +The contents of this file should be as follows: + +``` +#!/usr/bin/env bash + +#If log file does not exist, create it +if [ ! -f /home/bgpalerter/logs/upgrade.log ]; then + touch /home/bgpalerter/logs/upgrade.log + chown bgpalerter:bgpalerter /home/bgpalerter/logs/upgrade.log +fi + +#Log everything if executing manually +exec 1> /home/bgpalerter/logs/upgrade.log 2>&1 +set -vex +PS4='+\t ' + +#Download the latest version and save it to a temp file +wget -O bgpalerter-linux-x64.tmp https://github.com/nttgin/BGPalerter/releases/latest/download/bgpalerter-linux-x64 + +#Set permissions and ownership to execute the file and capture the version +chmod +x bgpalerter-linux-x64.tmp +chown -R bgpalerter:bgpalerter /home/bgpalerter/ + +#Set variables to compare versions +if [ -f bgpalerter-linux-x64 ]; then + #If a file exists already + v1=$(./bgpalerter-linux-x64 -v) + v2=$(./bgpalerter-linux-x64.tmp -v) + +else + #If the file does not exist - For testing purposes + v1=$"0" + v2=$(./bgpalerter-linux-x64.tmp -v) +fi + +#If the versions are not the same +if [ "$v1" == "0" ];then + #Rename the temp file + mv bgpalerter-linux-x64.tmp bgpalerter-linux-x64 + + #Restart the service + systemctl restart bgpalerter + + #Pause for one second for service to fully start + sleep 1 + +elif [ "$v1" != "$v2" ];then + #Rename the old binary and append the version + mv bgpalerter-linux-x64 "bgpalerter-linux-x64-$v1" + + #Rename the temp file + mv bgpalerter-linux-x64.tmp bgpalerter-linux-x64 + + #Restart the service + systemctl restart bgpalerter + + #Pause for one second for service to fully start + sleep 1 + +else + #If the versions are the same - delete the temp file + rm bgpalerter-linux-x64.tmp +fi + +#Log service status +systemctl status bgpalerter -l + +#Delete renamed binaries older than 60 days +find -type f -name 'bgpalerter-linux-x64-*' -mtime +60 -delete + +#Delete log file if larger than 5MB +find /home/bgpalerter/logs/ -type f -name "upgrade.log" -size +5M -delete +``` + +Configure a cron job to run, in this case, weekly. + +`crontab -e` + +The contents of this file should be as follows: +``` +0 0 * * 0 /home/bgpalerter/bgpalerter/upgrade.sh +``` diff --git a/docs/node.md b/docs/node.md index c1eeb337..dd83d573 100644 --- a/docs/node.md +++ b/docs/node.md @@ -2,7 +2,7 @@ Some fast commands are below. The complete documentation (including other platforms) is [here](https://nodejs.org/en/download/) -## With apt (e.g. debian) +## With apt (e.g., debian) ```bash curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - @@ -10,14 +10,14 @@ sudo apt install nodejs ``` -## With homebrew (e.g. macos) +## With homebrew (e.g., macos) ```bash brew update brew install node@12 ``` -## With yum (e.g. centos) +## With yum (e.g., centos) ```bash curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - diff --git a/docs/path-matching.md b/docs/path-matching.md new file mode 100644 index 00000000..df0460dd --- /dev/null +++ b/docs/path-matching.md @@ -0,0 +1,53 @@ +# Path matching + +The component `monitorPath` allows to specify rules in order to get notified when specific conditions are matched on AS_PATHs. + +The rules must be expressed with regular expressions (if you need to refresh your skills or test a regular expression, I suggest [this](https://regex101.com/)). +The AS_PATH parsed by BGPalerter are in the form `137,3333,1335,2914`. + +> Example: +> The prefixes list of BGPalerter has an entry such as: +> ```yaml +> 165.254.255.0/24: +> asn: 15562 +> description: an example on path matching +> ignoreMorespecifics: false +> path: +> - match: ".*2194,1234$" +> notMatch: ".*5054.*" +> matchDescription: detected scrubbing center +> - match: ".*123$" +> notMatch: ".*5056.*" +> matchDescription: other match +> ``` + + +Each item in the `path` list is a matching rule. +Each matching rule is composed of: +* `match`, the regular expression that will be tested on each AS path. If the expression tests positive, the BGP message triggers an alert. ASns are comma separated (see example above). +* `notMatch`, the regular expression that will be tested on each AS path. If the expression tests positive, the BGP message will not trigger an alert. ASns are comma separated (see example above). +* `matchDescription`, the description that will be reported in the alert in case the matchin rule results in a match. +* `maxLength`, the maximum length allowed for an AS path. Longer paths will trigger an alert. +* `minLength`, the minimum length allowed for an AS path. Shorter paths will trigger an alert. + +> Remember, the various matching rules are in OR among each other, while the fields inside a single matching rule are in AND. +> This means that in the example above, the user will receive an alert if (".*2194,1234$" AND NOT ".*5054.*") OR (".*123$" AND NOT ".*5056.*") + +## Some common examples + +* `(,|^)789$` - match paths that originate with AS789, no matter what it has in front (including nothing); +* `(,|^)456,` - match any path that traverses AS456 at any point, except origin; +* `(,|^)456(,|$)` - match any path that traverses AS456 at any point (including as origin, or as last AS); +* `^123,456,` - match paths where the last traversed ASns were 123 and 456 (in that order); +* `^123,456,789$` - match the exact path "123,457,789"; +* `\[789,101112\]` - match paths containing the AS_SET [789, 101112]. + + +### Match regular expression with multiple conditions in AND + +If you want to specify multiple conditions in AND in the same match parameter, you can use the positive lookahead construct offered natively by regular expressions. +A positive lookahead is in the form `(?=exp1)(?=exp2)` where `exp1` and `exp2` are regular expressions. +Positive lookaheads work also for expressing negative conditions (e.g., `(?!exp)`), but in most of the cases this is redundant with the `notMatch` parameter. + + + diff --git a/docs/path-neighbors.md b/docs/path-neighbors.md new file mode 100644 index 00000000..925b527f --- /dev/null +++ b/docs/path-neighbors.md @@ -0,0 +1,70 @@ +# Upstream and downstream AS monitoring + +The component `monitorPathNeighbors` allows to monitor for unexpected neighbor ASes in AS paths. The list of neighbors can be specified in `prefixes.yml` inside the `monitorASns` sections. + +> For example, imagine AS100 has two upstreams, AS99 and AS98, and one downstream, AS101. You can express the following rule in 'prefixes.yml' +> +> ```yaml +> options: +> monitorASns: +> 100: +> group: noc +> upstreams: +> - 99 +> - 98 +> downstreams: +> - 101 +> ``` + +Every time an AS path is detected with a different upstream/downstream AS, an alert will be generated. + +**You can generate the upstream/downstream lists automatically. Refer to the options `-u` and `-n` of the [auto configuration](prefixes.md#generate).** + +According to the above configuration, +* the AS path [10, 20, 30, 100, 101] will generate an alert since AS30 is not an upstream of AS100; +* the AS path [10, 20, 30, 100] will generate an alert since AS30 is not an upstream of AS100; +* the AS path [10, 20, 99, 100, 101] will not generate an alert since AS99 is an upstream of AS100 and AS101 is a downstream of of AS100; +* the AS path [10, 20, 99, 100, 104] will generate an alert since AS104 is not a downstream of AS100; +* the AS path [100, 104] will generate an alert since AS104 is not a downstream of AS100. + +You can disable the monitoring by removing the upstreams and downstreams lists or by removing the `monitorPathNeighbors` block in `config.yml`. + +If you delete only one of the upstreams and downstreams lists, the monitoring will continue on the remaining one. + +> E.g., the config below monitors only for upstreams +> +> ```yaml +> options: +> monitorASns: +> 100: +> group: noc +> upstreams: +> - 99 +> - 98 +> ``` + +Example of alert: +> A new upstream of AS100 has been detected: AS30 +> + + +If you provide empty lists, the monitoring will be performed and you will receive an alert for every upstream/downstream. + +> E.g., the config below monitors only for downstreams and expects to never see any downstream AS (stub network) +> +> ```yaml +> options: +> monitorASns: +> 100: +> group: noc +> downstreams: +> ``` + + + +Parameters for this monitor module: + +|Parameter| Description| +|---|---| +|thresholdMinPeers| Minimum number of peers that need to see the BGP update before to trigger an alert. | +|maxDataSamples| Maximum number of collected BGP messages for each alert which doesn't reach yet the `thresholdMinPeers`. Default to 1000. As soon as the `thresholdMinPeers` is reached, the collected BGP messages are flushed, independently from the value of `maxDataSamples`.| diff --git a/docs/prefixes.md b/docs/prefixes.md index dbea0a0e..d20a46c4 100644 --- a/docs/prefixes.md +++ b/docs/prefixes.md @@ -3,8 +3,8 @@ ## Auto-generate prefixes list To auto generate the monitored prefixes file (by default called `prefixes.yml`) execute: -* If you are using the binary `./bgpalerter-linux-x64 generate -a ASN(S) -o OUTPUT_FILE` (e.g. `./bgpalerter-linux-x64 generate -a 2914 -o prefixes.yml`). -* If you are using the source code `npm run generate-prefixes -- --a ASN(S) --o OUTPUT_FILE` (e.g. `npm run generate-prefixes -- --a 2914 --o prefixes.yml`). +* If you are using the binary `./bgpalerter-linux-x64 generate -a ASN(S) -o OUTPUT_FILE` (e.g., `./bgpalerter-linux-x64 generate -a 2914 -o prefixes.yml`). +* If you are using the source code `npm run generate-prefixes -- --a ASN(S) --o OUTPUT_FILE` (e.g., `npm run generate-prefixes -- --a 2914 --o prefixes.yml`). The script will detect whatever is currently announced by the provided AS and will take this as "the expected status". @@ -14,14 +14,21 @@ Below the list of possible parameters. **Remember to prepend them with a `--` in | Parameter | Description | Expected format | Example | Required | |---|---|---|---|---| -| -o | The YAML output file | A string ending in ".yml" | prefixes.yml | Yes | -| -a | The AS number(s) you want to generate the list for | A comma-separated list of integers | 2914,3333 | No (one among -a, -p, -l is required) | -| -e | Prefixes to exclude from the list | A comma-separated list of prefixes | 165.254.255.0/24,192.147.168.0/24 | No | -| -i | Avoid monitoring delegated prefixes. If a more specific prefix is found and it results announced by an AS different from the one declared in -a, then set `ignore: true` and `ignoreMorespecifics: true` | Nothing | | No -| -p | Prefixes for which the list will be generated | A comma-separated list of prefixes | 165.254.255.0/24,192.147.168.0/24 | No (one among -a, -p, -l is required) | -| -l | A file containing the prefixes for which the list will be generated | A text file having a prefix for each line | prefixes.txt | No (one among -a, -p, -l is required) | -| -s | A list of ASns to be monitored. See [monitorASns](#monitorASns) for more information | A comma separated list of integer | 2914,3333 | No | -| -m | Monitor all ASns which are origin of at least one of the monitored prefixes. This option is the same of `-s` except that the list of ASns is automatically generated by detecting the origin AS of all the monitored prefixes. See [monitorASns](#monitorASns) for more information | Nothing | | No | +| -o | The YAML output file. | A string ending in ".yml" | prefixes.yml | Yes | +| -a | The AS number(s) you want to generate the list for. | A comma-separated list of integers | 2914,3333 | No (one among -a, -p, -l is required) | +| -e | Prefixes to exclude from the list. | A comma-separated list of prefixes | 165.254.255.0/24,192.147.168.0/24 | No | +| -i | Avoid monitoring delegated prefixes. If a more specific prefix is found and it results announced by an AS different from the one declared in -a, then set `ignore: true` and `ignoreMorespecifics: true`. | Nothing | | No +| -p | Prefixes for which the list will be generated. | A comma-separated list of prefixes | 165.254.255.0/24,192.147.168.0/24 | No (one among -a, -p, -l is required) | +| -l | A file containing the prefixes for which the list will be generated. | A text file having a prefix for each line | prefixes.txt | No (one among -a, -p, -l is required) | +| -s | A list of ASns to be monitored. See [monitorASns](#monitorASns) for more information. | A comma separated list of integer | 2914,3333 | No | +| -m | Monitor all ASns which are origin of at least one of the monitored prefixes. This option is the same of `-s` except that the list of ASns is automatically generated by detecting the origin AS of all the monitored prefixes. See [monitorASns](#monitorASns) for more information. | Nothing | | No | +| -x | HTTP/HTTPS proxy server to use. | A string | http://username:password@proxy.example.org:8080 | No | +| -A | Append the new configuration to the old one. E.g. you can use this option to add another AS. | Nothing | | No | +| -D | Enable debug mode. All queries executed in background will be shown. | Nothing | | No | +| -H | Use historical visibility data for generating prefix list (prefixes visible in the last week). Useful in case the prefix generation process returns an empty dataset. | Nothing | | No | +| -g | The name of the user group that will be assigned to all the generated rules. See [here](usergroups.md). | A string | noc | No | +| -u | Calculate all upstream ASes and enable detection of new left-side ASes. See [here](path-neighbors.md). | Nothing | | No | +| -n | Calculate all downstream ASes and enable detection of new right-side ASes. See [here](path-neighbors.md). | Nothing | | No | ## Prefixes list fields @@ -59,19 +66,14 @@ Below the complete list of attributes (the dot notation is used to represent yml | Attribute | Description | Expected type | Required | |---|---|---|---| -| asn | The expected origin AS(es) of the prefix | An integer or an array of integers. | Yes | -| description | A description that will be reported in the alerts | A string | Yes | -| ignoreMorespecifics | Prefixes more specific of the current one will be excluded from monitoring | A boolean | Yes | -| ignore | Exclude the current prefix from monitoring. Useful when you are monitoring a prefix and you want to exclude a particular sub-prefix| A boolean | No | +| asn | The expected origin AS(es) of the prefix. | An integer or an array of integers. | Yes | +| description | A description that will be reported. in the alerts | A string | Yes | +| ignoreMorespecifics | Prefixes more specific of the current one will be excluded from monitoring. | A boolean | Yes | +| ignore | Exclude the current prefix from monitoring. Useful when you are monitoring a prefix and you want to exclude a particular sub-prefix. | A boolean | No | | includeMonitors | The list of monitors you want to run on this prefix. If this attribute is not declared, all monitors will be used. Not compatible with excludeMonitors. | An array of strings (monitors name according to config.yml) | No | -| excludeMonitors | The list of monitors you want to exclude on this prefix. Not compatible with includeMonitors. Use monitors `name` attributes, as defined in the monitor listy in [config.yml](https://github.com/nttgin/BGPalerter/blob/master/config.yml.example). | An array of strings (monitors name according to config.yml) | No | -| path | A dictionary containing all sub-attributes for path matching. All the sub-attributes are in AND.| Sub-attributes (as follows) | No | -| path.match | The regular expression that will be tested on each AS path. If the expression tests positive the BGP message triggers an alert. ASns are comma separated (see example above). **Please, use optimized regular expression as described [in the following sub-section](#optimized-regular-expressions-for-as-path-matching)** | A string (valid RegEx) | No | -| path.notMatch | The regular expression that will be tested on each AS path. If the expression tests positive the BGP message will not triggers an alert. ASns are comma separated (see example above). | A string (valid RegEx) | No | -| path.matchDescription | The description that will be reported in the alert in case the regex test results in a match. | A string | No | -| path.maxLength | The maximum length allowed for an AS path. Longer paths will trigger an alert. | A number | No | -| path.minLength | The minimum length allowed for an AS path. Shorter paths will trigger an alert. | A number | No | -| group | The name of the group that will receive alerts about this monitored prefix. By default all alerts are sent to the "default" group. | A string | No | +| excludeMonitors | The list of monitors you want to exclude on this prefix. Not compatible with includeMonitors. Use monitors `name` attributes, as defined in the monitor list in [config.yml](https://github.com/nttgin/BGPalerter/blob/main/config.yml.example). | An array of strings (monitors name according to config.yml) | No | +| path | A list path matching rules, read more [here](path-matching.md). | | No | +| group | The name of the group that will receive alerts about this monitored prefix. See [here](usergroups.md).| A string | No | ### Options entry @@ -107,16 +109,4 @@ monitorASns: The AS2914 and AS3333 will be monitored. The alerts related to AS2914 will be sent to the "ntt" user group and the alerts for AS3333 to the "ripencc" user group. -The monitor in charge of doing this type of detection is [monitorAS (click for more information)](configuration.md#monitoras). - -### Optimized regular expressions for AS path matching - -The following simple regular expressions will drastically reduce CPU and network usage when applied to the `path.match` attribute. Instead, there are no benefits in applying the following regular expressions to the `path.notMatch` attribute. - -To drastically optimize the process, try to use one of the following regular expression for `path.match` attribute. If the obtained filter is too loose, add additional (complex) constraints in `path.notMatch`. In this way the more complex `path.notMatch` will be tested only on the subset produced by the faster `path.match`. - -* "789$" - match paths that originate with AS789 -* "456" - match any path that traverses AS456 at any point -* "^123,456" - match paths where the last traversed ASns were 123 and 456 (in that order) -* "^123,456,789$" - match the exact path [123, 457, 789] -* "[789,101112]" - match paths containing the AS_SET {789, 101112} +The monitor in charge of doing this type of detection is [monitorAS (click for more information)](configuration.md#monitoras). \ No newline at end of file diff --git a/docs/process-monitors.md b/docs/process-monitors.md index 45e88afe..89d99104 100644 --- a/docs/process-monitors.md +++ b/docs/process-monitors.md @@ -10,9 +10,6 @@ processMonitors: - file: uptimeApi params: useStatusCodes: true - host: null - port: 8011 - - file: uptimeHealthcheck params: url: url_to_poll @@ -49,15 +46,23 @@ In `config.yml` the uptimeApi is declared as: ```yaml processMonitors: - - file: uptimeApi params: useStatusCodes: true - host: null - port: 8011 ``` -When the uptimeApi block is commented/deleted from the config file, no extra dependencies are loaded and no open port is required. +When the uptimeApi block is commented/deleted from the config file, no extra dependencies are loaded and no open port is required. + + +The REST API uses the generic `rest` configuration in `config.yml`. Read [here](configuration.md) or see `config.yml.example` for more information. +The REST configuration is by default: +```yaml +rest: + host: localhost + port: 8011 +``` + + The API, in addition to the JSON answer, can use HTTP status codes for an easier integration with Nagios and similar. @@ -65,16 +70,14 @@ Parameters for this module are: |Parameter| Description| |---|---| -|useStatusCodes| A boolean that if set to true enables HTTP status codes in the response. Nothing changes in the JSON output provided by the API. | -|host| The IP address on which the API will be reachable. If `null` or missing, the API will be reachable on all the addresses of the machine.| -|port| The port on which the API will be reachable. | +|useStatusCodes| A boolean that if set to true enables HTTP status codes in the response. Nothing changes in the JSON output provided by the API. | ## uptimeHealthcheck The uptimeHealthcheck module is a component that will start polling a provided URL at a regular interval. -This can be used to send a heartbeat signal to a monitoring system (e.g. https://healthchecks.io/). +This can be used to send a heartbeat signal to a monitoring system (e.g., https://healthchecks.io/). If there is any warning about any component activated in BGPalerter, the heartbeat will not be issued (independently from the fact that the process is still running). diff --git a/docs/release-process.md b/docs/release-process.md new file mode 100644 index 00000000..c6c71e74 --- /dev/null +++ b/docs/release-process.md @@ -0,0 +1,82 @@ +# Release process + +We use BGPalerter in production to monitor our network. +It's essential for us to make sure BGPalerter is reliable and secure. As it is essential for us, it is also essential for the many organizations that are currently relying on BGPalerter for their monitoring. +For this reason, we want to summarize in this document our efforts in this matter. + +The image below shows the flow for releasing a new version of BGPalerter + +![BGPalerter](img/diagram_release.png) + + + +## Release candidate +A release candidate is not a release at all, and so it will not appear in the [release tab](https://github.com/nttgin/BGPalerter/releases), it will not have a release number, and its testing is ongoing. +A release candidate is a way for us to identify the set of features that are mature enough to be included in the next release. + +We compile the release candidate and we use it to monitor our network. We do this while running in parallel the previous stable release of BGPalerter. +This not only assures us to have at least a stable monitoring running, but also allows us to compare the two versions in terms of resource utilization and reported alerts. +We do such testing for at least 1 week (but in general, especially in busy periods, this lasts more, and we leave it running for up to 4 weeks). + +Additionally, we have continuous testing in place and we test the release candidate against the entire data flow generated by [RIPE RIS](https://ris.ripe.net) for the entire v4 and v6 address spaces (6k+ BGP updates per second). + +## Pre release +We create a pre-release after a release candidate passed the test period. A pre-release is *NOT intended for production deployment*. +A pre-release appears in the [release tab](https://github.com/nttgin/BGPalerter/releases) marked by an orange label declaring its pre-release status. +A pre-release is NOT considered `latest`, so you will never update to a pre-release without explicitly selecting its version. +Additionally, pre-releases are not announced and not notified as updates. + +Pre-releases are anyway tested and considered "stable". Consider installing a pre-release if you want to try the latest features. + +## Release +We finally create a new release after the pre-release passes the 1 week test period. +A release appears in the [release tab](https://github.com/nttgin/BGPalerter/releases) and it is marked with a green label "latest release". +The availability of a new release is notified. + +### Release number +The release number is composed of 3 digis `x.x.x`: + +* The first number identifies a breaking release. The first number is incremented only when the breaking change cannot be addressed with a deprecation warning. +* The second number identifies a major release. The second number is incremented if new functionalities are introduced. +* The third number identifies a minor release. The third number is incremented when there are changes which don't introduce any new functionality but improve the available ones. + +### Patching +In the event a serious bug (e.g., affecting one of the core functionalities) is discovered in a released version, we will not follow the timing described above and release as soon as possible a minor release. +Minor releases are anyway tested and deployed for the monitoring of our network. + +A minor release can also be released as a part of the normal release cycle in case there are no new features to justify a major release. + +### Dependencies +We use [Dependabot](https://dependabot.com/) to automatically check for newer version of the dependencies used by BGPalerter. +The dependencies used are listed in [package.json](https://github.com/nttgin/BGPalerter/blob/dev/package.json). +It is important to distinguish between the dependencies used for development and the one used for production. Only production dependencies are bundled in the binary of a release. + +### Module isolation +BGPalerter includes a series of components (i.e. connectors, monitors, reports) each of them are enabled from config.yml. +All components are bundled in the binary release, BUT they are loaded (and so also their dependencies) only if enabled from the config file. +If a component is not enabled from the config file, the component file is not loaded and the code not executed. + +### Automation + +We currently use the following automation on the repository: + +* [GitHub Actions](https://github.com/nttgin/BGPalerter/actions) to perform tests on each commit and pull request. +* [Dependabot](https://dependabot.com/) to check for newer version of the dependencies used. +* [Hound](https://houndci.com/) to check code correctness and style. +* [GitHub Security Alerts](https://github.com/nttgin/BGPalerter/network/alerts) to check for vulnerabilities on the entire repository. + + +# Git flow + +1) The development happens in the `dev` branch. +2) When a set of feature is defined as the next release candidate, the `dev` branch is branched to create the `release` branch, which contains the release candidate code. +3) The release candidate is tested as described above. +4) After the test period, the release candidate is promoted to pre-release: + 1) The source is tagged with a tag reporting the release number + 2) It is compiled and released in the [release tab](https://github.com/nttgin/BGPalerter/releases) +5) The pre-release is tested +6) After the test period, the pre-released is marked as latest and released as stable. + 1) Clients will be notified of the new release. +7) The `release` branch is merged in `main` and after deleted. + +All pull requests must happen against the `dev` branch (or rebased during review). If needed they will be cherry-picked in the `release` branch. diff --git a/docs/report-http.md b/docs/report-http.md new file mode 100644 index 00000000..1b6f77ca --- /dev/null +++ b/docs/report-http.md @@ -0,0 +1,140 @@ +# Send alerts with POST requests + +BGPalerter can send alerts by means of POST requests to a provided URL. +This can be done by configuring the module reportHTTP. Read [here](configuration.md#reporthttp) to understand how. + +For configuring reportHTTP, essentially you need to specify two things: +* The URL +* A template of the POST request. + +If you are using [user groups](usergroups.md), you can specify a URL for every user group. This can be done inside `hooks`, a dictionary containing API URLs grouped by user group (key: group, value: URL). +The default user group is mandatory. +Example: +```yaml + hooks: + default: https://MY_WEB_HOOK/ + noc: https://MY_WEB_HOOK_FOR_THE_NOC_GROUP +``` + +You can also specify a template for each type of alert (channel). More information about templates is available [here](context.md). +Example: +```yaml + isTemplateJSON: true + templates: + default: '{"message": "${summary}", "color": "blue"}' + visibility: '{"message": "${summary}", "color": "orange"}' +``` + +Templates are expressed as strings. If the parameter `isTemplateJSON` is set to true, the string will be converted to JSON before to be posted. + +What follows is a list of examples showing how to adapt this module to some well-known applications. + + +## Mattermost + +Mattermost is an open source messaging platform. + +```yaml +reports: + - file: reportHTTP + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + templates: + default: '{"attachments": [ + { + "author_name" : "BGPalerter", + "fields": [ + {"title": "Event type:", "value": "${type}", "short": "true"}, + {"title": "First event:", "value": "${earliest} UTC", "short": "true"}, + {"title": "Last event:", "value": "${latest} UTC", "short": "true"} + ], + "text": "${channel}: ${summary}", "color": "#ffffff" + } + ]}' + isTemplateJSON: true + headers: + showPaths: 0 # Amount of AS_PATHs to report in the alert + hooks: + default: WEBHOOK_URL +``` +Thanks to [@fstolba](https://github.com/nttgin/BGPalerter/issues/81). + +## Pushover + +Pushover is an app that makes it easy to get real-time notifications on your Android, iPhone, iPad, and Desktop. + +```yaml +- file: reportHTTP + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + templates: + default: '{"message": "${channel}: ${summary}", "title": "BGPalerter", "priority": "1", "token": "_YOUR_API_TOKEN_HERE_", "user": "_YOUR_USER_KEY_HERE_"}' + headers: + isTemplateJSON: true + showPaths: 0 + hooks: + default: https://api.pushover.net/1/messages.json +``` + +Thanks to [Hugo Salgado](https://twitter.com/huguei/status/1278771420525408258). + +## MS Teams + +Microsoft Teams is a communication platform developed by Microsoft, as part of the Microsoft 365 family of products. + +```yaml +reports: + - file: reportHTTP + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + templates: + default: '{ + "@type": "MessageCard", + "@context": "http://schema.org/extensions", + "themeColor": "d76100", + "summary": "BGPalerter", + "sections": [{ + "activityTitle": "BGPalerter", + "activitySubtitle": "${channel}", + "facts": [{ + "name": "Summary", + "value": "${summary}" + }, { + "name": "Event type", + "value": "${type}" + }, { + "name": "First event", + "value": "${earliest} UTC" + }, { + "name": "Last event", + "value": "${latest} UTC" + }], + "markdown": true + }] + }' + isTemplateJSON: true + headers: + showPaths: 0 # Amount of AS_PATHs to report in the alert + hooks: + default: https://WEBHOOK_URL +``` + +Thanks [arpanet-creeper](https://github.com/nttgin/BGPalerter/pull/412) for the help. diff --git a/docs/reports.md b/docs/reports.md new file mode 100644 index 00000000..fa4474e5 --- /dev/null +++ b/docs/reports.md @@ -0,0 +1,174 @@ +# Reports + +Reports send/store the alerts, e.g., by email or to a file. Reports can also provide the data triggering such alerts. + +After configuring a report module, you can run the BGPalerter binary with the option `-t` to test the configuration. +This will generate fake alerts. [Read more here](installation.md#bgpalerter-parameters). + +> By default all communications will be sent to the default user group, so it is not mandatory to configure any user group. +> Note that the default group is used also for administrative and error communications, if you want to filter out such communications you need to [create another user group](usergroups.md). + +#### Possible reports are: + +- [reportFile](reports.md#reportFile) +- [reportEmail](reports.md#reportEmail) +- [reportSlack](reports.md#reportSlack) +- [reportKafka](reports.md#reportKafka) +- [reportSyslog](reports.md#reportSyslog) +- [reportAlerta](reports.md#reportAlerta) +- [reportWebex](reports.md#reportWebex) +- [reportHTTP](reports.md#reportHTTP) +- [reportTelegram](reports.md#reportTelegram) +- [reportPullAPI](reports.md#reportPullAPI) + +## reportFile + +This report module is the default one. It sends the alerts as verbose logs. +To configure the logs see the [configuration introduction](configuration.md). + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|persistAlertData| If set to true, the BGP messages that triggered an alert will be collected in JSON files. The default is false.| +|alertDataDirectory| If persistAlertData is set to true, this field must contain the directory where the JSON files with the BGP messages will be stored. | + +## reportEmail + +This report module sends the alerts by email. + +Read [here](context.md) how to write a template. + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | +|senderEmail| The email address that will be used as sender for the alerts. | +|smtp| A dictionary containing the SMTP configuration. Some parameters are described in `config.yml.example`. For all the options refer to the [nodemailer documentation](https://nodemailer.com/smtp/). | +|notifiedEmails| A dictionary containing email addresses grouped by user groups. (key: group, value: list of emails)| +|notifiedEmails.default| The default user group. Each user group is a [list](prefixes.md#array) of emails. This group should contain at least the admin. | + +After configuring this module, [test the configuration](installation.md#bgpalerter-parameters) (`-t` option) to be sure everything will work once in production. + +## reportSlack + +This report module sends alerts on Slack. + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|colors| A dictionary having as key the event channel and as value a hex color (string). These colors will be used to make messages in Slack distinguishable. | +|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | +|hooks| A dictionary containing Slack WebHooks grouped by user group (key: group, value: WebHook).| +|hooks.default| The WebHook (URL) of the default user group.| + +## reportKafka + +This report sends the alerts (including the BGP messages triggering them) to Kafka. By default it creates a topic `bgpalerter`. + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|host| Host of the Kafka instance/broker (e.g., localhost).| +|port| Port of the Kafka instance/broker (e.g., 9092).| +|topics| A dictionary containing a mapping from BGPalerter channels to Kafka topics (e.g., `hijack: hijack-topic`). By default all channels are sent to the topic `bgpalerter` (`default: bgpalerter`) | + +## reportSyslog + +This report module sends the alerts on Syslog. + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | +|host| Host of the Syslog server (e.g., localhost).| +|port| Port of the Syslog server (e.g., 514).| +|transport| The transport protocol to use. Two options: `udp` or `tcp`| +|templates| A dictionary containing string templates for each BGPalerter channels. If a channel doesn't have a template defined, the `default` template will be used (see `config.yml.example` for more details). | + +## reportAlerta + +This report module sends alerts to [Alerta](https://alerta.io/). +Alerta is an open-source and easy to install dashboard that allows you to collect and monitor color-coded alerts. + +Parameters for this report module: + +|Parameter| Description | +|---|---| +|severity| The alert severity, e.g., ``critical``. See https://docs.alerta.io/en/latest/api/alert.html#alert-severities for the list of possible values. | +|environment| The Alerta environment name. If not specified, it'll use the BGPalerter environment name. | +|key| Optional, the Alerta API key to use for authenticated requests. | +|token| Optional value used when executing HTTP requests to the Alerta API with bearer authentication. | +|resourceTemplates| A dictionary of string templates for each channels to generate the content of the `resource` field for the alert. If a channel doesn't have a template defined, the `default` template will be used (see `config.yml.example` for more details). Read [here](context.md) how to write a template.| +|urls| A dictionary containing Alerta API URLs grouped by user group (key: group, value: API URL). | +|urls.default| The Alerta API URL of the default user group. | + +> If you receive a 403 error in the BGPalerter error logs, try to check if you correctly set the ALLOWED_ENVIRONMENTS in /etc/alertad.conf. +> In particular set ALLOWED_ENVIRONMENTS=['Production','Development']. + +## reportWebex + +This report module sends alerts on [Webex Teams](https://teams.webex.com). + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|hooks| A dictionary containing Webex Teams WebHooks grouped by user group (key: group, value: WebHook).| +|hooks.default| The WebHook (URL) of the default user group.| + +## reportHTTP + +This report module sends alerts on a generic HTTP end-point. + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|hooks| A dictionary containing API URLs grouped by user group (key: group, value: URL).| +|hooks.default| The URL of the default user group.| +|templates| A dictionary containing string templates for each channels. If a channel doesn't have a template defined, the `default` template will be used (see `config.yml.example` for more details). Read [here](context.md) how to write a template. | +|isTemplateJSON| A boolean defining if the template provided above are JSON or plain string | +|headers| Additional headers to use in the GET request. For example for authentication.| +|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | + +[See here some examples of how to adapt reportHTTP to some common applications.](report-http.md) + +## reportTelegram + +This report module sends alerts directly to specified Telegram users, groups, or channels. +To send alert to Telegram you need to create a bot. + +To create a bot: +1. Open Telegram, search `@botfather` and open a chat with it. +2. Type `/newbot` and follow the procedure to create a bot. +3. Take note of the bot ID provided. +4. Open the chat (channel, group, user) where you want to send the alerts. +5. Write something in the chat (from whatever user). +6. Visit `https://api.telegram.org/bot_BOT_ID_/getUpdates` (replace `_BOT_ID_` with your bot ID) from your browser and take note of the chat ID returned in the answer. In case of multiple chat IDs, use the one with the same text you sent at the previous point. + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|showPaths| Amount of AS_PATHs to report in the alert (0 to disable). | +|botUrl| The Telegram bot URL. Usually `https://api.telegram.org/bot_BOT_ID_/` where `_BOT_ID_` is your both ID. | +|chatIds| A dictionary containing chat IDs grouped by user group (key: group, value: chat ID).| +|chatIds.default| The chat ID of the default user group.| + +## reportPullAPI + +This report module creates a REST API reachable at `http://host:port/alerts/`. The API provides the list of generated alerts and some metadata (including the timestamp of the last time the API was queried). + +The REST API uses the generic `rest` configuration in `config.yml`. Read [here](configuration.md) or see `config.yml.example` for more information. + + +Parameters for this report module: + +|Parameter| Description| +|---|---| +|maxAlertsAmount| The maximum amount of alerts the API will return. By default set to 100. Don't exagerate with the number, the greater this value is the more memory BGPalerter will use. | diff --git a/docs/research.md b/docs/research.md new file mode 100644 index 00000000..74979f5c --- /dev/null +++ b/docs/research.md @@ -0,0 +1,114 @@ +# BGPalerter for researchers + +> This is a draft, this tutorial will improve soon + +BGPalerter has been designed in order to be suitable also for research activities. +While for production purposes it's usually enough to monitor specific prefixes, for research purposes you might need to monitor the entire address space. +In particular, BGPalerter is designed to be able to handle many more BGP messages than the current RIS live streaming produces in total, with a small CPU and memory footprint. + +This tutorial will briefly explain how to use BGPalerter for research. + +## Environment: research + +In `config.yml` you can set the parameter `environment` to the value `research`. +This will disable some of the rules enforced for production monitoring. + +In particular, you will be able to define (if needed) a rule in `prefixes.yml` such us: + +```yaml +"::/0": + description: Monitoring the entire v6 space! + ignoreMorespecifics: false + ignore: false +``` + +If during your analysis you will find a warning of messages dropped in the logs, you may want to: + +1) Check your code to verify if something is taking too much time for the processing of a single BGP message +2) Set a higher value for `maxMessagesPerSecond` (depending on the CPU resources available). Something like 10000 is a good start. +3) Set `multiProcess` to true, in order to use two processes (this is rarely required). + +If the memory consumption during your analysis increases drastically, you may want to: +1) Check your code for memory leaks +2) Check you are not doing many async calls accumulating in the stack E.g., if you monitor the entire v6 address space, like on the example above, you cannot do a single network call for each BGP message received. You can instead bundle together multiple calls or implement a better `filter` function. +3) Check that the `squashAlerts` of your monitor component is working as expected. In particular, if the squashAlerts methods returns null it means the bucket of BGP messages is not yet ready to be squashed, hence it will remain in memory. See below for more information. +4) Reduce the `fadeOffSeconds`. This will drop all the BGP messages that took too long to be squashed by `squashAlerts`. + + +## Implementing a monitor + +The analysis of the BGP messages happens in the monitor components. +So in 99% of the cases you need to create a monitor and leave as is the rest of the pipeline. +You can start by copying one of the available monitors and adapting it for your needs. + +Below an example of monitor component with comments in the code. + +```javascript +import Monitor from "./monitor"; + +export default class myMonitor extends Monitor { // It MUST extend Monitor + + constructor(name, channel, params, env){ + super(name, channel, params, env); + this.count = 0; // You can set here your instance variables + }; + + updateMonitoredResources = () => { + /* This function allows you to set what you are going to + * monitor and update the set every time the input changes */ + this.monitored = this.input.getMonitoredMoreSpecifics(); + }; + + filter = (message) => { + /* Pre-filtering. This filtering is blocking since it happens synchronously. + * Make this filtering as tight as possible without involving external resources + * (e.g., do NOT do database or API calls here). For example base your filtering + * on the properties of the BGP message received */ + return message.type === 'announcement'; + }; + + squashAlerts = (alerts) => { + /* The input 'alerts' is an array of alerts with the same signature generated by the monitor method. + * Alerts with the same signature are usually referring to the same issue (maybe as seen by different peers). + * The expected output is a string. Here you can define what is the summary for the entire "chunk" of alerts. + * If you return null, the alerts will not be sent to the report but will remain in the queue. The next + * squash attempt is going to be when another alert with the same signature is received. + * With this method you can collect alerts and decide when it's time to send them. */ + + if (alerts.length > 5) { // Useless condition just to explain the concept (e.g., you could instead check how many different peers saw the issue before to report it) + return "summary of the alerts"; + } + + return null; + }; + + monitor = (message) => + new Promise((resolve, reject) => { + /* This method is non blocking since it happens asynchronously. + * Here you can do database or API calls (maybe bundle multiple requests together to reduce network overhead). + * This is where the real analysis happens and when the alerts are generated. Place here your complex filtering/analysis. + * The 'filter' function described before is needed to avoid useless calls to the 'monitor' function, which is much more expensive in terms of memory. */ + + const matchedRule = this.getMoreSpecificMatch(message.prefix); //The method getMoreSpecificMatch is inherited from the super class, it provides the rule in prefixes.yml that matches the current BGP message. + + if (matchedRule) { // We matched something in prefixes.yml + const signature = message.originAS.getId() + "-" + message.prefix; // All messages with the same origin AS and prefix will be bundled together. Read above the squash method to understand why. + + this.publishAlert(signature, // The method publishAlert is inherited from the super class. + message.prefix, // The monitored resource subject of the alert (it can be an AS or a prefix) + matchedRule, // The monitored rule that was matched (from prefixes.yml) + message, // The entire BGP message (needed for possible further troubleshooting or for storing it) + { + love: "pizza" // Extra information I want to annotate this alert with (this information will be shared with the squash method and all the reports) + }); + } + + resolve(true); // Remember to resolve the Promise when the calculation is completed! + }); +} +``` + +Useful for research are the following pre-made monitors: +* monitorPath - will detect specific conditions on AS paths +* monitorPassthrough - will let everything pass to the report component. This is useful if you want to use BGPalerter as a data connector and you want to pass the data to another application (e.g., you can send them to Kafka with `reportKafka` or you can create a report wit a `console.log` to pipe everything into standard output) +* monitorRPKI - will detect RPKI invalid announcements. I use this on the entire v4 and v6 address space. It can be used as an example of "complex" monitoring. \ No newline at end of file diff --git a/docs/ris-disconnections.md b/docs/ris-disconnections.md index aba5cf68..d3380a0d 100644 --- a/docs/ris-disconnections.md +++ b/docs/ris-disconnections.md @@ -5,7 +5,8 @@ Sometimes among the error logs you will find RIS disconnection logs (usually err The following causes are possible: 1) **Network issues.** The machine where BGPalerter is running loses connectivity (maybe just for a few seconds). -2) **You are monitoring something that produces too many BGP updates** (e.g. your prefixes are not stable or constantly re-announced). In such cases you may be too slow in consuming the data and the server disconnects you to flush the buffer. +2) **You are monitoring something that produces too many BGP updates** (e.g., your prefixes are not stable or constantly re-announced). In such cases you may be too slow in consuming the data and the server disconnects you to flush the buffer. +3) **Process termination.** This happens when BGPalerter was killed or crashed for some reason, this is not related to RIPE RIS. Anyway, unfortunately sometimes this happens without an explanation due to RIPE RIS instabilities. This has been reported to the RIPE RIS team. diff --git a/docs/rpki.md b/docs/rpki.md new file mode 100644 index 00000000..8d68ca9b --- /dev/null +++ b/docs/rpki.md @@ -0,0 +1,132 @@ +# RPKI configuration + +The RPKI validation performed by BGPalerter can be configured in `config.yml` in the `rpki` section. + +```yaml +rpki: + vrpProvider: ntt + preCacheROAs: true, + refreshVrpListMinutes: 15 +``` + +This configuration will be used across the entire process (e.g., by `monitorRPKI`, `monitorHijack`, `monitorROAs`). + +Below you can see the parameters available: + +|Parameter| Description| +|---|---| +|preCacheROAs| When this parameter is set to true (default), BGPalerter will download Validated ROA Payloads (VRPs) lists locally instead of using online validation. More info [here](https://github.com/massimocandela/rpki-validator).| +|refreshVrpListMinutes| If `preCacheROAs` is set to true, this parameter allows to specify a refresh time for the VRPs lists (read [here](https://github.com/massimocandela/rpki-validator#rpki-auto-refresh-limits) for the minimum refresh time allowed). | +|vrpProvider| A string indicating the provider of the VRPs list. Possible options are: `ntt` (default), `cloudflare`, `rpkiclient`, `ripe`, `external`, `api`. The `external` and `api` options are used to specify your own VRP source, read here.| +|vrpFile| A JSON file with an array of VRPs. See example below.| +|markDataAsStaleAfterMinutes| The amount of minutes (integer) after which an unchanged VRP list is marked as stale. Set to 0 to disable the check. | + + +## Use your own VRPs +Using external VRP providers for the monitoring is quick and easy, but you are essentially trusting somebody else writing the VRP file correctly. + +Instead, you can specify your own VRPs in two ways: + +* Using your own API producing JSON output; +* Using your favourite rpki validator to generate a file locally. + +> In case the download of the VRP data fails, an online provider is used (the error is reported in the logs). + +### Use your own API +To use your own API you need to set the following options in config.yml: + +```yaml +rpki: + vrpProvider: api + url: https://my-api.api.com/vrps/ + preCacheROAs: true +``` + +> Remember, you must specify the url when you use "api" as vrpProvider + +The API must return the JSON format described [here](https://github.com/massimocandela/rpki-validator#vrps-on-custom-api); + +### Use your own VRP file + +You can generate your JSON VRP file periodically and BGPalerter will detect changes and reload it automatically. +To do so, you have to use the following options in config.yml: + +```yaml +rpki: + vrpProvider: external + vrpFile: myfile.json + preCacheROAs: true +``` + +> Remember, you must specify vrpFile when you use "external" as vrpProvider + + +The VRPs file must be in the following format: +```json5 +[ + { + "prefix": "123.4.5.0/22", + "asn": 1234, + "maxLength": 24 + }, + { + "prefix": "321.4.5.0/22", + "asn": 9876, + "maxLength": 22 + } +] +``` + +Also the following format is supported: +```json5 +{ + roas: [ ... ] // containing items as described above +} +``` + +You can use any of the RPKI validator that support JSON as output format to generate it. Below some copy-paste examples. + + +#### rpki-client + +* Download rpki-client [here](https://www.rpki-client.org/); + +* Create a cron job every 15 minutes with the following + * `rpki-client -j test/` + +* Set the `vrpFile` parameter in `config.yml` + ```yaml + rpki: + vrpFile: test/export.json + preCacheROAs: true + ``` + +#### Routinator + +* Download Routinator [here](https://github.com/NLnetLabs/routinator) + +* Run the Routinator [daemon](https://rpki.readthedocs.io/en/latest/routinator/daemon.html) with the HTTP service + * `routinator server --http 127.0.0.1:8323` + +* Set the `vrpProvider` parameter in `config.yml` + ```yaml + vrpProvider: api + url: http://127.0.0.1:8323/json + preCacheROAs: true + ``` + +> Please, help with other examples + + +### Staging/testing ROAs +You can use BGPalerter to test ROAs before deploying them for real. + +How: +- enable [connectorRISDump](configuration.md#connectorrisdump) (optional but useful); +- add the "ROA" in the VRP file using the JSON format described above; +- be sure there is a prefix rule in config.yml covering the prefix of the ROA; +- leave BGPalerter on for some time. + +You will get notified if your new staged roa conflicts with what announced at the BGP level. +If you are starting BGPalerter after you already created the VRP file, by enabling connectorRISDump you would be able to get an immediate feedback based on a BGP dump. In any case, new BGP updates are going to be processed in real-time and compared with the VRP file provided. + diff --git a/docs/update.md b/docs/update.md new file mode 100644 index 00000000..7f7f2a78 --- /dev/null +++ b/docs/update.md @@ -0,0 +1,8 @@ +# Updating BGPalerter + +BGPalerter can be easily updated. The configuration of BGPalerter is persisted in `config.yml` and `prefixes.yml`, as long as you preserve such files you will not need to reconfigure it after the update. + +* If you are using the binary, go in the [releases tab](https://github.com/nttgin/BGPalerter/releases), download the new binary and replace the old one. +* If you are using the source code, simply do a git pull of the main branch. After, do `npm install` to update the dependencies. +* If you are using docker, do `docker pull nttgin/bgpalerter:latest`, after stop and remove your current container and [run it again](installation.md#running-bgpalerter-in-docker). +* You can set [automatic updates](linux-service.md#automatic-updates). \ No newline at end of file diff --git a/docs/usergroups.md b/docs/usergroups.md new file mode 100644 index 00000000..de86b54a --- /dev/null +++ b/docs/usergroups.md @@ -0,0 +1,159 @@ +# User Groups + +BGPalerter supports user groups. With user groups you can: +* Notify only specific users about specific prefixes. +* Notify only specific users about specific type of alerts. + +By default, BGPalerter creates two user groups `noc` and `default` (since v1.27.0). +* The `noc` user group receives only alerts related to the BGP monitoring. Even if set by default, this user group is optional. +* The `default` user group receives administrative and error communications. Additionally, it receives all the alerts that could not be dispatched to any other specific user group. This group is mandatory, and it MUST be set for all the report modules. + + +You can create how many user groups you wish, for example to monitor resources of your customers and forward them the alerts about their resources without sending them administrative communications. + +User groups can be specified directly in the report configuration on in an external yaml file. +Using an external file allows BGPalerter to auto-reload the user group definitions when the external file is changed. + +## Notify only specific users about specific prefixes + +Example of configuration. + +In prefixes.yml you can associate different groups to different resources. + +```yml +165.254.225.0/24: + description: my description 1 + asn: 2914 + ignoreMorespecifics: false + ignore: false + group: group1 + +165.254.255.0/24: + description: my description 2 + asn: 2914 + ignoreMorespecifics: false + ignore: false + group: group1 + +192.147.168.0/24: + description: my description 3 + asn: 15562 + ignoreMorespecifics: false + ignore: false + group: group2 + + +options: + monitorASns: + 2914: + group: group1 + 15562: + group: group2 +``` + + +In config.yml you have to specify the groups in the report modules. + +```yml +reports: + - file: reportEmail + channels: + - hijack + - newprefix + params: + notifiedEmails: + default: + - admin@org.com + group1: + - joh@example.com + - max@example.com + group2: + - al@org.net + + - file: reportSlack + channels: + - hijack + - newprefix + params: + hooks: + default: _SLACK_WEBOOK_FOR_ADMIN_ + group1: _SLACK_WEBOOK_FOR_GROUP1_ + group2: _SLACK_WEBOOK_FOR_GROUP2_ +``` + + +## Notify only specific users about specific type of alerts + +It's essentially the same configuration of above, except you have to duplicate report components, each serving a subset of the channels. + +```yml + - file: reportSlack + channels: + - hijack + params: + hooks: + default: _SLACK_WEBOOK_FOR_ADMIN_ + group1: _SLACK_WEBOOK_FOR_GROUP2_ + + - file: reportSlack + channels: + - newprefix + params: + hooks: + default: _SLACK_WEBOOK_FOR_ADMIN_ + group2: _SLACK_WEBOOK_FOR_GROUP1_ +``` + +You can also split the notification across different reporting mechanism based on their type. + +```yml +reports: + - file: reportEmail + channels: + - newprefix + params: + notifiedEmails: + default: + - admin@org.com + group1: + - joh@example.com + - max@example.com + + - file: reportSlack + channels: + - hijack + params: + hooks: + default: _SLACK_WEBOOK_FOR_ADMIN_ + group2: _SLACK_WEBOOK_FOR_GROUP2_ +``` + +> User groups associated to prefixes have always precedence on user groups associated to ASes. If an alert matches both a prefix rule and an AS rule, the prefix rule will define which user group will receive the alert. + +## Define an external user groups file + +Edit `config.yml`, uncomment the `groupsFile` option, and add the position of the file (e.g., `groupsFile: groups.yml`). + +Create the user groups file as follows: + +```yaml +report_module_name1: + user_group_to_define: + list_of_contacts + +report_module_name2: + user_group_to_define: + list_of_contacts +``` +The format of the list of contacts depends on the report_module (e.g., emails for reportEmail, urls for reportHTTP). + +For example, for reportEmail: + +```yaml +reportEmail: + myGroup: + example@example.it +``` +In the repo there is a `config.yaml.example` file that you can use. + +It the file is changed, BGPalerter will auto-reload the user groups. diff --git a/groups.yml.example b/groups.yml.example new file mode 100644 index 00000000..7d5d3e4b --- /dev/null +++ b/groups.yml.example @@ -0,0 +1,12 @@ +# This file is an example of how you can define user groups + +# The structure is: +# report_module_name: +# user_group_to_define: +# list_of_contacts + +# The format of the list of contacts depends on the report_module (e.g., emails for reportEmail, urls for reportHTTP) + +reportEmail: + mygroup: + - example@example.it \ No newline at end of file diff --git a/index.js b/index.js index 42a62dd9..fe3a625d 100644 --- a/index.js +++ b/index.js @@ -31,15 +31,38 @@ */ import yargs from 'yargs'; +import fs from "fs"; +import yaml from "js-yaml"; const params = yargs .usage('Usage: $0 [options]') .command('$0', 'Run BGPalerter (default)', function () { + yargs + .alias('v', 'version') + .nargs('v', 0) + .describe('v', 'Show version number') + + .alias('c', 'config') + .nargs('c', 1) + .describe('c', 'Config file to load') + + .alias('t', 'test') + .nargs('t', 0) + .describe('t', 'Test the configuration with fake BGP updates') + + .alias('d', 'data-volume') + .nargs('d', 1) + .describe('d', 'A directory where configuration and data is persisted') }) - .example('$0 run -c config.yml', 'Run BGPalerter') + .command('generate', 'Generate prefixes to monitor', function () { - yargs.alias('o', 'output') + yargs + .alias('v', 'version') + .nargs('v', 0) + .describe('v', 'Show version number') + + .alias('o', 'output') .nargs('o', 1) .describe('o', 'Write to file') @@ -49,15 +72,15 @@ const params = yargs .alias('e', 'exclude') .nargs('e', 1) - .describe('e', 'Prefixes to exclude') + .describe('e', 'Comma-separated list of prefixes to exclude') .alias('p', 'prefixes') .nargs('p', 1) - .describe('p', 'Prefixes to include') + .describe('p', 'Comma-separated list of prefixes to include') .alias('l', 'prefixes-file') .nargs('l', 1) - .describe('l', 'File containing the prefixes to include') + .describe('l', 'File containing the prefixes to include in the monitoring. One prefix for each line') .alias('i', 'ignore-delegated') .nargs('i', 0) @@ -71,6 +94,34 @@ const params = yargs .nargs('m', 0) .describe('m', 'Automatically generate list of monitored ASes (options.monitorASns) from prefix origins.') + .alias('x', 'proxy') + .nargs('x', 1) + .describe('x', 'HTTP/HTTPS proxy to use') + + .alias('g', 'group') + .nargs('g', 1) + .describe('x', 'Define a user group for all the generated rules.') + + .alias('A', 'append') + .nargs('A', 0) + .describe('A', 'Append the new configuration to the previous one.') + + .alias('D', 'debug') + .nargs('D', 0) + .describe('D', 'Provide verbose output for debugging') + + .alias('H', 'historical') + .nargs('H', 0) + .describe('H', 'Use historical visibility data for generating prefix list (prefixes visible in the last week).') + + .alias('u', 'upstreams') + .nargs('u', 0) + .describe('u', 'Detect a list of allowed upstream ASes and enable detection of new left-side ASes') + + .alias('n', 'downstreams') + .nargs('n', 0) + .describe('n', 'Detect a list of allowed downstream ASes and enable detection of new right-side ASes.') + .demandOption(['o']); }) .example('$0 generate -a 2914 -o prefixes.yml', 'Generate prefixes for AS2914') @@ -82,6 +133,8 @@ const params = yargs switch(params._[0]) { case "generate": const generatePrefixes = require("./src/generatePrefixesList"); + const debug = !!params.D; + const historical = !!params.H; let prefixes = null; let monitoredASes = false; if (params.pf) { @@ -108,18 +161,45 @@ switch(params._[0]) { monitoredASes = true; } - generatePrefixes( - (params.a) ? params.a.toString().split(",") : null, - params.o, - (params.e || "").split(","), - params.i || false, + const inputParameters = { + asnList: (params.a) ? params.a.toString().split(",") : null, + outputFile: params.o, + exclude: (params.e) ? params.e.toString().split(",") : null, + excludeDelegated: params.i || false, prefixes, - monitoredASes - ); + monitoredASes, + httpProxy: params.x || null, + debug, + historical, + group: params.g || null, + append: !!params.A, + logger: null, + upstreams: !!params.u, + downstreams: !!params.n, + getCurrentPrefixesList: () => { + return Promise.resolve(yaml.load(fs.readFileSync(params.o, "utf8"))); + } + }; + + if (!inputParameters.outputFile) { + throw new Error("Output file not specified"); + } + + generatePrefixes(inputParameters) + .then(content => { + fs.writeFileSync(params.o, yaml.dump(content)); + process.exit(0); + }); break; default: // Run monitor + global.DRY_RUN = !!params.t; + if (global.DRY_RUN) console.log("Testing BGPalerter configuration. WARNING: remove -t option for production monitoring."); const Worker = require("./src/worker").default; - module.exports = new Worker(params.c); + module.exports = new Worker({ + configFile: params.c, + volume: params.d, + groupFile: params.E + }); } diff --git a/package-lock.json b/package-lock.json index 7567ef8d..e068305d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1002 +1,869 @@ { "name": "bgpalerter", - "version": "1.24.0", + "version": "1.28.4", "lockfileVersion": 1, "requires": true, "dependencies": { "@babel/cli": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.8.4.tgz", - "integrity": "sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.15.7.tgz", + "integrity": "sha512-YW5wOprO2LzMjoWZ5ZG6jfbY9JnkDxuHDwvnrThnuYtByorova/I0HNXJedrUfwuXFQfYOjcqDA4PU3qlZGZjg==", "dev": true, "requires": { - "chokidar": "^2.1.8", + "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3", + "chokidar": "^3.4.0", "commander": "^4.0.1", "convert-source-map": "^1.1.0", "fs-readdir-recursive": "^1.1.0", "glob": "^7.0.0", - "lodash": "^4.17.13", "make-dir": "^2.1.0", "slash": "^2.0.0", "source-map": "^0.5.0" }, "dependencies": { - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "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" + } } } }, "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.15.8.tgz", + "integrity": "sha512-2IAnmn8zbvC/jKYhq5Ki9I+DwjlrtMPUCH/CpHvqI4dNnlwHwsxoIhlc8WcYY5LSYknXQtAlFYuHfqAFCvQ4Wg==", "dev": true, "requires": { - "@babel/highlight": "^7.8.3" + "@babel/highlight": "^7.14.5" } }, "@babel/compat-data": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.8.6.tgz", - "integrity": "sha512-CurCIKPTkS25Mb8mz267vU95vy+TyUpnctEX2lV33xWNmHAfjruztgiPBbXZRh3xZZy1CYvGx6XfxyTVS+sk7Q==", - "dev": true, - "requires": { - "browserslist": "^4.8.5", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.15.0.tgz", + "integrity": "sha512-0NqAC1IJE0S0+lL1SWFMxMkz1pKCNCjI4tr2Zx4LJSXxCLAdr6KyArnY+sno5m3yH9g737ygOyPABDsnXkpxiA==", + "dev": true }, "@babel/core": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.7.tgz", - "integrity": "sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.7", - "@babel/helpers": "^7.8.4", - "@babel/parser": "^7.8.7", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.7", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.15.8.tgz", + "integrity": "sha512-3UG9dsxvYBMYwRv+gS41WKHno4K60/9GPy1CJaH6xy3Elq8CTtvtjT5R5jmNhXfCYLX2mTw+7/aq5ak/gOE0og==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.15.8", + "@babel/generator": "^7.15.8", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.8", + "@babel/helpers": "^7.15.4", + "@babel/parser": "^7.15.8", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6", "convert-source-map": "^1.7.0", "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.0", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", + "gensync": "^1.0.0-beta.2", + "json5": "^2.1.2", + "semver": "^6.3.0", "source-map": "^0.5.0" }, "dependencies": { - "@babel/generator": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", - "integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", - "dev": true, - "requires": { - "@babel/types": "^7.8.7", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" - } - }, - "@babel/parser": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", - "integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", - "dev": true - }, - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, "@babel/types": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", - "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/generator": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.6.tgz", - "integrity": "sha512-4bpOR5ZBz+wWcMeVtcf7FbjcFzCp+817z2/gHNncIRcM9MmKzUhtWCYAq27RAfUrAFwb+OCG1s9WEaVxfi6cjg==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.15.8.tgz", + "integrity": "sha512-ECmAKstXbp1cvpTTZciZCgfOt6iN64lR0d+euv3UZisU5awfRawOvg07Utn/qBGuH4bRIEZKrA/4LzZyXhZr8g==", "dev": true, "requires": { - "@babel/types": "^7.8.6", + "@babel/types": "^7.15.6", "jsesc": "^2.5.1", - "lodash": "^4.17.13", "source-map": "^0.5.0" }, "dependencies": { "@babel/types": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz", - "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true } } }, "@babel/helper-annotate-as-pure": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz", - "integrity": "sha512-6o+mJrZBxOoEX77Ezv9zwW7WV8DdluouRKNY/IR5u/YTMuKHgugHOzYWlYvYLpLA9nPsQCAAASpCIbjI9Mv+Uw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.15.4.tgz", + "integrity": "sha512-QwrtdNvUNsPCj2lfNQacsGSQvGX8ee1ttrBrcozUP2Sv/jylewBP/8QFe6ZkBsC8T/GYWonNAWJV4aRR9AL2DA==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.8.3.tgz", - "integrity": "sha512-5eFOm2SyFPK4Rh3XMMRDjN7lBH0orh3ss0g3rTYZnBQ+r6YPj7lgDyCvPphynHvUrobJmeMignBr6Acw9mAPlw==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-call-delegate": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.8.7.tgz", - "integrity": "sha512-doAA5LAKhsFCR0LAFIf+r2RSMmC+m8f/oQ+URnUET/rWeEzC0yTRmAGyWkD4sSu3xwbS7MYQ2u+xlt1V5R56KQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.14.5.tgz", + "integrity": "sha512-YTA/Twn0vBXDVGJuAX6PwW7x5zQei1luDDo2Pl6q1qZ7hVNl0RZrhHCQG/ArGpR29Vl7ETiB8eJyrvpuRp300w==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.7" + "@babel/helper-explode-assignable-expression": "^7.14.5", + "@babel/types": "^7.14.5" }, "dependencies": { "@babel/types": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", - "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", + "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } } } }, "@babel/helper-compilation-targets": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.8.7.tgz", - "integrity": "sha512-4mWm8DCK2LugIS+p1yArqvG1Pf162upsIsjE7cNBjez+NjliQpVhj20obE520nao0o14DaTnFJv+Fw5a0JpoUw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.15.4.tgz", + "integrity": "sha512-rMWPCirulnPSe4d+gwdWXLfAXTTBj8M3guAf5xFQJ0nvFY7tfNAFnWdqaHegHlgDZOCT4qvhF3BYlSJag8yhqQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.8.6", - "browserslist": "^4.9.1", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" + "@babel/compat-data": "^7.15.0", + "@babel/helper-validator-option": "^7.14.5", + "browserslist": "^4.16.6", + "semver": "^6.3.0" }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, "@babel/helper-create-class-features-plugin": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.8.3.tgz", - "integrity": "sha512-qmp4pD7zeTxsv0JNecSBsEmG1ei2MqwJq4YQcK3ZWm/0t07QstWfvuV/vm3Qt5xNMFETn2SZqpMx2MQzbtq+KA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.15.4.tgz", + "integrity": "sha512-7ZmzFi+DwJx6A7mHRwbuucEYpyBwmh2Ca0RvI6z2+WLZYCqV0JOaLb+u0zbtmDicebgKBZgqbYfLaKNqSgv5Pw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.5.tgz", + "integrity": "sha512-TLawwqpOErY2HhWbGJ2nZT5wSkR192QpN+nBg1THfBfftrlvOh+WbhrxXCH4q4xJ9Gl16BGPR/48JA+Ryiho/A==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3" + "@babel/helper-annotate-as-pure": "^7.14.5", + "regexpu-core": "^4.7.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.3.tgz", + "integrity": "sha512-RH3QDAfRMzj7+0Nqu5oqgO5q9mFtQEVvCRsi8qCEfzLR9p2BHfn5FzhSB2oj1fF7I2+DcTORkYaQ6aTR9Cofew==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.13.0", + "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/traverse": "^7.13.0", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" }, "dependencies": { - "@babel/code-frame": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.8.3.tgz", - "integrity": "sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.8.3" - } - }, - "@babel/generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.3.tgz", - "integrity": "sha512-WjoPk8hRpDRqqzRpvaR8/gDUPkrnOOeuT2m8cNICJtZH6mwaCo3v0OKMI7Y6SM1pBtyijnLtAL0HDi41pf41ug==", + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.14.5.tgz", + "integrity": "sha512-Htb24gnGJdIGT4vnRKMdoXiOIlqOLmdiUYpAQ0mYfgVT/GDm8GOYhgi4GL+hMKrkiPRohO4ts34ELFsGAPQLDQ==", + "dev": true, + "requires": { + "@babel/types": "^7.14.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.0.tgz", + "integrity": "sha512-OBvfqnllOIdX4ojTHpwZbpvz4j3EWyjkZEdmjH0/cgsd6QOdSgU8rLSk6ard/pcW7rlmjdVSX/AWOaORR1uNOQ==", "dev": true, "requires": { - "@babel/types": "^7.8.3", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + } + } + }, + "@babel/helper-function-name": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.15.4.tgz", + "integrity": "sha512-Z91cOMM4DseLIGOnog+Z8OI6YseR9bua+HpvLAQ2XayUGU+neTtX+97caALaLdyu53I/fjhbeCnWnRH1O3jFOw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + } + } + }, + "@babel/helper-get-function-arity": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.15.4.tgz", + "integrity": "sha512-1/AlxSF92CmGZzHnC515hm4SirTxtpDnLEJ0UyEMgTMZN+6bxXKg04dKhiRx5Enel+SUA1G1t5Ed/yQia0efrA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + } + } + }, + "@babel/helper-hoist-variables": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.15.4.tgz", + "integrity": "sha512-VTy085egb3jUGVK9ycIxQiPbquesq0HUQ+tPO0uv5mPEBZipk+5FkRKiWq5apuyTE9FUrjENB0rCf8y+n+UuhA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + } + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.15.4.tgz", + "integrity": "sha512-cokOMkxC/BTyNP1AlY25HuBWM32iCEsLPI4BHDpJCHHm1FU2E7dKWWIXJgQgSFiu4lp8q3bL1BIKwqkSUviqtA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.3.tgz", - "integrity": "sha512-xOUssL6ho41U81etpLoT2RTdvdus4VfHamCuAm4AHxGr+0it5fnwoVdwUJ7GFEqCsQYzJUhcbsN9wB9apcYKFA==", + } + } + }, + "@babel/helper-module-imports": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.15.4.tgz", + "integrity": "sha512-jeAHZbzUwdW/xHgHQ3QmWR4Jg6j15q4w/gCfwZvtqOxoo5DKtLHk8Bsf4c5RZRC7NmLEs+ohkdq8jFefuvIxAA==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + } + } + }, + "@babel/helper-module-transforms": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.15.8.tgz", + "integrity": "sha512-DfAfA6PfpG8t4S6npwzLvTUpp0sS7JrcuaMiy1Y5645laRJIp/LiLGIBbQKaXSInK8tiGNI7FL7L8UvB8gdUZg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.15.4", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-simple-access": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/helper-validator-identifier": "^7.15.7", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.6" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.15.4.tgz", + "integrity": "sha512-E/z9rfbAOt1vDW1DR7k4SzhzotVV5+qMciWV6LaG1g4jeFrkDlJedjtV4h0i4Q/ITnUu+Pk08M7fczsB9GXBDw==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/parser": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.3.tgz", - "integrity": "sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ==", - "dev": true - }, - "@babel/template": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", - "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + } + } + }, + "@babel/helper-plugin-utils": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz", + "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.15.4.tgz", + "integrity": "sha512-v53MxgvMK/HCwckJ1bZrq6dNKlmwlyRNYM6ypaRTdXWGOE2c1/SCa6dL/HimhPulGhZKw9W0QhREM583F/t0vQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-wrap-function": "^7.15.4", + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "@babel/traverse": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.3.tgz", - "integrity": "sha512-we+a2lti+eEImHmEXp7bM9cTxGzxPmBiVJlLVD+FuuQMeeO7RaDbutbgeheDkw+Xe3mCfJHnGOWLswT74m2IPg==", + } + } + }, + "@babel/helper-replace-supers": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.15.4.tgz", + "integrity": "sha512-/ztT6khaXF37MS47fufrKvIsiQkx1LBRvSJNzRqmbyeZnTwU9qBxXYLaaT/6KaxfKhjs2Wy8kG8ZdsFUuWBjzw==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.3", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/types": "^7.8.3", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, + } + } + }, + "@babel/helper-simple-access": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.15.4.tgz", + "integrity": "sha512-UzazrDoIVOZZcTeHHEPYrr1MvTR/K+wgLg6MY6e1CJyaRhbibftF6fR2KU2sFRtI/nERUZR9fBd6aKgBlIBaPg==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { "@babel/types": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", - "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + } + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.15.4.tgz", + "integrity": "sha512-BMRLsdh+D1/aap19TycS4eD1qELGrCBJwzaY9IE8LrpJtJb+H7rQkPIdsfgnMtLBA6DJls7X9z93Z4U8h7xw0A==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + } + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.15.4.tgz", + "integrity": "sha512-HsFqhLDZ08DxCpBdEVtKmywj6PQbwnF6HHybur0MAnkAKnlS6uHkwnmRIkElB2Owpfb4xL4NwDmDLFubueDXsw==", + "dev": true, + "requires": { + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + } + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz", + "integrity": "sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.15.4.tgz", + "integrity": "sha512-Y2o+H/hRV5W8QhIfTpRIBwl57y8PrZt6JM3V8FOo5qarjshHItyH5lXlpMfBfmBefOqSCpKZs/6Dxqp0E/U+uw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.15.4", + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "ms": "^2.1.1" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + } + } + }, + "@babel/helpers": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.15.4.tgz", + "integrity": "sha512-V45u6dqEJ3w2rlryYYXf6i9rQ5YMNu4FLS6ngs8ikblhu2VdR1AqAd6aJjBzmf2Qzh6KOLqKHxEN9+TFbAkAVQ==", + "dev": true, + "requires": { + "@babel/template": "^7.15.4", + "@babel/traverse": "^7.15.4", + "@babel/types": "^7.15.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" } } } }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.6.tgz", - "integrity": "sha512-bPyujWfsHhV/ztUkwGHz/RPV1T1TDEsSZDsN42JPehndA+p1KKTh3npvTadux0ZhCrytx9tvjpWNowKby3tM6A==", + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-regex": "^7.8.3", - "regexpu-core": "^4.6.0" + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" } }, - "@babel/helper-define-map": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.8.3.tgz", - "integrity": "sha512-PoeBYtxoZGtct3md6xZOCWPcKuMuk3IHhgxsRRNtnNShebf4C8YonTSblsK4tvDbm+eJAw2HAPOfCr+Q/YRG/g==", + "@babel/node": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.15.8.tgz", + "integrity": "sha512-JbgTCrnx6IUECznEbQ7e2rbVYtrXKixSbEG2bxxbMkV648WdUf3tdc+ZUW2++dvnnAswcpwc76cRwf0xEQqsrw==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/types": "^7.8.3", - "lodash": "^4.17.13" + "@babel/register": "^7.15.3", + "commander": "^4.0.1", + "core-js": "^3.16.0", + "node-environment-flags": "^1.0.5", + "regenerator-runtime": "^0.13.4", + "v8flags": "^3.1.1" } }, - "@babel/helper-explode-assignable-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.8.3.tgz", - "integrity": "sha512-N+8eW86/Kj147bO9G2uclsg5pwfs/fqqY5rwgIL7eTBklgXjcOJ3btzS5iM6AitJcftnY7pm2lGsrJVYLGjzIw==", + "@babel/parser": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.15.8.tgz", + "integrity": "sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==", + "dev": true + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.15.4.tgz", + "integrity": "sha512-eBnpsl9tlhPhpI10kU06JHnrYXwg3+V6CaP2idsCXNef0aeslpqyITXQ74Vfk5uHgY7IG7XP0yIH8b42KSzHog==", "dev": true, "requires": { - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.15.4", + "@babel/plugin-proposal-optional-chaining": "^7.14.5" } }, - "@babel/helper-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz", - "integrity": "sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==", + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.15.8.tgz", + "integrity": "sha512-2Z5F2R2ibINTc63mY7FLqGfEbmofrHU9FitJW1Q7aPaKFhiPvSq6QEt/BoWN5oME3GVyjcRuNNSRbb9LC0CSWA==", "dev": true, "requires": { - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.15.4", + "@babel/plugin-syntax-async-generators": "^7.8.4" } }, - "@babel/helper-get-function-arity": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", - "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "@babel/plugin-proposal-class-properties": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.14.5.tgz", + "integrity": "sha512-q/PLpv5Ko4dVc1LYMpCY7RVAAO4uk55qPwrIuJ5QJ8c6cVuAmhu7I/49JOppXL6gXf7ZHzpRVEUZdYoPLM04Gg==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, - "@babel/helper-hoist-variables": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.8.3.tgz", - "integrity": "sha512-ky1JLOjcDUtSc+xkt0xhYff7Z6ILTAHKmZLHPxAhOP0Nd77O+3nCsd6uSVYur6nJnCI029CrNbYlc0LoPfAPQg==", + "@babel/plugin-proposal-class-static-block": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.15.4.tgz", + "integrity": "sha512-M682XWrrLNk3chXCjoPUQWOyYsB93B9z3mRyjtqqYJWDf2mfCdIYgDrA11cgNVhAQieaq6F2fn2f3wI0U4aTjA==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "@babel/plugin-proposal-dynamic-import": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.14.5.tgz", + "integrity": "sha512-ExjiNYc3HDN5PXJx+bwC50GIx/KKanX2HiggnIUAYedbARdImiCU4RhhHfdf0Kd7JNXGpsBBBCOm+bBVy3Gb0g==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" } }, - "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.5.tgz", + "integrity": "sha512-g5POA32bXPMmSBu5Dx/iZGLGnKmKPc5AiY7qfZgurzrCYgIztDlHFbznSNCoQuv57YQLnQfaDi7dxCtLDIdXdA==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" } }, - "@babel/helper-module-transforms": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.8.6.tgz", - "integrity": "sha512-RDnGJSR5EFBJjG3deY0NiL0K9TO8SXxS9n/MPsbPK/s9LbQymuLNtlzvDiNS7IpecuL45cMeLVkA+HfmlrnkRg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.8.6", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/template": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", - "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6" - } - }, - "@babel/types": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", - "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/plugin-proposal-json-strings": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.14.5.tgz", + "integrity": "sha512-NSq2fczJYKVRIsUJyNxrVUMhB27zb7N7pOFGQOhBKJrChbGcgEAqyZrmZswkPk18VMurEeJAaICbfm57vUeTbQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-json-strings": "^7.8.3" } }, - "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.14.5.tgz", + "integrity": "sha512-YGn2AvZAo9TwyhlLvCCWxD90Xq8xJ4aSgaX3G5D/8DW94L8aaT+dS5cSP+Z06+rCJERGSr9GxMBZ601xoc2taw==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, - "@babel/helper-plugin-utils": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.0.tgz", - "integrity": "sha512-+hAlRGdf8fHQAyNnDBqTHQhwdLURLdrCROoWaEQYiQhk2sV9Rhs+GoFZZfMJExTq9HG8o2NX3uN2G90bFtmFdA==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.8.3.tgz", - "integrity": "sha512-BWt0QtYv/cg/NecOAZMdcn/waj/5P26DR4mVLXfFtDokSR6fyuG0Pj+e2FqtSME+MqED1khnSMulkmGl8qWiUQ==", + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.14.5.tgz", + "integrity": "sha512-gun/SOnMqjSb98Nkaq2rTKMwervfdAoz6NphdY0vTfuzMfryj+tDGb2n6UkDKwez+Y8PZDhE3D143v6Gepp4Hg==", "dev": true, "requires": { - "lodash": "^4.17.13" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" } }, - "@babel/helper-remap-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.8.3.tgz", - "integrity": "sha512-kgwDmw4fCg7AVgS4DukQR/roGp+jP+XluJE5hsRZwxCYGg+Rv9wSGErDWhlI90FODdYfd4xG4AQRiMDjjN0GzA==", + "@babel/plugin-proposal-numeric-separator": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.5.tgz", + "integrity": "sha512-yiclALKe0vyZRZE0pS6RXgjUOt87GWv6FYa5zqj15PvhOGFO69R5DusPlgK/1K5dVnCtegTiWu9UaBSrLLJJBg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-wrap-function": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" } }, - "@babel/helper-replace-supers": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz", - "integrity": "sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==", + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.15.6.tgz", + "integrity": "sha512-qtOHo7A1Vt+O23qEAX+GdBpqaIuD3i9VRrWgCJeq7WO6H2d14EK3q11urj5Te2MAeK97nMiIdRpwd/ST4JFbNg==", "dev": true, "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.8.6", - "@babel/types": "^7.8.6" - }, - "dependencies": { - "@babel/types": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", - "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", - "to-fast-properties": "^2.0.0" - } - } + "@babel/compat-data": "^7.15.0", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.15.4" } }, - "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.14.5.tgz", + "integrity": "sha512-3Oyiixm0ur7bzO5ybNcZFlmVsygSIQgdOa7cTfOYCMY+wEPAYhZAJxi3mixKFCTCKUhQXuCTtQ1MzrpL3WT8ZQ==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" } }, - "@babel/helper-split-export-declaration": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", - "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "@babel/plugin-proposal-optional-chaining": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.14.5.tgz", + "integrity": "sha512-ycz+VOzo2UbWNI1rQXxIuMOzrDdHGrI23fRiz/Si2R4kv2XZQ1BK8ccdHwehMKBlcH/joGW/tzrUmo67gbJHlQ==", "dev": true, "requires": { - "@babel/types": "^7.8.3" + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.14.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, - "@babel/helper-wrap-function": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz", - "integrity": "sha512-LACJrbUET9cQDzb6kG7EeD7+7doC3JNvUgTEQOx2qaO1fKlzE/Bf05qs9w1oXQMmXlPO65lC3Tq9S6gZpTErEQ==", + "@babel/plugin-proposal-private-methods": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.14.5.tgz", + "integrity": "sha512-838DkdUA1u+QTCplatfq4B7+1lnDa/+QMI89x5WZHBcnNv+47N8QEj2k9I2MUU9xIv8XJ4XvPCviM/Dj7Uwt9g==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/helper-create-class-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, - "@babel/helpers": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.8.4.tgz", - "integrity": "sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==", + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.15.4.tgz", + "integrity": "sha512-X0UTixkLf0PCCffxgu5/1RQyGGbgZuKoI+vXP4iSbJSYwPb7hu06omsFGBvQ9lJEvwgrxHdS8B5nbfcd8GyUNA==", "dev": true, "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.8.4", - "@babel/types": "^7.8.3" + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-create-class-features-plugin": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, - "@babel/highlight": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.8.3.tgz", - "integrity": "sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==", + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.14.5.tgz", + "integrity": "sha512-6axIeOU5LnY471KenAB9vI8I5j7NQ2d652hIYwVyRfgaZT5UpiqFKCuVXCDMSrU+3VFafnu2c5m3lrWIlr6A5Q==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "@babel/helper-create-regexp-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, - "@babel/node": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.8.7.tgz", - "integrity": "sha512-o8cBT3cfRPLwoPh7VBYonSeZypIawGUeVfOIt1xSDgcDdirRGDPZ7/x+FLhhgQmKp3PKbz5Juh9/BNP4Jzrr9Q==", + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "requires": { - "@babel/register": "^7.8.3", - "commander": "^4.0.1", - "core-js": "^3.2.1", - "lodash": "^4.17.13", - "node-environment-flags": "^1.0.5", - "regenerator-runtime": "^0.13.4", - "resolve": "^1.13.1", - "v8flags": "^3.1.1" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", - "integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/parser": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz", - "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==", - "dev": true + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz", - "integrity": "sha512-NZ9zLv848JsV3hs8ryEh7Uaz/0KsmPLqv0+PdkDJL1cJy0K4kOCFa8zc1E3mp+RHPQcpdfb/6GovEsW4VDrOMw==", + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-remap-async-to-generator": "^7.8.3", - "@babel/plugin-syntax-async-generators": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, - "@babel/plugin-proposal-class-properties": { + "@babel/plugin-syntax-dynamic-import": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz", - "integrity": "sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-proposal-dynamic-import": { + "@babel/plugin-syntax-export-namespace-from": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.8.3.tgz", - "integrity": "sha512-NyaBbyLFXFLT9FP+zk0kYlUlA8XtCUbehs67F0nnEg7KICgMc2mNkIeu9TYhKzyXMkrapZFwAhXLdnt4IYHy1w==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.8.3" } }, - "@babel/plugin-proposal-json-strings": { + "@babel/plugin-syntax-json-strings": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.8.3.tgz", - "integrity": "sha512-KGhQNZ3TVCQG/MjRbAUwuH+14y9q0tpxs1nWWs3pbSleRdDro9SAMMDyye8HhY1gqZ7/NqIc8SKhya0wRDgP1Q==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-TS9MlfzXpXKt6YYomudb/KU7nQI6/xnapG6in1uZxoxDghuSMZsPb6D2fyUwNYSAp4l1iR7QtFOjkqcRYcUsfw==", + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-proposal-object-rest-spread": { + "@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-8qvuPwU/xxUCt78HocNlv0mXXo0wdh9VT1R04WU8HGOfaOob26pF+9P5/lYjN/q7DHOX1bvX60hnhOvuQUJdbA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.8.0" } }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-0gkX7J7E+AtAw9fcwlVQj8peP61qhdg/89D5swOkjYbkboA2CVckn3kiyum1DE0wskGb7KJJxBdyEBApDLLVdw==", + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.10.4" } }, - "@babel/plugin-proposal-optional-chaining": { + "@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.8.3.tgz", - "integrity": "sha512-QIoIR9abkVn+seDE3OjA08jWcs3eZ9+wJCKSRgo3WdEU2csFYgdScb+8qHB3+WXsGJD55u+5hWCISI7ejXS+kg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz", - "integrity": "sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.0.tgz", - "integrity": "sha512-dt89fDlkfkTrQcy5KavMQPyF2A6tR0kYp8HAnIoQv5hO34iAUffHghP/hMGd7Gf/+uYTmLQO0ar7peX1SUWyIA==", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.8.0" @@ -1020,752 +887,529 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, "@babel/plugin-syntax-top-level-await": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.8.3.tgz", - "integrity": "sha512-kwj1j9lL/6Wd0hROD3b/OZZ7MSrZLqqn9RAZ5+cYYsflQ9HZBIKCUkr3+uL1MEJ1NePiUbf98jjiMQSv0NMR9g==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.8.3.tgz", - "integrity": "sha512-0MRF+KC8EqH4dbuITCWwPSzsyO3HIWWlm30v8BbbpOrS1B++isGxPnnuq/IZvOX5J2D/p7DQalQm+/2PnlKGxg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.14.5.tgz", + "integrity": "sha512-KOnO0l4+tD5IfOdi4x8C1XmEIRWUjNRV8wc6K2vz/3e8yAOoZZvsRXRRIF/yo/MAOFb4QjtAw9xSxMXbSMRy8A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.8.3.tgz", - "integrity": "sha512-imt9tFLD9ogt56Dd5CI/6XgpukMwd/fLGSrix2httihVe7LOGVPhyhMh1BU5kDM7iHD08i8uUtmV2sWaBFlHVQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.14.5.tgz", + "integrity": "sha512-szkbzQ0mNk0rpu76fzDdqSyPu0MuvpXgC+6rz5rpMb5OIRxdmHfQxrktL8CYolL2d8luMCZTR0DpIMIdL27IjA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-remap-async-to-generator": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-module-imports": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-remap-async-to-generator": "^7.14.5" } }, "@babel/plugin-transform-block-scoped-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.8.3.tgz", - "integrity": "sha512-vo4F2OewqjbB1+yaJ7k2EJFHlTP3jR634Z9Cj9itpqNjuLXvhlVxgnjsHsdRgASR8xYDrx6onw4vW5H6We0Jmg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.14.5.tgz", + "integrity": "sha512-dtqWqdWZ5NqBX3KzsVCWfQI3A53Ft5pWFCT2eCVUftWZgjc5DpDponbIF1+c+7cSGk2wN0YK7HGL/ezfRbpKBQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-block-scoping": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.8.3.tgz", - "integrity": "sha512-pGnYfm7RNRgYRi7bids5bHluENHqJhrV4bCZRwc5GamaWIIs07N4rZECcmJL6ZClwjDz1GbdMZFtPs27hTB06w==", + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.15.3.tgz", + "integrity": "sha512-nBAzfZwZb4DkaGtOes1Up1nOAp9TDRRFw4XBzBBSG9QK7KVFmYzgj9o9sbPv7TX5ofL4Auq4wZnxCoPnI/lz2Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "lodash": "^4.17.13" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-classes": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.8.6.tgz", - "integrity": "sha512-k9r8qRay/R6v5aWZkrEclEhKO6mc1CCQr2dLsVHBmOQiMpN6I2bpjX3vgnldUWeEI1GHVNByULVxZ4BdP4Hmdg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-define-map": "^7.8.3", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-split-export-declaration": "^7.8.3", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.15.4.tgz", + "integrity": "sha512-Yjvhex8GzBmmPQUvpXRPWQ9WnxXgAFuZSrqOK/eJlOGIXwvv8H3UEdUigl1gb/bnjTrln+e8bkZUYCBt/xYlBg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-optimise-call-expression": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", "globals": "^11.1.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } } }, "@babel/plugin-transform-computed-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.8.3.tgz", - "integrity": "sha512-O5hiIpSyOGdrQZRQ2ccwtTVkgUDBBiCuK//4RJ6UfePllUTCENOzKxfh6ulckXKc0DixTFLCfb2HVkNA7aDpzA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.14.5.tgz", + "integrity": "sha512-pWM+E4283UxaVzLb8UBXv4EIxMovU4zxT1OPnpHJcmnvyY9QbPPTKZfEj31EUvG3/EQRbYAGaYEUZ4yWOBC2xg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-destructuring": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz", - "integrity": "sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==", + "version": "7.14.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.14.7.tgz", + "integrity": "sha512-0mDE99nK+kVh3xlc5vKwB6wnP9ecuSj+zQCa/n0voENtP/zymdT4HH6QEb65wjjcbqr1Jb/7z9Qp7TF5FtwYGw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-dotall-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.8.3.tgz", - "integrity": "sha512-kLs1j9Nn4MQoBYdRXH6AeaXMbEJFaFu/v1nQkvib6QzTj8MZI5OQzqmD83/2jEM1z0DLilra5aWO5YpyC0ALIw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.14.5.tgz", + "integrity": "sha512-loGlnBdj02MDsFaHhAIJzh7euK89lBrGIdM9EAtHFo6xKygCUGuuWe07o1oZVk287amtW1n0808sQM99aZt3gw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-create-regexp-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-duplicate-keys": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.8.3.tgz", - "integrity": "sha512-s8dHiBUbcbSgipS4SMFuWGqCvyge5V2ZeAWzR6INTVC3Ltjig/Vw1G2Gztv0vU/hRG9X8IvKvYdoksnUfgXOEQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.14.5.tgz", + "integrity": "sha512-iJjbI53huKbPDAsJ8EmVmvCKeeq21bAze4fu9GBQtSLqfvzj2oRuHVx4ZkDwEhg1htQ+5OBZh/Ab0XDf5iBZ7A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-exponentiation-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.8.3.tgz", - "integrity": "sha512-zwIpuIymb3ACcInbksHaNcR12S++0MDLKkiqXHl3AzpgdKlFNhog+z/K0+TGW+b0w5pgTq4H6IwV/WhxbGYSjQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.14.5.tgz", + "integrity": "sha512-jFazJhMBc9D27o9jDnIE5ZErI0R0m7PbKXVq77FFvqFbzvTMuv8jaAwLZ5PviOLSFttqKIW0/wxNSDbjLk0tYA==", "dev": true, "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-for-of": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.8.6.tgz", - "integrity": "sha512-M0pw4/1/KI5WAxPsdcUL/w2LJ7o89YHN3yLkzNjg7Yl15GlVGgzHyCU+FMeAxevHGsLVmUqbirlUIKTafPmzdw==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.15.4.tgz", + "integrity": "sha512-DRTY9fA751AFBDh2oxydvVm4SYevs5ILTWLs6xKXps4Re/KG5nfUkr+TdHCrRWB8C69TlzVgA9b3RmGWmgN9LA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-function-name": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.8.3.tgz", - "integrity": "sha512-rO/OnDS78Eifbjn5Py9v8y0aR+aSYhDhqAwVfsTl0ERuMZyr05L1aFSCJnbv2mmsLkit/4ReeQ9N2BgLnOcPCQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.14.5.tgz", + "integrity": "sha512-vbO6kv0fIzZ1GpmGQuvbwwm+O4Cbm2NrPzwlup9+/3fdkuzo1YqOZcXw26+YUJB84Ja7j9yURWposEHLYwxUfQ==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-function-name": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.8.3.tgz", - "integrity": "sha512-3Tqf8JJ/qB7TeldGl+TT55+uQei9JfYaregDcEAyBZ7akutriFrt6C/wLYIer6OYhleVQvH/ntEhjE/xMmy10A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.14.5.tgz", + "integrity": "sha512-ql33+epql2F49bi8aHXxvLURHkxJbSmMKl9J5yHqg4PLtdE6Uc48CH1GS6TQvZ86eoB/ApZXwm7jlA+B3kra7A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-member-expression-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.8.3.tgz", - "integrity": "sha512-3Wk2EXhnw+rP+IDkK6BdtPKsUE5IeZ6QOGrPYvw52NwBStw9V1ZVzxgK6fSKSxqUvH9eQPR3tm3cOq79HlsKYA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.14.5.tgz", + "integrity": "sha512-WkNXxH1VXVTKarWFqmso83xl+2V3Eo28YY5utIkbsmXoItO8Q3aZxN4BTS2k0hz9dGUloHK26mJMyQEYfkn/+Q==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-modules-amd": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.8.3.tgz", - "integrity": "sha512-MadJiU3rLKclzT5kBH4yxdry96odTUwuqrZM+GllFI/VhxfPz+k9MshJM+MwhfkCdxxclSbSBbUGciBngR+kEQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.14.5.tgz", + "integrity": "sha512-3lpOU8Vxmp3roC4vzFpSdEpGUWSMsHFreTWOMMLzel2gNGfHE5UWIh/LN6ghHs2xurUp4jRFYMUIZhuFbody1g==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5", + "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.8.3.tgz", - "integrity": "sha512-JpdMEfA15HZ/1gNuB9XEDlZM1h/gF/YOH7zaZzQu2xCFRfwc01NXBMHHSTT6hRjlXJJs5x/bfODM3LiCk94Sxg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz", + "integrity": "sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-simple-access": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-simple-access": "^7.15.4", + "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.8.3.tgz", - "integrity": "sha512-8cESMCJjmArMYqa9AO5YuMEkE4ds28tMpZcGZB/jl3n0ZzlsxOAi3mC+SKypTfT8gjMupCnd3YiXCkMjj2jfOg==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.15.4.tgz", + "integrity": "sha512-fJUnlQrl/mezMneR72CKCgtOoahqGJNVKpompKwzv3BrEXdlPspTcyxrZ1XmDTIr9PpULrgEQo3qNKp6dW7ssw==", "dev": true, "requires": { - "@babel/helper-hoist-variables": "^7.8.3", - "@babel/helper-module-transforms": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "babel-plugin-dynamic-import-node": "^2.3.0" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-identifier": "^7.14.9", + "babel-plugin-dynamic-import-node": "^2.3.3" } }, "@babel/plugin-transform-modules-umd": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.8.3.tgz", - "integrity": "sha512-evhTyWhbwbI3/U6dZAnx/ePoV7H6OUG+OjiJFHmhr9FPn0VShjwC2kdxqIuQ/+1P50TMrneGzMeyMTFOjKSnAw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.14.5.tgz", + "integrity": "sha512-RfPGoagSngC06LsGUYyM9QWSXZ8MysEjDJTAea1lqRjNECE3y0qIJF/qbvJxc4oA4s99HumIMdXOrd+TdKaAAA==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-module-transforms": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", - "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", + "version": "7.14.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.14.9.tgz", + "integrity": "sha512-l666wCVYO75mlAtGFfyFwnWmIXQm3kSH0C3IRnJqWcZbWkoihyAdDhFm2ZWaxWTqvBvhVFfJjMRQ0ez4oN1yYA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.14.5" } }, "@babel/plugin-transform-new-target": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.8.3.tgz", - "integrity": "sha512-QuSGysibQpyxexRyui2vca+Cmbljo8bcRckgzYV4kRIsHpVeyeC3JDO63pY+xFZ6bWOBn7pfKZTqV4o/ix9sFw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.14.5.tgz", + "integrity": "sha512-Nx054zovz6IIRWEB49RDRuXGI4Gy0GMgqG0cII9L3MxqgXz/+rgII+RU58qpo4g7tNEx1jG7rRVH4ihZoP4esQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-object-super": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.8.3.tgz", - "integrity": "sha512-57FXk+gItG/GejofIyLIgBKTas4+pEU47IXKDBWFTxdPd7F80H8zybyAY7UoblVfBhBGs2EKM+bJUu2+iUYPDQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.14.5.tgz", + "integrity": "sha512-MKfOBWzK0pZIrav9z/hkRqIk/2bTv9qvxHzPQc12RcVkMOzpIKnFCNYJip00ssKWYkd8Sf5g0Wr7pqJ+cmtuFg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-replace-supers": "^7.14.5" } }, "@babel/plugin-transform-parameters": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.7.tgz", - "integrity": "sha512-brYWaEPTRimOctz2NDA3jnBbDi7SVN2T4wYuu0aqSzxC3nozFZngGaw29CJ9ZPweB7k+iFmZuoG3IVPIcXmD2g==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.15.4.tgz", + "integrity": "sha512-9WB/GUTO6lvJU3XQsSr6J/WKvBC2hcs4Pew8YxZagi6GkTdniyqp8On5kqdK8MN0LMeu0mGbhPN+O049NV/9FQ==", "dev": true, "requires": { - "@babel/helper-call-delegate": "^7.8.7", - "@babel/helper-get-function-arity": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-property-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.8.3.tgz", - "integrity": "sha512-uGiiXAZMqEoQhRWMK17VospMZh5sXWg+dlh2soffpkAl96KAm+WZuJfa6lcELotSRmooLqg0MWdH6UUq85nmmg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.14.5.tgz", + "integrity": "sha512-r1uilDthkgXW8Z1vJz2dKYLV1tuw2xsbrp3MrZmD99Wh9vsfKoob+JTgri5VUb/JqyKRXotlOtwgu4stIYCmnw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-regenerator": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz", - "integrity": "sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.14.5.tgz", + "integrity": "sha512-NVIY1W3ITDP5xQl50NgTKlZ0GrotKtLna08/uGY6ErQt6VEQZXla86x/CTddm5gZdcr+5GSsvMeTmWA5Ii6pkg==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" } }, "@babel/plugin-transform-reserved-words": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.8.3.tgz", - "integrity": "sha512-mwMxcycN3omKFDjDQUl+8zyMsBfjRFr0Zn/64I41pmjv4NJuqcYlEtezwYtw9TFd9WR1vN5kiM+O0gMZzO6L0A==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.14.5.tgz", + "integrity": "sha512-cv4F2rv1nD4qdexOGsRQXJrOcyb5CrgjUH9PKrrtyhSDBNWGxd0UIitjyJiWagS+EbUGjG++22mGH1Pub8D6Vg==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-shorthand-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.8.3.tgz", - "integrity": "sha512-I9DI6Odg0JJwxCHzbzW08ggMdCezoWcuQRz3ptdudgwaHxTjxw5HgdFJmZIkIMlRymL6YiZcped4TTCB0JcC8w==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.14.5.tgz", + "integrity": "sha512-xLucks6T1VmGsTB+GWK5Pl9Jl5+nRXD1uoFdA5TSO6xtiNjtXTjKkmPdFXVLGlK5A2/or/wQMKfmQ2Y0XJfn5g==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.8.3.tgz", - "integrity": "sha512-CkuTU9mbmAoFOI1tklFWYYbzX5qCIZVXPVy0jpXgGwkplCndQAa58s2jr66fTeQnA64bDox0HL4U56CFYoyC7g==", + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.15.8.tgz", + "integrity": "sha512-/daZ8s2tNaRekl9YJa9X4bzjpeRZLt122cpgFnQPLGUe61PH8zMEBmYqKkW5xF5JUEh5buEGXJoQpqBmIbpmEQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.15.4" } }, "@babel/plugin-transform-sticky-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.8.3.tgz", - "integrity": "sha512-9Spq0vGCD5Bb4Z/ZXXSK5wbbLFMG085qd2vhL1JYu1WcQ5bXqZBAYRzU1d+p79GcHs2szYv5pVQCX13QgldaWw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.14.5.tgz", + "integrity": "sha512-Z7F7GyvEMzIIbwnziAZmnSNpdijdr4dWt+FJNBnBLz5mwDFkqIXU9wmBcWWad3QeJF5hMTkRe4dAq2sUZiG+8A==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/helper-regex": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-template-literals": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.8.3.tgz", - "integrity": "sha512-820QBtykIQOLFT8NZOcTRJ1UNuztIELe4p9DCgvj4NK+PwluSJ49we7s9FB1HIGNIYT7wFUJ0ar2QpCDj0escQ==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.14.5.tgz", + "integrity": "sha512-22btZeURqiepOfuy/VkFr+zStqlujWaarpMErvay7goJS6BWwdd6BY9zQyDLDa4x2S3VugxFb162IZ4m/S/+Gg==", "dev": true, "requires": { - "@babel/helper-annotate-as-pure": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-typeof-symbol": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.8.4.tgz", - "integrity": "sha512-2QKyfjGdvuNfHsb7qnBBlKclbD4CfshH2KvDabiijLMGXPHJXGxtDzwIF7bQP+T0ysw8fYTtxPafgfs/c1Lrqg==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.14.5.tgz", + "integrity": "sha512-lXzLD30ffCWseTbMQzrvDWqljvZlHkXU+CnseMhkMNqU1sASnCsz3tSzAaH3vCUXb9PHeUb90ZT1BdFTm1xxJw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.14.5.tgz", + "integrity": "sha512-crTo4jATEOjxj7bt9lbYXcBAM3LZaUrbP2uUdxb6WIorLmjNKSpHfIybgY4B8SRpbf8tEVIWH3Vtm7ayCrKocA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/plugin-transform-unicode-regex": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.8.3.tgz", - "integrity": "sha512-+ufgJjYdmWfSQ+6NS9VGUR2ns8cjJjYbrbi11mZBTaWm+Fui/ncTLFF28Ei1okavY+xkojGr1eJxNsWYeA5aZw==", + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.14.5.tgz", + "integrity": "sha512-UygduJpC5kHeCiRw/xDVzC+wj8VaYSoKl5JNVmbP7MadpNinAm3SvZCxZ42H37KZBKztz46YC73i9yV34d0Tzw==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3" - }, - "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - } + "@babel/helper-create-regexp-features-plugin": "^7.14.5", + "@babel/helper-plugin-utils": "^7.14.5" } }, "@babel/preset-env": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.8.7.tgz", - "integrity": "sha512-BYftCVOdAYJk5ASsznKAUl53EMhfBbr8CJ1X+AJLfGPscQkwJFiaV/Wn9DPH/7fzm2v6iRYJKYHSqyynTGw0nw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.8.6", - "@babel/helper-compilation-targets": "^7.8.7", - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-proposal-async-generator-functions": "^7.8.3", - "@babel/plugin-proposal-dynamic-import": "^7.8.3", - "@babel/plugin-proposal-json-strings": "^7.8.3", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.8.3", - "@babel/plugin-proposal-optional-catch-binding": "^7.8.3", - "@babel/plugin-proposal-optional-chaining": "^7.8.3", - "@babel/plugin-proposal-unicode-property-regex": "^7.8.3", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.8.3", - "@babel/plugin-transform-arrow-functions": "^7.8.3", - "@babel/plugin-transform-async-to-generator": "^7.8.3", - "@babel/plugin-transform-block-scoped-functions": "^7.8.3", - "@babel/plugin-transform-block-scoping": "^7.8.3", - "@babel/plugin-transform-classes": "^7.8.6", - "@babel/plugin-transform-computed-properties": "^7.8.3", - "@babel/plugin-transform-destructuring": "^7.8.3", - "@babel/plugin-transform-dotall-regex": "^7.8.3", - "@babel/plugin-transform-duplicate-keys": "^7.8.3", - "@babel/plugin-transform-exponentiation-operator": "^7.8.3", - "@babel/plugin-transform-for-of": "^7.8.6", - "@babel/plugin-transform-function-name": "^7.8.3", - "@babel/plugin-transform-literals": "^7.8.3", - "@babel/plugin-transform-member-expression-literals": "^7.8.3", - "@babel/plugin-transform-modules-amd": "^7.8.3", - "@babel/plugin-transform-modules-commonjs": "^7.8.3", - "@babel/plugin-transform-modules-systemjs": "^7.8.3", - "@babel/plugin-transform-modules-umd": "^7.8.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", - "@babel/plugin-transform-new-target": "^7.8.3", - "@babel/plugin-transform-object-super": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.8.7", - "@babel/plugin-transform-property-literals": "^7.8.3", - "@babel/plugin-transform-regenerator": "^7.8.7", - "@babel/plugin-transform-reserved-words": "^7.8.3", - "@babel/plugin-transform-shorthand-properties": "^7.8.3", - "@babel/plugin-transform-spread": "^7.8.3", - "@babel/plugin-transform-sticky-regex": "^7.8.3", - "@babel/plugin-transform-template-literals": "^7.8.3", - "@babel/plugin-transform-typeof-symbol": "^7.8.4", - "@babel/plugin-transform-unicode-regex": "^7.8.3", - "@babel/types": "^7.8.7", - "browserslist": "^4.8.5", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" + "version": "7.15.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.15.8.tgz", + "integrity": "sha512-rCC0wH8husJgY4FPbHsiYyiLxSY8oMDJH7Rl6RQMknbN9oDDHhM9RDFvnGM2MgkbUJzSQB4gtuwygY5mCqGSsA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.15.0", + "@babel/helper-compilation-targets": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-validator-option": "^7.14.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.15.4", + "@babel/plugin-proposal-async-generator-functions": "^7.15.8", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-class-static-block": "^7.15.4", + "@babel/plugin-proposal-dynamic-import": "^7.14.5", + "@babel/plugin-proposal-export-namespace-from": "^7.14.5", + "@babel/plugin-proposal-json-strings": "^7.14.5", + "@babel/plugin-proposal-logical-assignment-operators": "^7.14.5", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5", + "@babel/plugin-proposal-numeric-separator": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/plugin-proposal-optional-catch-binding": "^7.14.5", + "@babel/plugin-proposal-optional-chaining": "^7.14.5", + "@babel/plugin-proposal-private-methods": "^7.14.5", + "@babel/plugin-proposal-private-property-in-object": "^7.15.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.14.5", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.14.5", + "@babel/plugin-transform-async-to-generator": "^7.14.5", + "@babel/plugin-transform-block-scoped-functions": "^7.14.5", + "@babel/plugin-transform-block-scoping": "^7.15.3", + "@babel/plugin-transform-classes": "^7.15.4", + "@babel/plugin-transform-computed-properties": "^7.14.5", + "@babel/plugin-transform-destructuring": "^7.14.7", + "@babel/plugin-transform-dotall-regex": "^7.14.5", + "@babel/plugin-transform-duplicate-keys": "^7.14.5", + "@babel/plugin-transform-exponentiation-operator": "^7.14.5", + "@babel/plugin-transform-for-of": "^7.15.4", + "@babel/plugin-transform-function-name": "^7.14.5", + "@babel/plugin-transform-literals": "^7.14.5", + "@babel/plugin-transform-member-expression-literals": "^7.14.5", + "@babel/plugin-transform-modules-amd": "^7.14.5", + "@babel/plugin-transform-modules-commonjs": "^7.15.4", + "@babel/plugin-transform-modules-systemjs": "^7.15.4", + "@babel/plugin-transform-modules-umd": "^7.14.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.14.9", + "@babel/plugin-transform-new-target": "^7.14.5", + "@babel/plugin-transform-object-super": "^7.14.5", + "@babel/plugin-transform-parameters": "^7.15.4", + "@babel/plugin-transform-property-literals": "^7.14.5", + "@babel/plugin-transform-regenerator": "^7.14.5", + "@babel/plugin-transform-reserved-words": "^7.14.5", + "@babel/plugin-transform-shorthand-properties": "^7.14.5", + "@babel/plugin-transform-spread": "^7.15.8", + "@babel/plugin-transform-sticky-regex": "^7.14.5", + "@babel/plugin-transform-template-literals": "^7.14.5", + "@babel/plugin-transform-typeof-symbol": "^7.14.5", + "@babel/plugin-transform-unicode-escapes": "^7.14.5", + "@babel/plugin-transform-unicode-regex": "^7.14.5", + "@babel/preset-modules": "^0.1.4", + "@babel/types": "^7.15.6", + "babel-plugin-polyfill-corejs2": "^0.2.2", + "babel-plugin-polyfill-corejs3": "^0.2.5", + "babel-plugin-polyfill-regenerator": "^0.2.2", + "core-js-compat": "^3.16.0", + "semver": "^6.3.0" }, "dependencies": { - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - }, "@babel/types": { - "version": "7.8.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.7.tgz", - "integrity": "sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw==", + "version": "7.15.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.6.tgz", + "integrity": "sha512-BPU+7QhqNjmWyDO0/vitH/CuhpV8ZmK1wpKva8nuyNF5MJfuRNWMc+hc14+u9xT93kvykMdncrJT19h74uB1Ig==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, + "@babel/preset-modules": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz", + "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, "@babel/register": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.8.6.tgz", - "integrity": "sha512-7IDO93fuRsbyml7bAafBQb3RcBGlCpU4hh5wADA2LJEEcYk92WkwFZ0pHyIi2fb5Auoz1714abETdZKCOxN0CQ==", + "version": "7.15.3", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.15.3.tgz", + "integrity": "sha512-mj4IY1ZJkorClxKTImccn4T81+UKTo4Ux0+OFSV9hME1ooqS9UV+pJ6BjD0qXPK4T3XW/KNa79XByjeEMZz+fw==", "dev": true, "requires": { + "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", - "lodash": "^4.17.13", "make-dir": "^2.1.0", "pirates": "^4.0.0", "source-map-support": "^0.5.16" } }, "@babel/runtime": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.8.4.tgz", - "integrity": "sha512-neAp3zt80trRVBI1x0azq6c57aNBqYZH8KhMm3TaB7wEI5Q4A2SHfBHE8w9gOhI/lrqxtEbXZgQIrHP+wvSGwQ==", - "dev": true, + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", + "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "^0.13.4" } }, "@babel/template": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.3.tgz", - "integrity": "sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz", + "integrity": "sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/parser": "^7.8.3", - "@babel/types": "^7.8.3" + "@babel/code-frame": "^7.14.5", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4" }, "dependencies": { - "@babel/parser": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.4.tgz", - "integrity": "sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==", - "dev": true - } + "@babel/types": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.9", + "to-fast-properties": "^2.0.0" + } + } } }, "@babel/traverse": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.8.6.tgz", - "integrity": "sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.8.6", - "@babel/helper-function-name": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/parser": "^7.8.6", - "@babel/types": "^7.8.6", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.15.4.tgz", + "integrity": "sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.14.5", + "@babel/generator": "^7.15.4", + "@babel/helper-function-name": "^7.15.4", + "@babel/helper-hoist-variables": "^7.15.4", + "@babel/helper-split-export-declaration": "^7.15.4", + "@babel/parser": "^7.15.4", + "@babel/types": "^7.15.4", "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" + "globals": "^11.1.0" }, "dependencies": { - "@babel/parser": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.6.tgz", - "integrity": "sha512-trGNYSfwq5s0SgM1BMEB8hX3NDmO7EP2wsDGDexiaKMB92BaRpS+qZfpkMqUBhcsOTBwNy9B/jieo4ad/t/z2g==", - "dev": true - }, "@babel/types": { - "version": "7.8.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.6.tgz", - "integrity": "sha512-wqz7pgWMIrht3gquyEFPVXeXCti72Rm8ep9b5tQKz9Yg9LzJA3HxosF1SB3Kc81KD1A3XBkkVYtJvCKS2Z/QrA==", + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.15.4.tgz", + "integrity": "sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.14.9", "to-fast-properties": "^2.0.0" } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true } } }, "@babel/types": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.8.3.tgz", - "integrity": "sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==", + "version": "7.13.12", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.12.tgz", + "integrity": "sha512-K4nY2xFN4QMvQwkQ+zmBDp6ANMbVNw6BbxWmYA4qNjhR9W+Lj/8ky5MEY2Me5r+B2c6/v6F53oMndG+f9s3IiA==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.13", + "@babel/helper-validator-identifier": "^7.12.11", + "lodash": "^4.17.19", "to-fast-properties": "^2.0.0" } }, @@ -1786,115 +1430,110 @@ } } }, + "@nicolo-ribaudo/chokidar-2": { + "version": "2.1.8-no-fsevents.3", + "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", + "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==", + "dev": true, + "optional": true + }, "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz", + "integrity": "sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.3", + "@nodelib/fs.stat": "2.0.4", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz", + "integrity": "sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz", + "integrity": "sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.3", + "@nodelib/fs.scandir": "2.1.4", "fastq": "^1.6.0" } }, - "@sentry/apm": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.14.2.tgz", - "integrity": "sha512-51yeQ04mKEsx2WiXbMlUSXhmG/D+YFiNJXxKuFopJkKkT02qr7B3QH0vHkS9OX2oniYoBTWZVCKYUAgJUSsIug==", - "requires": { - "@sentry/browser": "5.14.2", - "@sentry/hub": "5.14.2", - "@sentry/minimal": "5.14.2", - "@sentry/types": "5.14.2", - "@sentry/utils": "5.14.2", - "tslib": "^1.9.3" - } - }, - "@sentry/browser": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.14.2.tgz", - "integrity": "sha512-Vuuy2E5mt2VQKeHpFqtowZdKUe1Ui7J2KmgZQCduVilM7dFmprdXfv/mQ3Uv+73VIiCd22PpxojR3peDksb/Gg==", - "requires": { - "@sentry/core": "5.14.2", - "@sentry/types": "5.14.2", - "@sentry/utils": "5.14.2", - "tslib": "^1.9.3" - } - }, "@sentry/core": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.14.2.tgz", - "integrity": "sha512-B2XjUMCmVu4H3s5hapgynhb28MSc+irt9wRI9j0Lbjx2cxsCUr/YFGL8GuEuYwf4zXNKnh2ke6t+I37OlSaGOg==", - "requires": { - "@sentry/hub": "5.14.2", - "@sentry/minimal": "5.14.2", - "@sentry/types": "5.14.2", - "@sentry/utils": "5.14.2", + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-6.13.3.tgz", + "integrity": "sha512-obm3SjgCk8A7nB37b2AU1eq1q7gMoJRrGMv9VRIyfcG0Wlz/5lJ9O3ohUk+YZaaVfZMxXn6hFtsBiOWmlv7IIA==", + "requires": { + "@sentry/hub": "6.13.3", + "@sentry/minimal": "6.13.3", + "@sentry/types": "6.13.3", + "@sentry/utils": "6.13.3", "tslib": "^1.9.3" } }, "@sentry/hub": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.14.2.tgz", - "integrity": "sha512-0ckTDnhCANkuY+VepMPz5vl/dkFQnWmzlJiCIxgM5fCgAF8dfNd9VhGn0qVQXnzKPGoW9zxs/uAmH3/XFqqmNA==", + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-6.13.3.tgz", + "integrity": "sha512-eYppBVqvhs5cvm33snW2sxfcw6G20/74RbBn+E4WDo15hozis89kU7ZCJDOPkXuag3v1h9igns/kM6PNBb41dw==", "requires": { - "@sentry/types": "5.14.2", - "@sentry/utils": "5.14.2", + "@sentry/types": "6.13.3", + "@sentry/utils": "6.13.3", "tslib": "^1.9.3" } }, "@sentry/minimal": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.14.2.tgz", - "integrity": "sha512-uih9a8KwFCQrWaGb3UxkrSntxMRT4EIlud158ZKlrsLaCOE6i08unOR4xWqlrXlKPySq16H4wjbBFQ56ogOWdQ==", + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.13.3.tgz", + "integrity": "sha512-63MlYYRni3fs5Bh8XBAfVZ+ctDdWg0fapSTP1ydIC37fKvbE+5zhyUqwrEKBIiclEApg1VKX7bkKxVdu/vsFdw==", "requires": { - "@sentry/hub": "5.14.2", - "@sentry/types": "5.14.2", + "@sentry/hub": "6.13.3", + "@sentry/types": "6.13.3", "tslib": "^1.9.3" } }, "@sentry/node": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.14.2.tgz", - "integrity": "sha512-8s9JAKc/oid6lIFbYLtCLDwLhUpsgeU1WdNbs1eUJQSArb6WHS6EREVBuGr3RMfe+SkwEMg1rtPKnyj4C/WRig==", - "requires": { - "@sentry/apm": "5.14.2", - "@sentry/core": "5.14.2", - "@sentry/hub": "5.14.2", - "@sentry/types": "5.14.2", - "@sentry/utils": "5.14.2", - "cookie": "^0.3.1", - "https-proxy-agent": "^4.0.0", + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-6.13.3.tgz", + "integrity": "sha512-ZeZSw+TcPcf4e0j7iEqNMtoVmz+WFW/TEoGokXIwysZqSgchKdAXDHqn+CqUqFan7d76JcJmzztAUK2JruQ2Kg==", + "requires": { + "@sentry/core": "6.13.3", + "@sentry/hub": "6.13.3", + "@sentry/tracing": "6.13.3", + "@sentry/types": "6.13.3", + "@sentry/utils": "6.13.3", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", "tslib": "^1.9.3" } }, + "@sentry/tracing": { + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-6.13.3.tgz", + "integrity": "sha512-yyOFIhqlprPM0g4f35Icear3eZk2mwyYcGEzljJfY2iU6pJwj1lzia5PfSwiCW7jFGMmlBJNhOAIpfhlliZi8Q==", + "requires": { + "@sentry/hub": "6.13.3", + "@sentry/minimal": "6.13.3", + "@sentry/types": "6.13.3", + "@sentry/utils": "6.13.3", + "tslib": "^1.9.3" + } + }, "@sentry/types": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.14.2.tgz", - "integrity": "sha512-NtB/o+/whR/mJJf67Nvdab7E2+/THgAUY114FWFqDLHMaoiIVWy9J/yLKtQWymwuQslh7zpPxjA1AhqTJerVCg==" + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-6.13.3.tgz", + "integrity": "sha512-Vrz5CdhaTRSvCQjSyIFIaV9PodjAVFkzJkTRxyY7P77RcegMsRSsG1yzlvCtA99zG9+e6MfoJOgbOCwuZids5A==" }, "@sentry/utils": { - "version": "5.14.2", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.14.2.tgz", - "integrity": "sha512-DV9/kw/O8o5xqvQYwITm0lBaBqS4RKicjguWYJQ/+F94P/SKxuXor7EE0iMDYvUGslvPz8TlgB7r+nb/YRl+Fg==", + "version": "6.13.3", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-6.13.3.tgz", + "integrity": "sha512-zYFuFH3MaYtBZTeJ4Yajg7pDf0pM3MWs3+9k5my9Fd+eqNcl7dYQYJbT9gyC0HXK1QI4CAMNNlHNl4YXhF91ag==", "requires": { - "@sentry/types": "5.14.2", + "@sentry/types": "6.13.3", "tslib": "^1.9.3" } }, @@ -1903,75 +1542,16 @@ "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==" - }, - "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "dev": true, - "requires": { - "string-width": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, "ansi-escapes": { @@ -1983,9 +1563,9 @@ } }, "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.2.1", @@ -2002,191 +1582,26 @@ "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", "dev": true }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "optional": true + "dev": true }, "are-we-there-yet": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "optional": true, + "dev": true, "requires": { "delegates": "^1.0.0", "readable-stream": "^2.0.6" } }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "optional": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true, - "optional": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "array-union": { "version": "2.1.0", @@ -2194,13 +1609,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -2220,68 +1628,65 @@ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, - "assign-symbols": { + "at-least-node": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true, - "optional": true + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "axios": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.23.0.tgz", + "integrity": "sha512-NmvAE4i0YAv5cKq8zlDoPd1VLKAqX5oLuZKs8xkJa4qi6RGn0uhCYFjWtHHC9EM/MwOwYWOs53W+V0aqEXq1sg==", "requires": { - "lodash": "^4.17.14" + "follow-redirects": "^1.14.4" } }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true + "requires": { + "object.assign": "^4.1.0" + } }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "babel-plugin-polyfill-corejs2": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.2.tgz", + "integrity": "sha512-kISrENsJ0z5dNPq5eRvcctITNHYXWOA4DUZRFYCz3jYCcvTb/A546LIddmoGNMVYg2U38OyFeNosQwI9ENTqIQ==", "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", - "dev": true + "requires": { + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.2", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "babel-plugin-polyfill-corejs3": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.5.tgz", + "integrity": "sha512-ninF5MQNwAX9Z7c9ED+H2pGt1mXdP4TqzlHKyPIYmJIYz0N+++uwdM7RnJukklhzJ54Q84vA4ZJkgs7lu5vqcw==", + "dev": true, "requires": { - "follow-redirects": "1.5.10" + "@babel/helper-define-polyfill-provider": "^0.2.2", + "core-js-compat": "^3.16.2" } }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.0.tgz", - "integrity": "sha512-o6qFkpeQEBxcqt0XYlWzAVxNCSCZdUgcR8IRlhD/8DylxjjO4foPcvTW0GGKa/cVt3rvxZ7o5ippJ+/0nvLhlQ==", + "babel-plugin-polyfill-regenerator": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.2.tgz", + "integrity": "sha512-Goy5ghsc21HgPDFtzRkSirpZVW35meGoTmTOb2bxqdl60ghub4xOidgNTHaZfQ2FaxQsKmwvXtOAkcIS4SMBWg==", "dev": true, "requires": { - "object.assign": "^4.1.0" + "@babel/helper-define-polyfill-provider": "^0.2.2" } }, "balanced-match": { @@ -2289,65 +1694,10 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "optional": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "batch-promises": { "version": "0.0.3", @@ -2362,215 +1712,83 @@ "tweetnacl": "^0.14.3" } }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, + "brembo": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/brembo/-/brembo-2.0.6.tgz", + "integrity": "sha512-sGcm+tfYI9y+6if6ftI85a2dOpaeYWMCjkgk9bFkLnHRm+cp1L1dqG/N5kOhKd5qj8aZyLGQq2gDRWZZKYmDdA==" + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "browserslist": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.3.tgz", + "integrity": "sha512-59IqHJV5VGdcJZ+GZ2hU5n4Kv3YiASzW6Xk5g9tf5a/MAzGeFwgGWU39fVzNIOVcgB3+Gp+kiQu0HEfTVU/3VQ==", + "dev": true, "requires": { - "file-uri-to-path": "1.0.0" + "caniuse-lite": "^1.0.30001264", + "electron-to-chromium": "^1.3.857", + "escalade": "^3.1.1", + "node-releases": "^1.1.77", + "picocolors": "^0.2.1" } }, - "bl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", - "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "optional": true, "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "brembo": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brembo/-/brembo-2.0.3.tgz", - "integrity": "sha512-KLapbDMsMhrsXDvNl/mzl3HHqwJFSh9fBCdvRrkDQADA/CxNMmznVQHp24I9XVg22Y+qkY6PGRcG/11/aArhWA==" - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserslist": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.9.1.tgz", - "integrity": "sha512-Q0DnKq20End3raFulq6Vfp1ecB9fh8yUNV55s8sekaDDeqBaCtWlRHCUdaWyUeSSBJM7IbM6HcsyaeYqgeDhnw==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001030", - "electron-to-chromium": "^1.3.363", - "node-releases": "^1.1.50" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffermaker": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/buffermaker/-/buffermaker-1.2.1.tgz", - "integrity": "sha512-IdnyU2jDHU65U63JuVQNTHiWjPRH0CS3aYd/WPaEwyX84rFdukhOduAVb1jwUScmb5X0JWPw8NZOrhoLMiyAHQ==", - "requires": { - "long": "1.1.2" - } - }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" - }, - "bufferutil": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.1.tgz", - "integrity": "sha512-xowrxvpxojqkagPcWRQVXZl0YXhRhAtBEIq3VoER1NH5Mw1n1o0ojdspp+GS2J//2gCVyrzQDApQ4unGF+QOoA==", - "optional": true, - "requires": { - "node-gyp-build": "~3.7.0" + "node-gyp-build": "^4.3.0" } }, "bunyan": { @@ -2584,64 +1802,44 @@ "safe-json-stringify": "~1" } }, - "byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=", - "dev": true + "calculate-correlation": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/calculate-correlation/-/calculate-correlation-1.2.2.tgz", + "integrity": "sha512-TihJuPaWkVrfNZOEes+fDYuJhFNNYxc++ymmi3uqChWVaf6Y6IFqJzpT++umKV0p6zte+WyUbW6J79llS7p8/A==" }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", "dev": true, - "optional": true, "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" } }, "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" - }, - "caniuse-lite": { - "version": "1.0.30001032", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001032.tgz", - "integrity": "sha512-8joOm7BwcpEN4BfVHtfh0hBXSAPVYk+eUIcNntGtMkUWy/6AKRCDZINCLe3kB1vHhT2vBxBF85Hh9VlPXi/qjA==", - "dev": true - }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "caniuse-lite": { + "version": "1.0.30001265", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", + "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true }, "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", - "pathval": "^1.1.0", + "pathval": "^1.1.1", "type-detect": "^4.0.5" } }, @@ -2651,21 +1849,56 @@ "integrity": "sha1-pdDKFOMpp5WW7XAFi2ZGvWmIz+k=", "dev": true }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", - "requires": { - "traverse": ">=0.3.0 <0.4" - } - }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "chardet": { @@ -2673,6 +1906,11 @@ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", @@ -2680,165 +1918,94 @@ "dev": true }, "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "optional": true, "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "binary-extensions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "optional": true, "requires": { - "is-extendable": "^0.1.0" + "fill-range": "^7.0.1" } }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "optional": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "to-regex-range": "^5.0.1" } }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "optional": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "binary-extensions": "^2.0.0" } }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "optional": true, "requires": { - "is-buffer": "^1.1.5" + "picomatch": "^2.2.1" } }, "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "optional": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } } } }, "chownr": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", - "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", - "optional": true - }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", "dev": true }, "cli-cursor": { @@ -2849,67 +2016,48 @@ "restore-cursor": "^3.1.0" } }, + "cli-spinners": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.0.tgz", + "integrity": "sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q==" + }, "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" }, "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } + "wrap-ansi": "^7.0.0" } }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, - "optional": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" } }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2923,19 +2071,10 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.0.tgz", - "integrity": "sha512-NIQrwvv9V39FHgGFm36+U9SMQzbiHvU79k+iADraJTpmrFFfx7Ds0IvDoAdZsDrknlkRk14OYoWXb57uTh7/sw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true }, "commondir": { @@ -2944,54 +2083,21 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true, - "optional": true + "complex.js": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.0.15.tgz", + "integrity": "sha512-gDBvQU8IG139ZBQTSo2qvDFP+lANMGluM779csXOr6ny1NUtA3wkUnCFjlDNH/moAVfXtvClYt6G0zarFbtz5w==" }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true + "dev": true }, "convert-source-map": { "version": "1.7.0", @@ -3011,30 +2117,23 @@ } }, "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true, - "optional": true + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" }, "core-js": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", - "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==", + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.0.tgz", + "integrity": "sha512-5+5VxRFmSf97nM8Jr2wzOwLqRo6zphH2aX+7KsAUONObyzakDNq2G/bgbhinxB4PoV9L3aXQYhiDKyIKWd2c8g==", "dev": true }, "core-js-compat": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.4.tgz", - "integrity": "sha512-zAa3IZPvsJ0slViBQ2z+vgyyTuhd3MFn1rBQjZSKVEgB0UMYhUkCj9jJUVPgGTGqWvsBVmfnruXgTcNyTlEiSA==", + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.18.2.tgz", + "integrity": "sha512-25VJYCJtGjZwLguj7d66oiHfmnVw3TMOZ0zV8DyMJp/aeQ3OjR519iOOeck08HMyVVRAqXxafc2Hl+5QstJrsQ==", "dev": true, "requires": { - "browserslist": "^4.8.3", + "browserslist": "^4.17.3", "semver": "7.0.0" }, "dependencies": { @@ -3051,58 +2150,19 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "dev": true + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" }, "csv": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/csv/-/csv-5.3.1.tgz", - "integrity": "sha512-UBO4x5EYpihikfjHUQ7dCTIgC+e9TrWWZbCcoMr935tcAZfXT1MZKHLD+aYSHs1jwW2G1uljpFfJ4XxYwQ6t5w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/csv/-/csv-5.3.2.tgz", + "integrity": "sha512-odDyucr9OgJTdGM2wrMbJXbOkJx3nnUX3Pt8SFOwlAMOpsUQlz1dywvLMXJWX/4Ib0rjfOsaawuuwfI5ucqBGQ==", "requires": { "csv-generate": "^3.2.4", - "csv-parse": "^4.8.2", - "csv-stringify": "^5.3.4", + "csv-parse": "^4.8.8", + "csv-stringify": "^5.3.6", "stream-transform": "^2.0.1" } }, @@ -3112,14 +2172,14 @@ "integrity": "sha512-qNM9eqlxd53TWJeGtY1IQPj90b563Zx49eZs8e0uMyEvPgvNVmX1uZDtdzAcflB3PniuH9creAzcFOdyJ9YGvA==" }, "csv-parse": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.8.3.tgz", - "integrity": "sha512-0GPxubzYzSn08lhNTWDCkcDKn8krmw0WuscqB2RrW6sugGGskbwaaEz7PCFFwbQ0phNGTTieiyfzzu3S/jZZ7Q==" + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.10.1.tgz", + "integrity": "sha512-gdDJVchi0oSLIcYXz1H/VSgLE6htHDqJyFsRU/vTkQgmVOZ3S0IR2LXnNbWUYG7VD76dYVwdfBLyx8AX9+An8A==" }, "csv-stringify": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.3.6.tgz", - "integrity": "sha512-kPcRbMvo5NLLD71TAqW5K+g9kbM2HpIZJLAzm73Du8U+5TXmDp9YtXKCBLyxEh0q3Jbg8QhNFBz3b5VJzjZ/jw==" + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.5.0.tgz", + "integrity": "sha512-G05575DSO/9vFzQxZN+Srh30cNyHk0SM0ePyiTChMD5WVt7GMTVPBQf4rtgMF6mqhNCJUPw4pN8LDe8MF9EYOA==" }, "dashdash": { "version": "1.14.1", @@ -3130,30 +2190,29 @@ } }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true, - "optional": true + "decimal.js": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", + "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==" }, "decompress-response": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", - "optional": true, + "dev": true, "requires": { "mimic-response": "^2.0.0" } @@ -3170,7 +2229,8 @@ "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true }, "deep-is": { "version": "0.1.3", @@ -3178,6 +2238,19 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "requires": { + "clone": "^1.0.2" + } + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -3187,67 +2260,11 @@ "object-keys": "^1.0.12" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "optional": true - }, - "denque": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + "dev": true }, "depd": { "version": "1.1.2", @@ -3263,7 +2280,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "optional": true + "dev": true }, "detect-node": { "version": "2.0.4", @@ -3271,9 +2288,9 @@ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", "dev": true }, "dir-glob": { @@ -3285,15 +2302,6 @@ "path-type": "^4.0.0" } }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, "dtrace-provider": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", @@ -3303,12 +2311,6 @@ "nan": "^2.14.0" } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -3331,9 +2333,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "electron-to-chromium": { - "version": "1.3.368", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.368.tgz", - "integrity": "sha512-fqzDipW3p+uDkHUHFPrdW3wINRKcJsbnJwBD7hgaQEQwcuLSvNLw6SeUp5gKDpTbmTl7zri7IZfhsdTUTnygJg==", + "version": "1.3.861", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.861.tgz", + "integrity": "sha512-GZyflmpMnZRdZ1e2yAyvuFwz1MPSVQelwHX4TJZyXypB8NcxdPvPNwy5lOTxnlkrK13EiQzyTPugRSnj6cBgKg==", "dev": true }, "emoji-regex": { @@ -3350,28 +2352,63 @@ "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "optional": true, + "dev": true, "requires": { "once": "^1.4.0" } }, "es-abstract": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.0.tgz", - "integrity": "sha512-yYkE07YF+6SIBmg1MsJ9dlub5L48Ek7X0qz+c/CPCHS9EBXfESorzng4cJQjJW5/pB6vDF41u7F8vUhLVDqIug==", + "version": "1.18.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.4.tgz", + "integrity": "sha512-xjDAPJRxKc1uoTkdW8MEk7Fq/2bzz3YoCADYniDV7+KITCUdu9c90fj1aKI7nEZFZxRrHlDo3wtma/C6QkhlXQ==", "dev": true, "requires": { + "call-bind": "^1.0.2", "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", + "get-intrinsic": "^1.1.1", "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", + "has-symbols": "^1.0.2", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.3", + "is-negative-zero": "^2.0.1", + "is-regex": "^1.1.3", + "is-string": "^1.0.6", + "object-inspect": "^1.11.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "object.assign": "^4.1.2", + "string.prototype.trimend": "^1.0.4", + "string.prototype.trimstart": "^1.0.4", + "unbox-primitive": "^1.0.1" + }, + "dependencies": { + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } } }, "es-to-primitive": { @@ -3385,11 +2422,21 @@ "is-symbol": "^1.0.2" } }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" }, + "escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==" + }, "escape-regexp-component": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/escape-regexp-component/-/escape-regexp-component-1.0.2.tgz", @@ -3401,27 +2448,37 @@ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", + "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", "dev": true, "requires": { "esprima": "^4.0.1", - "estraverse": "^4.2.0", + "estraverse": "^5.2.0", "esutils": "^2.0.2", "optionator": "^0.8.1", "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } } }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true }, "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", "dev": true }, "esutils": { @@ -3443,103 +2500,12 @@ "assert-plus": "^1.0.0" } }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "dev": true }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "optional": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -3550,113 +2516,87 @@ "tmp": "^0.0.33" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" + }, + "fast-file-logger": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/fast-file-logger/-/fast-file-logger-1.1.1.tgz", + "integrity": "sha512-UlZ0buiGq+oGTt4ThLSsmJ/xRwsLL9SQB/cRJliF2C3r7fsgCH9shYchys2eAFry1AWf5MbxZvd7TF76PfzDvg==", + "requires": { + "calculate-correlation": "^1.2.2", + "file-stream-rotator": "^0.5.7", + "mathjs": "^9.5.1", + "moment": "^2.29.1", + "spearman-rho": "^1.0.6" + } + }, + "fast-glob": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", + "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", "dev": true, - "optional": true, "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "optional": true, "requires": { - "is-descriptor": "^1.0.0" + "fill-range": "^7.0.1" } }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "optional": true, "requires": { - "is-extendable": "^0.1.0" + "to-regex-range": "^5.0.1" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, - "optional": true, "requires": { - "kind-of": "^6.0.0" + "braces": "^3.0.1", + "picomatch": "^2.2.3" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "optional": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-number": "^7.0.0" } } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==" - }, - "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true - }, - "fast-glob": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", - "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -3664,9 +2604,9 @@ "dev": true }, "fastq": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.6.1.tgz", - "integrity": "sha512-mpIH5sKYueh3YyeJwqtVo8sORi0CgtmkVbK6kZStpQlZBYQuTzG2CZ7idSiJuA7bY0SFCWUc5WIs+oYumGCQNw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", + "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -3680,19 +2620,12 @@ "escape-string-regexp": "^1.0.5" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, + "file-stream-rotator": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.5.7.tgz", + "integrity": "sha512-VYb3HZ/GiAGUCrfeakO8Mp54YGswNUHvL7P09WQcXAJNSj3iQ5QraYSp3cIn1MUyw6uzfgN/EFOarCNa4JvUHQ==", "requires": { - "to-regex-range": "^5.0.1" + "moment": "^2.11.2" } }, "find-cache-dir": { @@ -3707,9 +2640,9 @@ } }, "find-my-way": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-2.2.1.tgz", - "integrity": "sha512-pzZA9/PlhDGG5PRzmd4vH4AbKW7FO68RE7q2I3NzjJHcVPukYbDA7bPdArg7ySKfS6pKki+qhrawFoN6aNZfjA==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-2.2.5.tgz", + "integrity": "sha512-GjRZZlGcGmTh9t+6Xrj5K0YprpoAFCAiCPgmAH9Kb09O4oX6hYuckDfnDipYj+Q7B1GtYWSzDI5HEecNYscLQg==", "requires": { "fast-decode-uri-component": "^1.0.0", "safe-regex2": "^2.0.0", @@ -3717,77 +2650,34 @@ } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "locate-path": "^3.0.0" } }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - } - } - }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "requires": { - "debug": "=3.1.0" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true, - "optional": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } + "follow-redirects": { + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==" }, "formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==" }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "optional": true, - "requires": { - "map-cache": "^0.2.2" - } + "fraction.js": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.1.tgz", + "integrity": "sha512-MHOhvvxHTfRFpF1geTK9czMIZ6xclsEor2wkIGYYq+PxcQqT7vStJqjhe6S1TenZrMZzo+wlqOufBDVepUEgPg==" }, "fresh": { "version": "0.5.2", @@ -3808,588 +2698,38 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true + "dev": true }, "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "requires": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" } }, "fs-readdir-recursive": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", - "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1", - "node-pre-gyp": "*" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "3.2.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.9.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^3.2.6", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.14.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4.4.2" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "npm-normalize-package-bin": "^1.0.1" - } - }, - "npm-normalize-package-bin": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "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": { - "version": "2.7.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.1", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.13", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.1.1", - "bundled": true, - "dev": true, - "optional": true - } - } + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true }, "function-bind": { "version": "1.1.1", @@ -4401,7 +2741,7 @@ "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "optional": true, + "dev": true, "requires": { "aproba": "^1.0.3", "console-control-strings": "^1.0.0", @@ -4411,12 +2751,49 @@ "string-width": "^1.0.1", "strip-ansi": "^3.0.1", "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } } }, "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true }, "get-caller-file": { @@ -4430,18 +2807,16 @@ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", "dev": true, - "optional": true + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } }, "getpass": { "version": "0.1.7", @@ -4455,40 +2830,30 @@ "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=", - "optional": true + "dev": true }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, "requires": { - "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "2 || 3", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { "is-glob": "^4.0.1" } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -4496,9 +2861,9 @@ "dev": true }, "globby": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.0.tgz", - "integrity": "sha512-iuehFnR3xu5wBBtm4xi0dMe92Ob87ufyu/dHwpDYfbcpYpIbrO5OnS8M1vWvrBhSGEJ3/Ecj7gnX76P8YxpPEg==", + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", + "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", "dev": true, "requires": { "array-union": "^2.1.0", @@ -4507,31 +2872,20 @@ "ignore": "^5.1.4", "merge2": "^1.3.0", "slash": "^3.0.0" - } - }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + }, + "dependencies": { + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } } }, "graceful-fs": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", - "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", "dev": true }, "growl": { @@ -4541,25 +2895,9 @@ "dev": true }, "handle-thing": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", - "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==" - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" }, "has": { "version": "1.0.3", @@ -4570,6 +2908,12 @@ "function-bind": "^1.1.1" } }, + "has-bigints": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.1.tgz", + "integrity": "sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==", + "dev": true + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4585,64 +2929,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "optional": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "dev": true }, "he": { "version": "1.2.0", @@ -4694,36 +2981,31 @@ } }, "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.4.tgz", + "integrity": "sha512-CbG3io8gUSIxNNSgq+XMjgpTMzAeVRipxVXjuGrDhH5M1a2kZ03w20s8FCLR1NjnnJj10KbvabvckmtQcYNb9g==", "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "sshpk": "^1.14.1" } }, "https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "requires": { - "agent-base": "5", + "agent-base": "6", "debug": "4" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "agent-base": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", + "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", "requires": { - "ms": "^2.1.1" + "debug": "4" } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, @@ -4735,28 +3017,15 @@ "safer-buffer": ">= 2.1.2 < 3" } }, - "ignore": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", - "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", - "dev": true - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", "dev": true }, "inflight": { @@ -4774,211 +3043,158 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true }, "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.0.tgz", + "integrity": "sha512-0crLweprevJ02tTuA6ThpoAERAGyVILC4sS74uib58Xf/zSr1/ZWtmm7D5CI+bSQEaA04f0K7idaHpQbSWgiVQ==", "requires": { "ansi-escapes": "^4.2.1", - "chalk": "^3.0.0", + "chalk": "^4.1.1", "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", + "cli-width": "^3.0.0", "external-editor": "^3.0.3", "figures": "^3.0.0", - "lodash": "^4.17.15", + "lodash": "^4.17.21", "mute-stream": "0.0.8", + "ora": "^5.4.1", "run-async": "^2.4.0", - "rxjs": "^6.5.3", + "rxjs": "^7.2.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6" }, "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + } + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "dependencies": { + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" } } } }, "into-stream": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-5.1.1.tgz", - "integrity": "sha512-krrAJ7McQxGGmvaYbB7Q1mcA+cRwg9Ij2RfWIeVesNBgVDZmzY/Fa4IpZUT3bmdRzMzdf/mzltCG2Dq99IZGBA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", + "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", "dev": true, "requires": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, "ip-address": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-6.2.0.tgz", - "integrity": "sha512-7G/8LVMRqM11pLcXx3PlX9rlqenMVUbppAc2sMvz+Ef0mUFm++cecpcEwb+Wfcdt2apu5XLTm9ox+Xz/TB7TGg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-8.1.0.tgz", + "integrity": "sha512-Wz91gZKpNKoXtqvY8ScarKYwhXoK4r/b5QuT+uywe/azv0/nUCo7Bh0IRRI7F9DHR06kJNWtzMGLIbXavngbKA==", "requires": { "jsbn": "1.1.0", - "lodash.find": "4.6.0", - "lodash.max": "4.0.1", - "lodash.merge": "4.6.2", - "lodash.padstart": "4.6.1", - "lodash.repeat": "4.1.0", "sprintf-js": "1.1.2" } }, "ip-sub": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/ip-sub/-/ip-sub-1.0.6.tgz", - "integrity": "sha512-LljiZE7cQflg3EruRKBe+ZpyjHCgovv82hgxCGZ+IFANzoqX2qhUO+O2WbfHepdmb6iW3gJJ/f1LVGCQsfS6Nw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ip-sub/-/ip-sub-1.1.2.tgz", + "integrity": "sha512-wG8grTj1P8RMICwEnpiwG5G+g46br78w5J06ckqzCBD3WaQmD/7ZHq67+hwEbJ8j+q7+jifUn5qyS/uKKnExfw==", "requires": { - "ip-address": "^6.2.0" + "ip-address": "^8.1.0" } }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "is-bigint": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.2.tgz", + "integrity": "sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA==", + "dev": true + }, + "is-boolean-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.1.tgz", + "integrity": "sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng==", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2" + "call-bind": "^1.0.2" }, "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", "dev": true, - "optional": true, "requires": { - "is-buffer": "^1.1.5" + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" } } } }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true, - "optional": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz", + "integrity": "sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==", "dev": true }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "dev": true, - "requires": { - "ci-info": "^1.5.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "has": "^1.0.3" } }, "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.4.tgz", + "integrity": "sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==", "dev": true }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "optional": true - }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4986,12 +3202,9 @@ "dev": true }, "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { "version": "4.0.1", @@ -5002,106 +3215,104 @@ "is-extglob": "^2.1.1" } }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { + "is-interactive": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", - "dev": true + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==" }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "is-negative-zero": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", + "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", "dev": true }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "is-number-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.5.tgz", + "integrity": "sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==", "dev": true }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, - "optional": true, "requires": { "isobject": "^3.0.1" } }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.3.tgz", + "integrity": "sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==", "dev": true, "requires": { - "has": "^1.0.3" + "call-bind": "^1.0.2", + "has-symbols": "^1.0.2" + }, + "dependencies": { + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } } }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "is-string": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.6.tgz", + "integrity": "sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==", "dev": true }, "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", "dev": true, "requires": { - "has-symbols": "^1.0.1" + "has-symbols": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } } }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "optional": true + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==" }, "isarray": { "version": "1.0.0", @@ -5118,15 +3329,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, + "javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5134,12 +3343,11 @@ "dev": true }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "jsbn": { @@ -5158,34 +3366,23 @@ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, "json5": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", - "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz", + "integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==", "dev": true, "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" } }, "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" } }, "jsprim": { @@ -5199,68 +3396,17 @@ "verror": "1.10.0" } }, - "kafka-node": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/kafka-node/-/kafka-node-5.0.0.tgz", - "integrity": "sha512-dD2ga5gLcQhsq1yNoQdy1MU4x4z7YnXM5bcG9SdQuiNr5KKuAmXixH1Mggwdah5o7EfholFbcNDPSVA6BIfaug==", - "requires": { - "async": "^2.6.2", - "binary": "~0.3.0", - "bl": "^2.2.0", - "buffer-crc32": "~0.2.5", - "buffermaker": "~1.2.0", - "debug": "^2.1.3", - "denque": "^1.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", - "nested-error-stacks": "^2.0.0", - "optional": "^0.1.3", - "retry": "^0.10.1", - "snappy": "^6.0.1", - "uuid": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } - } + "kafkajs": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/kafkajs/-/kafkajs-1.15.0.tgz", + "integrity": "sha512-yjPyEnQCkPxAuQLIJnY5dI+xnmmgXmhuOQ1GVxClG5KTOV/rJcW1qA3UfvyEJKTp/RTSqQnUR3HJsKFvHyTpNg==" }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "optional": true - }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - } - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -5272,124 +3418,55 @@ } }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.find": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", - "integrity": "sha1-ywcE1Hq3F4n/oN6Ll92Sb7iLE7E=" - }, - "lodash.max": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.max/-/lodash.max-4.0.1.tgz", - "integrity": "sha1-hzVWbGGLNan3YFILSHrnllivE2o=" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "lodash.padstart": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", - "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=" - }, - "lodash.repeat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz", - "integrity": "sha1-/H3oEx2MisB+S0n3T/6CnR8r7EQ=" + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true }, "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "requires": { - "chalk": "^2.4.2" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "requires": { - "has-flag": "^3.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } } } }, - "long": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/long/-/long-1.1.2.tgz", - "integrity": "sha1-6u9ZUcp1UdlpJrgtokLbnWso+1M=" - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, + "longest-prefix-match": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/longest-prefix-match/-/longest-prefix-match-1.1.13.tgz", + "integrity": "sha512-KvuD5G2G/72T1J9STpRgkAmVRIlTa0Dhuw4oxef8YhdtAZq4pshCFUXIRGSIw5lVwmkMfzhAU1b6VhKn8inKYw==", "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "ip-sub": "^1.1.2", + "radix-trie-js": "^1.0.5" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -5421,58 +3498,42 @@ } } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "optional": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "optional": true, + "mathjs": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-9.5.1.tgz", + "integrity": "sha512-yYu67sdmrLrQeRyN+DPH0aRQphdmI/gz4oNXFx4YR43NKifOiNTfXT30+ACsNIWaqJ1KihhVDD+X1kwfI2/X9g==", "requires": { - "object-visit": "^1.0.0" + "@babel/runtime": "^7.15.4", + "complex.js": "^2.0.15", + "decimal.js": "^10.3.1", + "escape-latex": "^1.2.0", + "fraction.js": "^4.1.1", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^2.0.0" } }, - "merge2": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.3.0.tgz", - "integrity": "sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" } }, - "mime": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", - "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==" - }, - "mime-db": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", - "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, - "mime-types": { - "version": "2.1.26", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", - "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", - "dev": true, - "requires": { - "mime-db": "1.43.0" - } + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" }, "mimic-fn": { "version": "2.1.0", @@ -5480,212 +3541,101 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, "mimic-response": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.0.0.tgz", - "integrity": "sha512-8ilDoEapqA4uQ3TwS0jakGONKXVJqpy+RpM+3b7pLdOjghCrEiGp9SRkFbUHAmZW9vdnrENWHjaweIoTIJExSQ==", - "optional": true - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "optional": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mixme": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.3.5.tgz", - "integrity": "sha512-SyV9uPETRig5ZmYev0ANfiGeB+g6N2EnqqEfBbCGmmJ6MgZ3E4qv5aPbnHVdZ60KAHHXV+T3sXopdrnIXQdmjQ==" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } - } - }, - "mocha": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.1.tgz", - "integrity": "sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.3", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mixme": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.5.4.tgz", + "integrity": "sha512-3KYa4m4Vlqx98GPdOHghxSdNtTvcP8E0kkaJ5Dlh+h2DRzF7zpuVVcA8B0QpKd11YJeP9QQ7ASkKzOeu195Wzw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "mocha": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -5696,165 +3646,108 @@ "path-is-absolute": "^1.0.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "binary-extensions": "^2.0.0" + "p-locate": "^5.0.0" } }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mkdirp": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz", - "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==", + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "minimist": "^1.2.5" + "yocto-queue": "^0.1.0" } }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true }, "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "has-flag": "^4.0.0" } }, "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } } } }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", - "optional": true + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multistream": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/multistream/-/multistream-2.1.1.tgz", - "integrity": "sha512-xasv76hl6nr1dEy3lPvy7Ej7K/Lx3O/FCvwge8PeVJpciPPoNCbaANcNiBug3IpdvTveZUcAV0DJzdnUDMesNQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/multistream/-/multistream-4.1.0.tgz", + "integrity": "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw==", "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.5" + "once": "^1.4.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "mute-stream": { @@ -5885,36 +3778,22 @@ } }, "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", "optional": true }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } + "nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "dev": true }, "napi-build-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.1.tgz", - "integrity": "sha512-boQj1WFgQH3v4clhu3mTNfP+vOBxorDlE8EKiMjUlLG3C4qAESnn9AxIOkFgTR2c9LtzNjPrjS60cT27ZKBhaA==", - "optional": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true }, "ncp": { "version": "2.0.0", @@ -5927,16 +3806,19 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" }, - "nested-error-stacks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz", - "integrity": "sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug==" + "net-validations": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/net-validations/-/net-validations-1.0.5.tgz", + "integrity": "sha512-IlNyi6hQ9tNyCjUaniVtVSR0cVHfchJoQOnVR1HVkk9/oshsNuPAViqmYl7ybv0pAMandNhvN/HhYjJ4awsixQ==", + "requires": { + "ip-sub": "^1.1.2" + } }, "node-abi": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.13.0.tgz", - "integrity": "sha512-9HrZGFVTR5SOu3PZAnAY2hLO36aW1wmA+FDsVkr85BTST32TLCA1H/AEcatVRAsWLyXS3bqUDYCAjq5/QGuSTA==", - "optional": true, + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.26.0.tgz", + "integrity": "sha512-ag/Vos/mXXpWLLAYWsAoQdgS+gW7IwvgMLOgqopm/DbzAjazLltzgzpVMsFlgmo9TzG5hGXeaBZx2AI731RIsQ==", + "dev": true, "requires": { "semver": "^5.4.1" }, @@ -5945,10 +3827,15 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "optional": true + "dev": true } } }, + "node-cleanup": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-cleanup/-/node-cleanup-2.1.2.tgz", + "integrity": "sha1-esGavSl+Caf3KnFUXZUbUX5N3iw=" + }, "node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -5967,10 +3854,16 @@ } } }, + "node-fetch": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.2.tgz", + "integrity": "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA==", + "dev": true + }, "node-gyp-build": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-3.7.0.tgz", - "integrity": "sha512-L/Eg02Epx6Si2NXmedx+Okg+4UHqmaf3TNcxd50SF9NQGcJaON3AtU++kax69XV7YWz4tUspqZSAsVofhFKG2w==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==", "optional": true }, "node-modules-regexp": { @@ -5980,154 +3873,21 @@ "dev": true }, "node-releases": { - "version": "1.1.50", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", - "integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", + "dev": true }, "nodemailer": { - "version": "6.4.5", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.5.tgz", - "integrity": "sha512-NH7aNVQyZLAvGr2+EOto7znvz+qJ02Cb/xpou98ApUt5tEAUSVUxhvHvgV/8I5dhjKTYqUw0nasoKzLNBJKrDQ==" - }, - "nodemon": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz", - "integrity": "sha512-GWhYPMfde2+M0FsHnggIHXTqPDHXia32HRhh6H0d75Mt9FKUoCBvumNHr7LdrpPBTKxsWmIEOjoN+P4IU6Hcaw==", - "dev": true, - "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.7" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.0.tgz", + "integrity": "sha512-AtiTVUFHLiiDnMQ43zi0YgkzHOEWUkhDgPlBXrsDzJiJvB29Alo4OKxHQ0ugF3gRqRQIneCLtZU3yiUo7pItZw==" }, "noop-logger": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", "integrity": "sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI=", - "optional": true - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1" - } + "dev": true }, "normalize-path": { "version": "3.0.0", @@ -6135,20 +3895,11 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, "npmlog": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "optional": true, + "dev": true, "requires": { "are-we-there-yet": "~1.1.2", "console-control-strings": "~1.1.0", @@ -6159,57 +3910,19 @@ "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "optional": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.11.0.tgz", + "integrity": "sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==", "dev": true }, "object-keys": { @@ -6218,46 +3931,50 @@ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", "dev": true }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.0" - } - }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz", + "integrity": "sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==", "dev": true, "requires": { + "call-bind": "^1.0.2", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.1" + "es-abstract": "^1.18.0-next.2" + }, + "dependencies": { + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + } } }, "obuf": { @@ -6282,18 +3999,13 @@ } }, "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "requires": { "mimic-fn": "^2.1.0" } }, - "optional": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/optional/-/optional-0.1.4.tgz", - "integrity": "sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==" - }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -6308,17 +4020,38 @@ "word-wrap": "~1.2.3" } }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, "p-is-promise": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", @@ -6326,45 +4059,28 @@ "dev": true }, "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, "requires": { "p-try": "^2.0.0" } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "^2.0.0" } }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true }, "parse-passwd": { "version": "1.0.0", @@ -6372,13 +4088,6 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "optional": true - }, "path": { "version": "0.12.7", "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", @@ -6388,39 +4097,21 @@ "util": "^0.10.3" } }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true, - "optional": true - }, "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, "path-type": { @@ -6430,27 +4121,27 @@ "dev": true }, "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", "dev": true }, "picomatch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", - "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", "dev": true }, "pidusage": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.17.tgz", - "integrity": "sha512-N8X5v18rBmlBoArfS83vrnD0gIFyZkXEo7a5pAS2aT0i2OLVymFb2AzVg+v8l/QcXnE1JwZcaXR8daJcoJqtjw==", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/pidusage/-/pidusage-2.0.20.tgz", + "integrity": "sha512-ObZwSCGhHAu+fDpHeIP7ZTy5YbQNOguJCKQE02kSnlEvBgj62cParJ4nzYQxpXauKUzT3e0CeSb1Y9Cgbry2qQ==", "requires": { "safe-buffer": "^5.1.2" } @@ -6471,34 +4162,49 @@ } }, "pkg": { - "version": "4.4.4", - "resolved": "https://registry.npmjs.org/pkg/-/pkg-4.4.4.tgz", - "integrity": "sha512-YBdBtnAIhseJ2K66V19tNlCY/oRaKhFroYxhDuY7msEOLCBklDSNfc7lhoBJGQ7T73VeU4Uzm5V05tcqSeM1Vg==", - "dev": true, - "requires": { - "@babel/parser": "^7.7.5", - "@babel/runtime": "^7.7.5", - "chalk": "^3.0.0", - "escodegen": "^1.13.0", - "fs-extra": "^8.1.0", - "globby": "^11.0.0", - "into-stream": "^5.1.1", - "minimist": "^1.2.0", - "multistream": "^2.1.1", - "pkg-fetch": "^2.6.4", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/pkg/-/pkg-5.3.3.tgz", + "integrity": "sha512-48qPxwyPvKfUuXxeK+kS3mBvfWWTX2khAdceDThbWCc8OUz3RFyO1Ft8SVkq2gQfPU2DtiPtWaf2SKH0Dlx59g==", + "dev": true, + "requires": { + "@babel/parser": "7.13.13", + "@babel/types": "7.13.12", + "chalk": "^4.1.0", + "escodegen": "^2.0.0", + "fs-extra": "^9.1.0", + "globby": "^11.0.3", + "into-stream": "^6.0.0", + "minimist": "^1.2.5", + "multistream": "^4.1.0", + "pkg-fetch": "3.2.3", + "prebuild-install": "6.0.1", "progress": "^2.0.3", - "resolve": "^1.15.0", - "stream-meter": "^1.0.4" + "resolve": "^1.20.0", + "stream-meter": "^1.0.4", + "tslib": "2.1.0" }, "dependencies": { - "resolve": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", - "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "@babel/parser": { + "version": "7.13.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", + "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", + "dev": true + }, + "chalk": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", + "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } + }, + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==", + "dev": true } } }, @@ -6509,89 +4215,61 @@ "dev": true, "requires": { "find-up": "^3.0.0" + } + }, + "pkg-fetch": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-3.2.3.tgz", + "integrity": "sha512-bv9vYANgAZ2Lvxn5Dsq7E0rLqzcqYkV4gnwe2f7oHV9N4SVMfDOIjjFCRuuTltop5EmsOcu7XkQpB5A/pIgC1g==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "fs-extra": "^9.1.0", + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.1", + "progress": "^2.0.3", + "semver": "^7.3.5", + "yargs": "^16.2.0" }, "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "pkg-fetch": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/pkg-fetch/-/pkg-fetch-2.6.4.tgz", - "integrity": "sha512-4j4jiuo6RRIuD9e9xUE6OQYnIkQCArZjkHXNYsSJjxhJeiHE16MA+rENMblvGLbeWsTY3BPfcYVCGFXzpfJetA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.7.5", - "byline": "^5.0.0", - "chalk": "^3.0.0", - "expand-template": "^2.0.3", - "fs-extra": "^8.1.0", - "minimist": "^1.2.0", - "progress": "^2.0.3", - "request": "^2.88.0", - "request-progress": "^3.0.0", - "semver": "^6.3.0", - "unique-temp-dir": "^1.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true } } }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "optional": true - }, "prebuild-install": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.3.tgz", - "integrity": "sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g==", - "optional": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-6.0.1.tgz", + "integrity": "sha512-7GOJrLuow8yeiyv75rmvZyeMGzl8mdEX5gY69d6a6bHWmiPevwqFw+tQavhK0EYMaSg3/KD24cWqeQv1EWsqDQ==", + "dev": true, "requires": { "detect-libc": "^1.0.3", "expand-template": "^2.0.3", "github-from-package": "0.0.0", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", "napi-build-utils": "^1.0.1", "node-abi": "^2.7.0", "noop-logger": "^0.1.1", @@ -6610,18 +4288,6 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==", - "dev": true - }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -6638,44 +4304,25 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "psl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", - "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", - "dev": true - }, - "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", - "dev": true - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "optional": true, + "dev": true, "requires": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, "radix-trie-js": { @@ -6683,6 +4330,15 @@ "resolved": "https://registry.npmjs.org/radix-trie-js/-/radix-trie-js-1.0.5.tgz", "integrity": "sha512-3Olqy+P8cM5G2x+mnhD231bRM8c2uxAyU7uLS/dORgwzmr2CJKhT6DeQGqZyjsfWuqCYeX415xlMr1J6Xt7M9Q==" }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -6692,6 +4348,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, "requires": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -6700,9 +4357,9 @@ } }, "read-last-lines": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/read-last-lines/-/read-last-lines-1.7.2.tgz", - "integrity": "sha512-K0yUvTYAYn6qpyLJufaJ7yC6BeL23qpgZ8SKM7/fA1R1rHotCDxB/zDp9i1I2JHvexWBW6/35jkt07iiIKKp4g==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/read-last-lines/-/read-last-lines-1.8.0.tgz", + "integrity": "sha512-oPL0cnZkhsO2xF7DBrdzVhXSNajPP5TzzCim/2IAjeGb17ArLLTRriI/ceV6Rook3L27mvbrOvLlf9xYYnaftQ==", "dev": true, "requires": { "mz": "^2.7.0" @@ -6729,216 +4386,59 @@ } } }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, "regenerate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", - "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", "dev": true }, "regenerate-unicode-properties": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", - "integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", "dev": true, "requires": { "regenerate": "^1.4.0" } }, "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", - "dev": true + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" }, "regenerator-transform": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.2.tgz", - "integrity": "sha512-V4+lGplCM/ikqi5/mkkpJ06e9Bujq1NFmNLvsCs56zg3ZbzrnUzAtizZ24TXxtRX/W2jcdScwQCnbL0CICTFkQ==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4", - "private": "^0.1.8" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", "dev": true, - "optional": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "@babel/runtime": "^7.8.4" } }, "regexpu-core": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", - "integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz", + "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==", "dev": true, "requires": { "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.1.0", - "regjsgen": "^0.5.0", - "regjsparser": "^0.6.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.1.0" - } - }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "requires": { - "rc": "^1.0.1" + "unicode-match-property-value-ecmascript": "^1.2.0" } }, "regjsgen": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz", - "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", "dev": true }, "regjsparser": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.3.tgz", - "integrity": "sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA==", + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.9.tgz", + "integrity": "sha512-ZqbNRz1SNjLAiYuwY0zoXW8Ne675IX5q+YHioAGbCw4X96Mjl2+dcX9B2ciaeyYjViDAfvIjFpQjJgLttTEERQ==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -6952,94 +4452,25 @@ } } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true, - "optional": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "optional": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "optional": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "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" - } - }, - "request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha1-TKdUCBx/7GP1BeT6qCWqBs1mnb4=", - "dev": true, - "requires": { - "throttleit": "^1.0.0" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "resolve": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz", - "integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", "dev": true, "requires": { + "is-core-module": "^2.2.0", "path-parse": "^1.0.6" } }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true, - "optional": true - }, "restify": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/restify/-/restify-8.5.1.tgz", - "integrity": "sha512-g+xciouvSDg2vmCZuCinztt2mvQynCfnGIE1y8vMjfcUrjBo4AP8DJ9RNheu0mdGpiI0cMoCHYA/GdZ3TEW+DA==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/restify/-/restify-8.6.0.tgz", + "integrity": "sha512-vLorgPD6j6bqmBqsBjKHa12c963jYESjJ2MOKK/+9nVlS7if/yl5GJ4/Vvty8qHecCtA2vZ3uejlczL+cv2mmg==", "requires": { "assert-plus": "^1.0.0", "bunyan": "^1.8.12", @@ -7065,15 +4496,15 @@ "vasync": "^2.2.0" }, "dependencies": { - "qs": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.1.tgz", - "integrity": "sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==" - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -7102,11 +4533,6 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==" }, - "retry": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz", - "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -7120,60 +4546,53 @@ "optional": true, "requires": { "glob": "^6.0.1" - }, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "optional": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } } }, "rpki-validator": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/rpki-validator/-/rpki-validator-1.0.13.tgz", - "integrity": "sha512-tymXvZj8PPonXyBkdIZntsTZ9fkjJs/hqRALnwd53Gh8N0WwdJ+R2B9x3s1lihgvM0ooSgwg0hMkan6lhEpCgw==", + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/rpki-validator/-/rpki-validator-2.7.4.tgz", + "integrity": "sha512-FooYYOoxnSOSdxzWBk8hjcQjo0dal83WY/zGOHjr9XabOeM/iAmXfiBzDRge61Pwtg4USzxdxugDTzlKDGd+nw==", "requires": { - "axios": "^0.19.2", - "brembo": "^2.0.3", - "ip-sub": "^1.0.6", - "radix-trie-js": "^1.0.5" + "axios": "^0.23.0", + "brembo": "^2.0.5", + "ip-sub": "^1.1.2", + "longest-prefix-match": "^1.1.13", + "net-validations": "^1.0.5" } }, "run-async": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.0.tgz", - "integrity": "sha512-xJTbh/d7Lm7SBhc1tNvTpeCHaEzoyxPrqNlvSdMfBTYwaY++UJFyXUOxAtsRUXjlqOfj8luNaR9vjCh4KeV+pg==", - "requires": { - "is-promise": "^2.1.0" - } + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" }, "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } }, "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.2.0.tgz", + "integrity": "sha512-aX8w9OpKrQmiPKfT1bqETtUr9JygIz6GZ+gql8v7CijClsP0laoFUdKzxFAoWuRdSlOdU2+crss+cMf+cqMTnw==", "requires": { - "tslib": "^1.9.0" + "tslib": "~2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", + "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + } } }, "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safe-json-stringify": { "version": "1.2.0", @@ -7181,25 +4600,6 @@ "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", "optional": true }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "optional": true, - "requires": { - "ret": "~0.1.10" - }, - "dependencies": { - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "optional": true - } - } - }, "safe-regex2": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-2.0.0.tgz", @@ -7213,30 +4613,36 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" }, "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==" - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true, + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "requires": { - "semver": "^5.0.3" + "lru-cache": "^6.0.0" }, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -7277,75 +4683,83 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, - "optional": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "randombytes": "^2.1.0" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "kind-of": "^6.0.2" } }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "dependencies": { + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + } + } }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, "simple-concat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.0.tgz", - "integrity": "sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY=", - "optional": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true }, "simple-get": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.0.tgz", "integrity": "sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA==", - "optional": true, + "dev": true, "requires": { "decompress-response": "^4.2.0", "once": "^1.3.1", @@ -7353,218 +4767,45 @@ } }, "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", "dev": true }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "optional": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "optional": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "snappy": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/snappy/-/snappy-6.2.3.tgz", - "integrity": "sha512-HZpVoIxMfQ4fL3iDuMdI1R5xycw1o9YDCAndTKZCY/EHRoKFvzwplttuBBVGeEg2fd1hYiwAXos/sM24W7N1LA==", - "optional": true, - "requires": { - "bindings": "^1.3.1", - "nan": "^2.14.0", - "prebuild-install": "^5.2.2" - } - }, "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", "dev": true }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "optional": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, "source-map-support": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", - "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true, - "optional": true - }, "spdy": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", - "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "requires": { "debug": "^4.1.0", "handle-thing": "^2.0.0", "http-deceiver": "^1.2.7", "select-hose": "^2.0.0", "spdy-transport": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } } }, "spdy-transport": { @@ -7580,23 +4821,10 @@ "wbuf": "^1.7.3" }, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7605,14 +4833,12 @@ } } }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "optional": true, + "spearman-rho": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/spearman-rho/-/spearman-rho-1.0.6.tgz", + "integrity": "sha1-ZuBM/6Olz8dSv/fBp5X2+okJ0/Y=", "requires": { - "extend-shallow": "^3.0.0" + "lodash": "^4.17.4" } }, "sprintf-js": { @@ -7643,29 +4869,6 @@ } } }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "optional": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -7681,41 +4884,87 @@ } }, "stream-transform": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.1.tgz", - "integrity": "sha512-GiTcO/rRvZP2R8WPwxmxCFP+Of1yIATuFAmYkvSLDfcD93X2WHiPwdgIqeFT2CvL1gyAsjQvu1nB6RDNQ5b2jw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.1.0.tgz", + "integrity": "sha512-bwQO+75rzQbug7e5OOHnOR3FgbJ0fCjHmDIdynkwUaFzleBXugGmv2dx3sX3aIHUQRLjrcisRPgN9BWl63uGgw==", "requires": { - "mixme": "^0.3.1" + "mixme": "^0.5.0" } }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "string.prototype.trimend": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz", + "integrity": "sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "dependencies": { + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + } } }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "string.prototype.trimstart": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz", + "integrity": "sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==", "dev": true, "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "dependencies": { + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + } } }, "string_decoder": { @@ -7734,23 +4983,18 @@ } }, "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^5.0.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true }, "supports-color": { "version": "7.1.0", @@ -7765,45 +5009,62 @@ "resolved": "https://registry.npmjs.org/syslog-client/-/syslog-client-1.1.1.tgz", "integrity": "sha1-vbKN47W36yihE1LsPreOVa7Sq2s=" }, + "syslogd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/syslogd/-/syslogd-1.1.2.tgz", + "integrity": "sha1-YjNXub+igBpGPlxy7Bp5+qdtlUE=", + "dev": true, + "requires": { + "debug": "^2.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "tar-fs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.0.0.tgz", - "integrity": "sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==", - "optional": true, + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, "requires": { "chownr": "^1.1.1", - "mkdirp": "^0.5.1", + "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", - "tar-stream": "^2.0.0" + "tar-stream": "^2.1.4" } }, "tar-stream": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.0.tgz", - "integrity": "sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==", - "optional": true, + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, "requires": { - "bl": "^3.0.0", + "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" }, "dependencies": { - "bl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz", - "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==", - "optional": true, - "requires": { - "readable-stream": "^3.0.1" - } - }, "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "optional": true, + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7812,15 +5073,6 @@ } } }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "^0.7.0" - } - }, "thenify": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", @@ -7839,22 +5091,15 @@ "thenify": ">= 3.1.0 < 4" } }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" }, "tmp": { "version": "0.0.33", @@ -7870,371 +5115,111 @@ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", "dev": true }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" - }, "tslib": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", - "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" - }, - "uid2": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", - "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=", - "dev": true - }, - "undefsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", - "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", - "dev": true, - "requires": { - "debug": "^2.2.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", - "integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", - "integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unique-temp-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-temp-dir/-/unique-temp-dir-1.0.0.tgz", - "integrity": "sha1-bc6VsmgcoAPuv7MEpBX5y6vMU4U=", - "dev": true, - "requires": { - "mkdirp": "^0.5.1", - "os-tmpdir": "^1.0.1", - "uid2": "0.0.3" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "optional": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "optional": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "optional": true - } - } - }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "optional": true - }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "punycode": "^2.1.0" + "safe-buffer": "^5.0.1" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, - "optional": true + "requires": { + "prelude-ls": "~1.1.2" + } }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" + }, + "typed-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-2.0.0.tgz", + "integrity": "sha512-Hhy1Iwo/e4AtLZNK10ewVVcP2UEs408DS35ubP825w/YgSBK1KVLwALvvIG4yX75QJrxjCpcWkzkVRB0BwwYlA==" + }, + "unbox-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz", + "integrity": "sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "function-bind": "^1.1.1", + "has-bigints": "^1.0.1", + "has-symbols": "^1.0.2", + "which-boxed-primitive": "^1.0.2" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + } } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", "dev": true, - "optional": true + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true }, "utf-8-validate": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.2.tgz", - "integrity": "sha512-SwV++i2gTD5qh2XqaPzBnNX88N6HdyhQrNNRykvcS0QKvItV9u3vPEJr+X5Hhfb1JC0r0e1alL0iB09rY8+nmw==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", + "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", "optional": true, "requires": { - "node-gyp-build": "~3.7.0" + "node-gyp-build": "^4.3.0" } }, "util": { @@ -8258,14 +5243,14 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "v8flags": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", - "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", "dev": true, "requires": { "homedir-polyfill": "^1.0.1" @@ -8297,74 +5282,49 @@ "minimalistic-assert": "^1.0.0" } }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "requires": { + "defaults": "^1.0.3" + } + }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } }, "which-pm-runs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", - "optional": true + "dev": true }, "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", "dev": true, "requires": { - "string-width": "^2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "string-width": "^1.0.2 || 2 || 3 || 4" } }, "word-wrap": { @@ -8373,44 +5333,20 @@ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", "dev": true }, + "workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "dev": true + }, "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } } }, "wrappy": { @@ -8418,32 +5354,15 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, "ws": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz", - "integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==" - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yallist": { "version": "3.1.1", @@ -8451,220 +5370,41 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.2.1.tgz", + "integrity": "sha512-XfR8du6ua4K6uLGm5S6fA+FIJom/MdJcFNVY8geLlp2v8GYbOXD4EB1tPNZsRn4vBzKGMgb5DRZMeWuFc2GO8Q==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - } - } + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" } }, "yargs-parser": { - "version": "18.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz", - "integrity": "sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" }, "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } - }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" - } - }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/package.json b/package.json index 27ed740e..dcc626e0 100644 --- a/package.json +++ b/package.json @@ -1,58 +1,86 @@ { "name": "bgpalerter", - "version": "1.24.0", - "description": "", - "main": "index.js", + "version": "1.28.4", + "description": "Software to monitor streams of BGP data. Pre-configured for real-time detection of visibility loss, RPKI invalid announcements, hijacks, and more.", + "author": { + "name": "Massimo Candela", + "url": "https://massimocandela.com" + }, + "license": "BSD-3-Clause", + "main": "src/worker.js", "bin": "index.js", "repository": { "type": "git", - "url": "git+https://github.com/massimocandela/rpki-validator.git" + "url": "git+https://github.com/nttgin/BGPalerter.git" }, "scripts": { "babel": "./node_modules/.bin/babel", - "test": "./node_modules/.bin/mocha --exit tests --require @babel/register", + "test": "npm run test-core && npm run test-generate && npm run test-reports && npm run test-rpki && npm run test-neighbor", + "test-core": "rm -rf volumetests/ && ./node_modules/.bin/mocha --exit tests/*.js --require @babel/register && rm -rf volumetests/", + "test-reports": "./node_modules/.bin/mocha --exit tests/reports_tests/testReportSyslog.js --require @babel/register && ./node_modules/.bin/mocha --exit tests/reports_tests/testsReportHttp.js --require @babel/register", + "test-proxy": "./node_modules/.bin/mocha --exit tests/proxy_tests/*.js --require @babel/register", + "test-generate": "./node_modules/.bin/mocha --exit tests/generate_tests/*.js --require @babel/register", + "test-kafka": "./node_modules/.bin/mocha --exit tests/kafka_tests/*.js --require @babel/register", + "test-neighbor": "./node_modules/.bin/mocha --exit tests/neighbor_tests/*.js --require @babel/register", + "test-npm": "./node_modules/.bin/mocha --exit tests/npm_tests/*.js --require @babel/register", + "test-dump": "rm -rf volumetests/ && ./node_modules/.bin/mocha --exit tests/dump_tests/*.js --require @babel/register", + "test-rpki": "./node_modules/.bin/mocha --exit tests/rpki_tests/tests.default.js --require @babel/register && ./node_modules/.bin/mocha --exit tests/rpki_tests/tests.external.js --require @babel/register && ./node_modules/.bin/mocha --exit tests/rpki_tests/tests.external-missing-roas.js --require @babel/register && rm -f -R .cache/ && ./node_modules/.bin/mocha --exit tests/rpki_tests/tests.external-roas.js --require @babel/register && ./node_modules/.bin/mocha --exit tests/rpki_tests/tests.api.js --require @babel/register", "build": "./build.sh", - "watch-and-serve": "nodemon -e yml,js,json,txt --inspect --exec babel-node index.js", + "compile": "rm -rf dist/ && ./node_modules/.bin/babel index.js -d dist && ./node_modules/.bin/babel src -d dist/src && cp package.json dist/package.json && cp README.md dist/README.md && cp .npm* dist/", "serve": "babel-node index.js", "inspect": "node --inspect --require @babel/register index.js", "update": "git update-index --assume-unchanged config.yml && git update-index --assume-unchanged prefixes.yml && git pull", "generate-prefixes": "babel-node index.js generate" }, - "author": "Massimo Candela", - "license": "ISC", + "keywords": [ + "BGP", + "monitoring", + "rpki", + "network", + "internet", + "real-time", + "hijack", + "detection", + "measurements" + ], "devDependencies": { - "@babel/cli": "^7.8.4", - "@babel/core": "^7.8.7", - "@babel/node": "^7.8.7", - "@babel/plugin-proposal-class-properties": "^7.8.3", - "@babel/plugin-proposal-object-rest-spread": "^7.8.3", - "@babel/preset-env": "^7.8.7", - "chai": "^4.2.0", + "@babel/cli": "^7.15.7", + "@babel/core": "^7.15.8", + "@babel/node": "^7.15.8", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-object-rest-spread": "^7.15.6", + "@babel/preset-env": "^7.15.8", + "chai": "^4.3.4", "chai-subset": "^1.6.0", - "mocha": "^7.1.1", - "pkg": "^4.4.4", - "nodemon": "^2.0.1", - "read-last-lines": "^1.7.2" + "mocha": "^9.1.3", + "pkg": "^5.3.3", + "read-last-lines": "^1.8.0", + "syslogd": "^1.1.2" }, "dependencies": { - "@sentry/node": "^5.14.2", - "axios": "^0.19.2", + "@sentry/node": "^6.13.3", + "axios": "^0.23.0", "batch-promises": "^0.0.3", - "brembo": "^2.0.3", - "inquirer": "^7.1.0", - "ip-address": "^6.2.0", - "ip-sub": "^1.0.6", - "js-yaml": "^3.13.1", - "kafka-node": "^5.0.0", - "minimist": "^1.2.5", - "nodemailer": "^6.4.5", + "brembo": "^2.0.6", + "deepmerge": "^4.2.2", + "fast-file-logger": "^1.1.1", + "https-proxy-agent": "^5.0.0", + "inquirer": "^8.2.0", + "ip-sub": "^1.1.2", + "js-yaml": "^4.1.0", + "kafkajs": "^1.15.0", + "md5": "^2.3.0", + "moment": "^2.29.1", + "node-cleanup": "^2.1.2", + "nodemailer": "^6.7.0", "path": "^0.12.7", - "restify": "^8.5.1", - "rpki-validator": "^1.0.13", - "semver": "^7.1.3", + "restify": "^8.6.0", + "rpki-validator": "^2.7.4", + "semver": "^7.3.5", "syslog-client": "^1.1.1", - "ws": "^7.2.3", - "yargs": "^15.3.1" + "uuid": "^8.3.2", + "ws": "^8.2.3", + "yargs": "^17.2.1" }, "pkg": { "scripts": [ @@ -66,11 +94,11 @@ "./bin/config.yml" ], "targets": [ - "node12" + "node14" ] }, "optionalDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "bufferutil": "^4.0.5", + "utf-8-validate": "^5.0.7" } } diff --git a/prefixes.yml.example b/prefixes.yml.example index 7d346561..fb98ba6e 100644 --- a/prefixes.yml.example +++ b/prefixes.yml.example @@ -1,17 +1,17 @@ 165.254.225.0/24: - description: Job + description: a description about this prefix asn: 15562 ignoreMorespecifics: false ignore: false 165.254.255.0/24: - description: Job + description: the description will be reported in the alerts asn: 15562 ignoreMorespecifics: false ignore: false 192.147.168.0/24: - description: Job + description: descriptions make alerts easier to read asn: 15562 ignoreMorespecifics: false ignore: false diff --git a/setup_build_environment.sh b/setup_build_environment.sh index bf52a926..5cd4737e 100755 --- a/setup_build_environment.sh +++ b/setup_build_environment.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash apt-get update apt-get install -y curl sudo build-essential vim openssh-client npm gcc g++ make nodejs -curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - +curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - +sudo apt-get install -y nodejs diff --git a/src/config/config.js b/src/config/config.js new file mode 100644 index 00000000..0e7d892e --- /dev/null +++ b/src/config/config.js @@ -0,0 +1,158 @@ +import axios from "axios"; + +export default class Config { + static configVersion = 2; + constructor(params) { + this.default = { + configVersion: Config.configVersion, + environment: "production", + connectors: [ + { + file: "connectorRIS", + name: "ris", + params: { + carefulSubscription: true, + url: "ws://ris-live.ripe.net/v1/ws/", + perMessageDeflate: true, + subscription: { + moreSpecific: true, + type: "UPDATE", + host: null, + socketOptions: { + includeRaw: false + } + } + } + }, + // { + // file: "connectorRISDump", + // name: "dmp" + // } + ], + monitors: [ + { + file: "monitorHijack", + channel: "hijack", + name: "basic-hijack-detection", + params: { + thresholdMinPeers: 3 + } + }, + { + file: "monitorPath", + channel: "path", + name: "path-matching", + params: { + thresholdMinPeers: 1 + } + }, + { + file: "monitorNewPrefix", + channel: "newprefix", + name: "prefix-detection", + params: { + thresholdMinPeers: 3 + } + }, + { + file: "monitorVisibility", + channel: "visibility", + name: "withdrawal-detection", + params: { + thresholdMinPeers: 40 + } + }, + { + file: "monitorAS", + channel: "misconfiguration", + name: "asn-monitor", + params: { + thresholdMinPeers: 3 + } + }, + { + file: "monitorRPKI", + channel: "rpki", + name: "rpki-monitor", + params: { + thresholdMinPeers: 3, + checkUncovered: false, + checkDisappearing: false + } + }, + { + file: "monitorROAS", + channel: "rpki", + name: "rpki-diff", + params: { + enableDiffAlerts: true, + enableExpirationAlerts: true, + enableExpirationCheckTA: true, + enableDeletedCheckTA: true, + roaExpirationAlertHours: 2, + checkOnlyASns: true, + toleranceDeletedRoasTA: 20, + toleranceExpiredRoasTA: 20 + } + }, + { + file: "monitorPathNeighbors", + channel: "hijack", + name: "path-neighbors", + params: { + thresholdMinPeers: 3 + } + } + ], + reports: [ + { + file: "reportFile", + channels: ["hijack", "newprefix", "visibility", "path", "misconfiguration", "rpki"] + } + ], + notificationIntervalSeconds: 86400, + alarmOnlyOnce: false, + monitoredPrefixesFiles: ["prefixes.yml"], + persistStatus: true, + generatePrefixListEveryDays: 0, + logging: { + directory: "logs", + logRotatePattern: "YYYY-MM-DD", + maxRetainedFiles: 10, + maxFileSizeMB: 15, + compressOnRotation: false, + }, + rpki: { + vrpProvider: "ntt", + preCacheROAs: true, + refreshVrpListMinutes: 15 + }, + rest: { + host: "localhost", + port: 8011 + }, + checkForUpdatesAtBoot: true, + pidFile: "bgpalerter.pid", + fadeOffSeconds: 360, + checkFadeOffGroupsSeconds: 30 + }; + }; + + downloadDefault = () => { + return axios({ + url: 'https://raw.githubusercontent.com/nttgin/BGPalerter/main/config.yml.example', + method: 'GET', + responseType: 'blob', // important + }) + .then(response => response.data); + }; + + retrieve = () => { + throw new Error('The method retrieve must be implemented in the config connector'); + }; + + save = () => { + throw new Error('The method save must be implemented in the config connector'); + }; + +} \ No newline at end of file diff --git a/src/config/configYml.js b/src/config/configYml.js new file mode 100644 index 00000000..bc209f6b --- /dev/null +++ b/src/config/configYml.js @@ -0,0 +1,95 @@ +import Config from "./config"; +import yaml from "js-yaml"; +import fs from "fs"; +import path from "path"; + +export default class ConfigYml extends Config { + constructor(params) { + super(params); + this.configFile = global.EXTERNAL_CONFIG_FILE || + ((global.EXTERNAL_VOLUME_DIRECTORY) + ? global.EXTERNAL_VOLUME_DIRECTORY + 'config.yml' + : path.resolve(process.cwd(), 'config.yml')); + + this.groupsFile = global.EXTERNAL_GROUP_FILE; + + console.log("Loaded config:", this.configFile); + }; + + save = (config) => { + try { + fs.writeFileSync(this.configFile, yaml.dump(config)); + yaml.load(fs.readFileSync(this.configFile, 'utf8')); // Test readability and format + } catch (error) { + throw new Error("Cannot save the configuration in " + this.configFile); + } + }; + + retrieve = () => { + const ymlBasicConfig = yaml.dump(this.default); + + if (fs.existsSync(this.configFile)) { + try { + const config = yaml.load(fs.readFileSync(this.configFile, 'utf8')) || this.default; + this._readUserGroupsFiles(config); + + return config; + } catch (error) { + throw new Error("The file " + this.configFile + " is not valid yml: " + error.message.split(":")[0]); + } + } else { + console.log("Impossible to load config.yml. A default configuration file has been generated."); + + this.downloadDefault() + .then(data => { + fs.writeFileSync(this.configFile, data); + yaml.load(fs.readFileSync(this.configFile, 'utf8')); // Test readability and format + + this._readUserGroupsFiles(data); + }) + .catch(() => { + fs.writeFileSync(this.configFile, ymlBasicConfig); // Download failed, write simple default config + }); + + return this.default; + } + }; + + _readUserGroupsFiles = (config) => { + if (config.groupsFile) { + this.groupsFile = ((config.volume) + ? config.volume + config.groupsFile + : path.resolve(process.cwd(), config.groupsFile)); + + const userGroups = yaml.load(fs.readFileSync(this.groupsFile, 'utf8')); + + for (let report of config.reports) { + const name = report.file; + const groups = userGroups[name]; + if (userGroups[name]) { + report.params.userGroups = groups; + } + } + + fs.watchFile(this.groupsFile, () => { + if (this._watchPrefixFileTimer) { + clearTimeout(this._watchPrefixFileTimer) + } + this._watchPrefixFileTimer = setTimeout(() => { + const userGroups = yaml.load(fs.readFileSync(this.groupsFile, 'utf8')); + + for (let report of config.reports) { + const name = report.file; + const groups = userGroups[name]; + + if (userGroups[name]) { + report.params.userGroups = groups; + } + } + }, 5000); + }); + } + + }; + +} diff --git a/src/connectorFactory.js b/src/connectorFactory.js index 7a3a6628..3a279c42 100644 --- a/src/connectorFactory.js +++ b/src/connectorFactory.js @@ -56,82 +56,72 @@ export default class ConnectorFactory { } }; - connectConnectors = () => - new Promise((resolve, reject) => { - const connectors = this.getConnectors(); + _connectConnector = (connector) => { - if (connectors.length === 0) { - reject(new Error("No connections available")); + connector.onError(error => { + logger.log({ + level: 'error', + message: error + }); + }); - } else { - resolve(Promise.all(connectors - .map(connector => - new Promise((resolve, reject) => { + connector.onConnect(message => { + logger.log({ + level: 'info', + message: message + }); + }); - connector.onError(error => { - logger.log({ - level: 'error', - message: error - }); - }); + connector.onDisconnect(error => { + if (error) { + logger.log({ + level: 'error', + message: error + }); + } else { + logger.log({ + level: 'info', + message: connector.name + ' disconnected' + }); + } + }); - connector.onConnect(message => { - connector.connected = true; - logger.log({ - level: 'info', - message: message - }); - }); + return connector.connect() + .catch(error => { + // If not connected log the error and move on + if (error) { + logger.log({ + level: 'error', + message: error + }); + } + }); + }; - connector.onDisconnect(error => { - connector.connected = false; + connectConnectors = (params) => + new Promise((resolve, reject) => { + const connectors = this.getConnectors(); + if (connectors.length === 0) { + reject(new Error("No connections available")); + } else { + const calls = connectors + .map(connector => { + return this._connectConnector(connector) + .then(() => { + connector.subscribe(params); + }) + .catch((error) => { if (error) { logger.log({ level: 'error', message: error }); - } else { - logger.log({ - level: 'info', - message: connector.name + ' disconnected' - }); } - }); - + }) + }); - connector - .connect() - .then(() => { - connector.connected = true; - resolve(true); - }) - .catch((error) => { - if (error) { - env.logger.log({ - level: 'error', - message: error - }); - } - resolve(false); - }) - })))); + resolve(Promise.all(calls)); } }); - - subscribeConnectors = (params, callback) => - new Promise((resolve, reject) => { - - const connectors = this.getConnectors(); - - if (connectors.length === 0) { - reject(new Error("No connections available")); - } else { - const connectorList = connectors - .map(connector => connector.subscribe(params)); - - resolve(Promise.all(connectorList)); - } - - }) } \ No newline at end of file diff --git a/src/connectors/connector.js b/src/connectors/connector.js index 43600948..321b9fe0 100644 --- a/src/connectors/connector.js +++ b/src/connectors/connector.js @@ -31,6 +31,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import axios from "axios"; +import axiosEnrich from "../utils/axiosEnrich"; + export default class Connector { constructor(name, params, env){ @@ -44,6 +47,11 @@ export default class Connector { this.connectCallback = null; this.errorCallback = null; this.disconnectCallback = null; + + + this.axios = axiosEnrich(axios, + (!this.params.noProxy && env.agent) ? env.agent : null, + `${env.clientId}/${env.version}`); } connect = () => @@ -60,6 +68,7 @@ export default class Connector { }; _disconnect = (message) => { + this.connected = false; if (this.disconnectCallback) this.disconnectCallback(message); }; @@ -70,6 +79,7 @@ export default class Connector { }; _connect = (message) => { + this.connected = true; if (this.connectCallback) this.connectCallback(message); }; @@ -94,4 +104,7 @@ export default class Connector { this.disconnectCallback = callback; }; + disconnect = () => { + throw new Error('The method disconnect MUST be implemented'); + }; } \ No newline at end of file diff --git a/src/connectors/connectorFullThrottle.js b/src/connectors/connectorFullThrottle.js index f44110e4..bc666fb8 100644 --- a/src/connectors/connectorFullThrottle.js +++ b/src/connectors/connectorFullThrottle.js @@ -30,6 +30,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +// IMPORTANT: This Connector is just for stress tests during development. Please, ignore! + import Connector from "./connector"; import {AS, Path} from "../model"; @@ -119,17 +122,17 @@ export default class ConnectorFullThrottle extends Connector{ }); _startStream = () => { - setInterval(() => { - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); - this.updates.forEach(message => this._message(message)); + setInterval(() => { // just create a huge amount of useless messages + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); + this.updates.forEach(this._message); }, 2); }; diff --git a/src/connectors/connectorRIS.js b/src/connectors/connectorRIS.js index de63dae7..f1204b6e 100644 --- a/src/connectors/connectorRIS.js +++ b/src/connectors/connectorRIS.js @@ -30,113 +30,112 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import WebSocket from "ws"; +import WebSocket from "../utils/WebSocket"; import Connector from "./connector"; import { AS, Path } from "../model"; import brembo from "brembo"; import ipUtils from "ip-sub"; -export default class ConnectorRIS extends Connector{ +export default class ConnectorRIS extends Connector { constructor(name, params, env) { super(name, params, env); this.ws = null; + this.environment = env.config.environment; this.subscription = null; - this.pingInterval = 5000; - setInterval(this._ping, this.pingInterval); + this.agent = env.agent; + this.subscribed = {}; + this.canaryBeacons = {}; + this.clientId = env.clientId; + this.instanceId = env.instanceId; this.url = brembo.build(this.params.url, { - path: [], params: { - client: env.clientId + client_version: env.version, + client: this.clientId, + instance: this.instanceId } }); - }; - - _ping = () => { - if (this.ws) { - try { - this.ws.ping(); - } catch (e) { - // Nothing to do here - } + if (this.environment !== "research") { // The canary feature may impact performance if you are planning to get all the possible updates of RIS + this._startCanaryInterval = setInterval(this._startCanary, 60000); } }; - _pingReceived = () => { - if (this.closeTimeout) { - clearTimeout(this.closeTimeout); + _openConnect = (resolve, data) => { + resolve(true); + this._connect(`${this.name} connector connected (instance:${this.instanceId} connection:${data.connection})`); + if (this.subscription) { + this.subscribe(this.subscription); } - this.closeTimeout = setTimeout(this._close, this.pingInterval * 3); }; - _openConnect = (resolve) => { - if (this.connectionFailedTimer) { - clearTimeout(this.connectionFailedTimer); + _messageToJson = (message) => { + const messageObj = JSON.parse(message); + if (this.environment !== "research") { + this._checkCanary(); } - resolve(true); - this._connect(this.name + ' connector connected'); + this._message(messageObj); }; - _messageToJson = (message) => { - this._message(JSON.parse(message)); + _appendListeners = (resolve, reject) => { + this.ws.on('message', this._messageToJson); + this.ws.on('close', (error) => { + + if (this.connected) { + this._disconnect("RIPE RIS disconnected (error: " + error + "). Read more at https://github.com/nttgin/BGPalerter/blob/main/docs/ris-disconnections.md"); + } else { + this._disconnect("It was not possible to establish a connection with RIPE RIS"); + reject(); + } + }); + this.ws.on('error', error => { + this._error(`${this.name} ${error.message} (instance:${this.instanceId} connection:${error.connection})`); + }); + this.ws.on('open', data => this._openConnect(resolve, data)); }; connect = () => new Promise((resolve, reject) => { try { - this.ws = new WebSocket(this.url, { + if (this.ws) { + this.ws.disconnect(); + } + const wsOptions = { perMessageDeflate: this.params.perMessageDeflate - }); + }; - this.connectionFailedTimer = setTimeout(() => { - this.ws.terminate(); - this.ws.removeAllListeners(); - this.connect(); - reject("RIPE RIS connection failed. Trying again..."); - }, 20000); - - this.ws.on('message', this._messageToJson); - this.ws.on('close', (error) => { - if (this.connectionFailedTimer) { - clearTimeout(this.connectionFailedTimer); + if (this.params.authorizationHeader){ + wsOptions.headers = { + Authorization: this.params.authorizationHeader } - this._close("RIPE RIS disconnected (error: " + error + "). Read more at https://github.com/nttgin/BGPalerter/blob/master/docs/ris-disconnections.md"); - }); - this.ws.on('error', this._error); - this.ws.on('open', this._openConnect.bind(null, resolve)); - this.ws.on('ping', this._pingReceived); + } + + if (!this.params.noProxy && this.agent) { + wsOptions.agent = this.agent; + } + + this.ws = new WebSocket(this.url, wsOptions); + this.ws.connect(); + this._appendListeners(resolve, reject); } catch(error) { this._error(error); - resolve(false); + reject(error); } }); - _reconnect = () => { - this.connect() - .then(this.subscribe.bind(null, this.subscription)); - }; - - _close = (error) => { - this._disconnect(error); - try { - this.ws.terminate(); - this.ws.removeAllListeners(); - } catch(e) { - // Nothing to do here + disconnect = () => { + if (this.ws) { + this._disconnect(`${this.name} disconnected`); } - // Reconnect - setTimeout(this._reconnect, 10000); }; _subscribeToAll = (input) => { - this.ws.send(JSON.stringify({ + return this.ws.send(JSON.stringify({ type: "ris_subscribe", data: this.params.subscription })); - }; _optimizedPathMatch = (regex) => { @@ -162,76 +161,184 @@ export default class ConnectorRIS extends Connector{ _subscribeToPrefixes = (input) => { const monitoredPrefixes = input.getMonitoredLessSpecifics(); - const params = JSON.parse(JSON.stringify(this.params.subscription)); - - if (monitoredPrefixes - .filter( - i => (ipUtils._isEqualPrefix(i.prefix, '0:0:0:0:0:0:0:0/0') || ipUtils._isEqualPrefix(i.prefix,'0.0.0.0/0')) - ).length === 2) { + if (monitoredPrefixes.filter(i => (ipUtils.isEqualPrefix(i.prefix, '0:0:0:0:0:0:0:0/0') || ipUtils.isEqualPrefix(i.prefix,'0.0.0.0/0'))).length === 2) { delete params.prefix; - console.log("Monitoring everything"); - this.ws.send(JSON.stringify({ + if (!this.subscribed["everything"]) { + console.log("Monitoring everything"); + this.subscribed["everything"] = true; + } + + return this.ws.send(JSON.stringify({ type: "ris_subscribe", data: params })); } else { - for (let p of monitoredPrefixes) { + return Promise.all(monitoredPrefixes.map(p => { + if (!this.subscribed[p.prefix]) { + console.log("Monitoring", p.prefix); + this.subscribed[p.prefix] = true; + } - console.log("Monitoring", p.prefix); params.prefix = p.prefix; - this.ws.send(JSON.stringify({ + return this.ws.send(JSON.stringify({ type: "ris_subscribe", data: params })); - } + })); + } }; _subscribeToASns = (input) => { - const monitoredASns = input.getMonitoredASns().map(i => i.asn); - + const monitoredASns = input.getMonitoredASns(); const params = JSON.parse(JSON.stringify(this.params.subscription)); - for (let asn of monitoredASns){ - console.log("Monitoring AS", asn.getValue()); - params.path = '' + asn.getValue() + '$'; + return Promise.all(monitoredASns + .map(asn => { + const asnString = asn.asn.getValue(); - this.ws.send(JSON.stringify({ - type: "ris_subscribe", - data: params + if (!this.subscribed[asnString]) { + console.log(`Monitoring AS${asnString}`); + this.subscribed[asnString] = true; + } + + params.path = `${asnString}\$`; + + return this.ws.send(JSON.stringify({ + type: "ris_subscribe", + data: params + })); })); - } }; + _startCanary = () => { + if (this.connected) { + const beacons = { + v4: ["84.205.64.0/24", "84.205.65.0/24", "84.205.67.0/24", "84.205.68.0/24", "84.205.69.0/24", + "84.205.70.0/24", "84.205.71.0/24", "84.205.74.0/24", "84.205.75.0/24", "84.205.76.0/24", "84.205.77.0/24", + "84.205.78.0/24", "84.205.79.0/24", "84.205.73.0/24", "84.205.82.0/24", "93.175.149.0/24", "93.175.151.0/24", + "93.175.153.0/24"], + v6: ["2001:7FB:FE00::/48", "2001:7FB:FE01::/48", "2001:7FB:FE03::/48", "2001:7FB:FE04::/48", + "2001:7FB:FE05::/48", "2001:7FB:FE06::/48", "2001:7FB:FE07::/48", "2001:7FB:FE0A::/48", "2001:7FB:FE0B::/48", + "2001:7FB:FE0C::/48", "2001:7FB:FE0D::/48", "2001:7FB:FE0E::/48", "2001:7FB:FE0F::/48", "2001:7FB:FE10::/48", + "2001:7FB:FE12::/48", "2001:7FB:FE13::/48", "2001:7FB:FE14::/48", "2001:7FB:FE15::/48", "2001:7FB:FE16::/48", + "2001:7FB:FE17::/48", "2001:7FB:FE18::/48"] + }; + + const selected = [ + ...beacons.v4.sort(() => .5 - Math.random()).slice(0, 3), + ...beacons.v6.sort(() => .5 - Math.random()).slice(0, 3) + ]; - subscribe = (input) => - new Promise((resolve, reject) => { - this.subscription = input; - try { - input.onChange(() => { - this._close(); + Promise.all(selected + .map(prefix => { + this.canaryBeacons[prefix] = true; + return this.ws.send(JSON.stringify({ + type: "ris_subscribe", + data: { + moreSpecific: false, + lessSpecific: false, + prefix, + type: "UPDATE", + socketOptions: { + includeRaw: false, + acknowledge: false + } + } + })); + })) + .then(() => { + this._checkCanary(); + clearInterval(this._startCanaryInterval); + }) + .catch(() => { + this.logger.log({ + level: 'error', + message: "Failed to subscribe to beacons" + }); }); + } + }; - if (this.params.carefulSubscription) { - this._subscribeToPrefixes(input); - this._subscribeToASns(input); - } else { - this._subscribeToAll(input); + _checkCanary = () => { + clearTimeout(this._timerCheckCanary); + if (!this.connected) { + this.logger.log({ + level: 'error', + message: "RIS connected again, the streaming session is working properly" + }); + } + this.connected = true; + this._timerCheckCanary = setTimeout(() => { + if (this.connected) { + this.connected = false; + this.logger.log({ + level: 'error', + message: "RIS has been silent for too long, probably there is something wrong" + }); + } + if (this.ws) { + this.ws.connect(); + } + }, 3600 * 1000 * 4.5); // every 4.5 hours + }; + + _onInputChange = (input) => { + this.connect() + .then(() => this.subscribe(input)) + .then(() => { + this.logger.log({ + level: 'info', + message: "Prefix rules reloaded" + }); + }) + .catch(error => { + if (error) { + this.logger.log({ + level: 'error', + message: error + }); } + }); + }; - resolve(true); - } catch(error) { - this._error(error); - resolve(false); + onInputChange = (input) => { + input.onChange(() => { + // An external process may write bits of the file and trigger the reload multiple times + // the timer is reset on each change and it triggers the reload 2 sec after the process stops writing. + if (this._timeoutFileChange) { + clearTimeout(this._timeoutFileChange); } + this._timeoutFileChange = setTimeout(() => { + this._onInputChange(input); + }, 5000); }); + }; + + subscribe = (input) => { + this.subscription = input; + + return (this.params.carefulSubscription + ? Promise.all([this._subscribeToPrefixes(input), this._subscribeToASns(input)]) + : this._subscribeToAll(input)) + .then(() => { + this.onInputChange(input); + + return true; + }) + .catch(error => { + this._error(error); + + return false; + }); + } static transform = (message) => { if (message.type === 'ris_message') { @@ -240,11 +347,12 @@ export default class ConnectorRIS extends Connector{ const components = []; const announcements = message["announcements"] || []; const aggregator = message["aggregator"] || null; - const withdrawals = message["withdrawals"] || []; + const withdrawals = (message["withdrawals"] || []).filter(prefix => ipUtils.isValidPrefix(prefix)); const peer = message["peer"]; const communities = message["community"] || []; const timestamp = message["timestamp"] * 1000; let path, originAS; + if (message["path"] && message["path"].length) { path = new Path(message["path"].map(i => new AS(i))); originAS = path.getLast(); @@ -253,23 +361,28 @@ export default class ConnectorRIS extends Connector{ originAS = null; } - for (let announcement of announcements) { - const nextHop = announcement["next_hop"]; - const prefixes = announcement["prefixes"] || []; - - for (let prefix of prefixes) { - - components.push({ - type: "announcement", - prefix, - peer, - path, - originAS, - nextHop, - aggregator, - timestamp, - communities - }) + if (originAS && path.length()) { + for (let announcement of announcements) { + const nextHop = announcement["next_hop"]; + + if (ipUtils.isValidIP(nextHop)) { + const prefixes = (announcement["prefixes"] || []) + .filter(prefix => ipUtils.isValidPrefix(prefix)); + + for (let prefix of prefixes) { + components.push({ + type: "announcement", + prefix, + peer, + path, + originAS, + nextHop, + aggregator, + timestamp, + communities + }); + } + } } } @@ -279,16 +392,15 @@ export default class ConnectorRIS extends Connector{ prefix, peer, timestamp - }) + }); } return components; } catch (error) { - throw new Error(`Error during tranform (${this.name}): ` + error.message); + throw new Error(`Error during transform (${this.name}): ` + error.message); } } else if (message.type === 'ris_error') { throw new Error("Error from RIS: " + message.data.message); } - } }; diff --git a/src/connectors/connectorRISDump.js b/src/connectors/connectorRISDump.js new file mode 100644 index 00000000..5a757bcf --- /dev/null +++ b/src/connectors/connectorRISDump.js @@ -0,0 +1,171 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import Connector from "./connector"; +import batchPromises from "batch-promises"; +import brembo from "brembo"; +import moment from "moment"; +import {AS, Path} from "../model"; + +export default class ConnectorRISDump extends Connector { + + constructor(name, params, env) { + super(name, params, env); + this.withdrawNotVisible = this.params.withdrawNotVisible || false; + this.storage = env.storage; + this.lastRun = null; + + if (this.storage) { + this.storage + .get(`run-${this.name}`) + .then(date => { + if (date && !isNaN(date)) { + this.lastRun = moment.unix(parseInt(date)).utc(); + } + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + } + }; + + _shouldDownloadDump = () => { + return !this.lastRun || this.lastRun.diff(moment(), 'hours') > 2; + }; + + connect = () => + new Promise((resolve, reject) => { + resolve(true); + }); + + _loadResource = (resource) => { + const stop = moment().subtract(2, "hours").utc(); + const url = brembo.build("https://stat.ripe.net/data/bgplay/data.json", { + params: { + resource, + rrcs: "0,11,13,14,15,16", + "unix_timestamps": "TRUE", + starttime: moment(stop).subtract(2, "minutes").unix(), + stoptime: stop.unix() + } + }); + + return this.axios({ + responseType: "json", + url + }) + .then(data => { + if (data && data.data && data.data.data && data.data.data.initial_state) { + const dump = data.data.data.initial_state; + const sent = {}; + + for (let entry of dump) { + const path = new Path((entry.path|| []).map(i => new AS(i))); + sent[entry.target_prefix] = true; + this._message({ + type: "announcement", + prefix: entry.target_prefix, + peer: entry.source_id.split("-")[1], + path, + originAS: path.getLast(), + nextHop: null, + aggregator: null, + timestamp: stop.valueOf(), + communities: entry.community + }); + } + + if (this.withdrawNotVisible) { // This feature is not reachable for now + for (let entry of dump) { + if (!sent[entry.target_prefix]) { + this._message({ + type: "withdrawal", + prefix: entry.target_prefix, + peer: null, + timestamp: stop.valueOf() + }); + } + } + } + } + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: `Cannot download historic RIS data ${error}` + }); + }); + }; + + _subscribe = (input) => { + if (this._shouldDownloadDump()) { + const asns = input.getMonitoredASns().map(i => i.asn); + const prefixes = input.getMonitoredPrefixes().filter(i => !asns.includes(i.asn)).map(i => i.prefix); + const dumps = [...asns, ...prefixes]; + + if (dumps.length) { + this.storage + .set(`run-${this.name}`, moment.utc().unix()) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + + return batchPromises(1, dumps, this._loadResource); + } + } + }; + + subscribe = (input) => { + this._subscribe(input); + + input.onChange(() => { + if (this._timeoutFileChange) { + clearTimeout(this._timeoutFileChange); + } + this._timeoutFileChange = setTimeout(() => { + this._subscribe(input); + }, 2000); + }); + + return Promise.resolve(); + }; + + static transform = (message) => { + return [ message ]; + }; +}; diff --git a/src/connectors/connectorSwUpdates.js b/src/connectors/connectorSwUpdates.js index 8d5539b8..090d259d 100644 --- a/src/connectors/connectorSwUpdates.js +++ b/src/connectors/connectorSwUpdates.js @@ -31,7 +31,6 @@ */ import Connector from "./connector"; -import axios from "axios"; import semver from "semver"; export default class ConnectorSwUpdates extends Connector{ @@ -46,11 +45,12 @@ export default class ConnectorSwUpdates extends Connector{ }); _checkForUpdates = () => { - return axios({ + return this.axios({ responseType: "json", - url: "https://raw.githubusercontent.com/nttgin/BGPalerter/master/package.json" + url: "https://raw.githubusercontent.com/nttgin/BGPalerter/main/package.json" }) .then(data => { + if (data && data.data && data.data.version && semver.gt(data.data.version, this.version)) { this._message({ type: "software-update", @@ -70,23 +70,8 @@ export default class ConnectorSwUpdates extends Connector{ subscribe = (input) => new Promise((resolve, reject) => { - // The functional test cases require this to be logged to stdout, because STDOUT is captured and - // parse to verify the BGPAlerter configuration. This is distinct from the log stream that generates - // ./log/error... ./log/reports... because these files are not cleared after each BGPAlerter restart - if (this.config.checkForUpdates) { - // The function test cases require this to be logged to stdout, because STDOUT is captured and parse to - // verify the configuration for restart of the BGPAlerter daemon - console.log("Software updates enabled"); - if (this.config.checkForUpdatesAtBoot){ - this._checkForUpdates(); - } - - setInterval(this._checkForUpdates, this.config.checkForUpdatesInterval); // Check every 5 days - } - else { - // The function test cases require this to be logged to stdout, because STDOUT is captured and parse to - // verify the configuration for restart of the BGPAlerter daemon - console.log("Software updates disabled"); + if (this.config.checkForUpdatesAtBoot){ + setTimeout(this._checkForUpdates, 20000000); // Check after 20 seconds from boot } resolve(true); }); diff --git a/src/connectors/connectorTest.js b/src/connectors/connectorTest.js index 6a3c337b..7282260b 100644 --- a/src/connectors/connectorTest.js +++ b/src/connectors/connectorTest.js @@ -30,25 +30,28 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +// IMPORTANT: This Connector is used by the automated tests. Please, ignore! + import Connector from "./connector"; import {AS, Path} from "../model"; +import ipUtils from "ip-sub"; -export default class ConnectorTest extends Connector{ - - static isTest = true; +export default class ConnectorTest extends Connector { constructor(name, params, env) { super(name, params, env); - this.pubSub.subscribe("test-type", (type, message) => { + this.pubSub.subscribe("test-type", (message, type) => { clearInterval(this.timer); this.subscribe({type: message}); }); + + this.subscribe({type: params.testType}); } - connect = () => - new Promise((resolve, reject) => { - resolve(true); - }); + connect = () => { + this._connect("Test connector connected"); + return Promise.resolve(); + }; _fadeOffTest = (fade) => { const updates = [ @@ -91,354 +94,508 @@ export default class ConnectorTest extends Connector{ }, (this.config.fadeOffSeconds + ((fade) ? -4 : 4)) * 1000); // depending on "fade" it goes in our out of the fading period }; - subscribe = (params) => - new Promise((resolve, reject) => { - resolve(true); + subscribe = (params) => { + const type = params.type || this.params.testType; - const type = params.type || this.params.testType; + let updates; - let updates; + switch (type) { + case "fade-off": + return this._fadeOffTest(false); - switch (type) { - case "fade-off": - return this._fadeOffTest(false); + case "fade-in": + return this._fadeOffTest(true); - case "fade-in": - return this._fadeOffTest(true); + case "hijack": + updates = [ + { + data: { + announcements: [{ // RPKI valid announcement, no alert should be triggered (issue #358) + prefixes: ["193.0.0.0/21"], + next_hop: "1.2.3.4" + }], + peer: "1.2.3.5", + path: [1, 2, 3, 3333] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["175.254.205.0/25", "170.254.205.0/25"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 4321] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["165.254.255.0/25"], + next_hop: "124.0.0.2" + }], + peer: "124.0.0.2", + path: [1, 2, 3, [4, 15562]] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a00:5884:ffff::/48"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 208585] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a00:5884::/32"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, [204092, 45]] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a00:5884::/32"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, [15563]] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a00:5884::/32"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 204092] + }, + type: "ris_message" + } + ]; + break; - case "hijack": - updates = [ - { - data: { - announcements: [{ - prefixes: ["175.254.205.0/25", "170.254.205.0/25"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 4321] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["165.254.255.0/25"], - next_hop: "124.0.0.2" - }], - peer: "124.0.0.2", - path: [1, 2, 3, [4, 15562]] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a00:5884:ffff::/48"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 208585] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a00:5884::/32"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, [204092, 45]] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a00:5884::/32"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, [15563]] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a00:5884::/32"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 204092] - }, - type: "ris_message" - } - ]; - break; + case "newprefix": + updates = [ + { + data: { + announcements: [{ + prefixes: ["165.254.255.0/25"], + next_hop: "124.0.0.2" + }], + peer: "124.0.0.2", + path: [1, 2, 3, 15562] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a00:5884::/32"], + next_hop: "124.0.0.2" + }], + peer: "124.0.0.2", + path: [1, 2, 3, [45]] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a00:5884:ffff::/48"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 204092] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a0e:f40::/32"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 204092] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a0e:240::/32"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 1345] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["175.254.205.0/25", "170.254.205.0/25"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 1234] + }, + type: "ris_message" + } + ]; + break; - case "newprefix": - updates = [ - { - data: { - announcements: [{ - prefixes: ["165.254.255.0/25"], - next_hop: "124.0.0.2" - }], - peer: "124.0.0.2", - path: [1, 2, 3, 15562] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a00:5884::/32"], - next_hop: "124.0.0.2" - }], - peer: "124.0.0.2", - path: [1, 2, 3, [45]] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a00:5884:ffff::/48"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 204092] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a0e:f40::/32"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 204092] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a0e:240::/32"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 1345] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["175.254.205.0/25", "170.254.205.0/25"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 1234] - }, - type: "ris_message" - } - ]; - break; + case "visibility": + updates = [ + { + data: { + withdrawals: ["165.254.225.0/24"], + peer: "124.0.0.2" + }, + type: "ris_message" + }, + { + data: { + withdrawals: ["2a00:5884::/32"], + peer: "124.0.0.2" + }, + type: "ris_message" + }, + { + data: { + withdrawals: ["2a00:5884:ffff::/48"], + peer: "124.0.0.2" + }, + type: "ris_message" + }, + { + data: { + withdrawals: ["2a0e:f40::/32"], + peer: "124.0.0.2" + }, + type: "ris_message" + }, + { + data: { + withdrawals: ["2001:db8:123::/48"], + peer: "124.0.0.2" + }, + type: "ris_message" + } + ]; + break; - case "visibility": - updates = [ - { - data: { - withdrawals: ["165.254.225.0/24"], - peer: "124.0.0.2" - }, - type: "ris_message" - }, - { - data: { - withdrawals: ["2a00:5884::/32"], - peer: "124.0.0.2" - }, - type: "ris_message" - }, - { - data: { - withdrawals: ["2a00:5884:ffff::/48"], - peer: "124.0.0.2" - }, - type: "ris_message" - }, - { - data: { - withdrawals: ["2a0e:f40::/32"], - peer: "124.0.0.2" - }, - type: "ris_message" - }, - { - data: { - withdrawals: ["2001:db8:123::/48"], - peer: "124.0.0.2" - }, - type: "ris_message" - } - ]; - break; + case "path": + updates = [ + { + data: { + announcements: [{ + prefixes: ["94.5.4.3/22", "98.5.4.3/22", "99.5.4.3/22"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 4321, 5060, 2914] + }, + type: "ris_message" + } + ]; + break; - case "path": - updates = [ - { - data: { - announcements: [{ - prefixes: ["94.5.4.3/22", "98.5.4.3/22", "99.5.4.3/22"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 4321, 5060, 2914] - }, - type: "ris_message" - } - ]; - break; + case "misconfiguration": + updates = [ + { + data: { + announcements: [{ + prefixes: ["2.2.2.3/22"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.4", + path: [1, 2, 3, 4321, 5060, 2914] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2001:db8:123::/48", "2001:db8:123::/49"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.10", + path: [1, 2, 3, 4321, 65000] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2001:db8:123::/48", "2001:db8:123::/49"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.9", + path: [1, 2, 3, 4321, 65000] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2001:db8:123::/48", "2001:db8:123::/49"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 4321, 65000] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2.2.2.5/22", "2001:db9:123::/49"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 4321, 5060, 2914] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2.2.2.3/22", "2001:db9:123::/49"], + next_hop: "124.0.0.5" + }], + peer: "124.0.0.6", + path: [1, 2, 3, 4321, 5060, 2914] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["2a0e:240::/32"], + next_hop: "124.0.0.5" + }], + peer: "124.0.0.6", + path: [1, 2, 3, 4321, 5060, 2914] + }, + type: "ris_message" + } + ]; + break; - case "misconfiguration": - updates = [ - { - data: { - announcements: [{ - prefixes: ["2.2.2.3/22"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.4", - path: [1, 2, 3, 4321, 5060, 2914] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2001:db8:123::/48", "2001:db8:123::/49"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.10", - path: [1, 2, 3, 4321, 65000] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2001:db8:123::/48", "2001:db8:123::/49"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.9", - path: [1, 2, 3, 4321, 65000] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2001:db8:123::/48", "2001:db8:123::/49"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 4321, 65000] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2.2.2.5/22", "2001:db9:123::/49"], - next_hop: "124.0.0.3" - }], - peer: "124.0.0.3", - path: [1, 2, 3, 4321, 5060, 2914] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2.2.2.3/22", "2001:db9:123::/49"], - next_hop: "124.0.0.5" - }], - peer: "124.0.0.6", - path: [1, 2, 3, 4321, 5060, 2914] - }, - type: "ris_message" - }, - { - data: { - announcements: [{ - prefixes: ["2a0e:240::/32"], - next_hop: "124.0.0.5" - }], - peer: "124.0.0.6", - path: [1, 2, 3, 4321, 5060, 2914] - }, - type: "ris_message" - } - ]; - break; - default: - return; - } + case "rpki": + updates = [ + { + data: { + announcements: [{ + prefixes: ["82.112.100.0/24"], // Valid + next_hop: "124.0.0.3" + }], + peer: "124.0.0.4", + path: [1, 2, 3, 4321, 2914] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["8.8.8.8/22"], // Not covered + next_hop: "124.0.0.3" + }], + peer: "124.0.0.4", + path: [1, 2, 3, 4321, 5060, 2914] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["103.21.244.0/24"], // Invalid + next_hop: "124.0.0.3" + }], + peer: "124.0.0.4", + path: [1, 2, 3, 4321, 13335] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["175.254.205.0/25", "170.254.205.0/25"], + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [1, 2, 3, 4321] + }, + type: "ris_message" + } + ]; + break; - this.timer = setInterval(() => { - updates.forEach(update => { - this._message(update); - if (type === 'visibility') { - let peer = update.data.peer.split('.'); - peer[3] = Math.min(parseInt(peer[3]) + 1, 254); - update.data.peer = peer.join("."); + case "path-neighbors": + updates = [ + { + data: { + announcements: [{ + prefixes: ["9.5.4.3/22"], // Path not ok but prefix not monitored + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [98, 99, 100, 101, 106] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["99.5.4.3/22"], // Monitored but path ok + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [98, 99, 100, 101, 104] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["99.5.4.3/22"], // Monitored, path with wrong downstream + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [98, 99, 100, 101, 106] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["99.5.4.3/22"], // Monitored, path with wrong upstream + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [98, 99, 30, 101, 104] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["99.5.4.3/22"], // Monitored, path with empty downstream ok + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [98, 99, 80] + }, + type: "ris_message" + }, + { + data: { + announcements: [{ + prefixes: ["99.5.4.3/22"], // Monitored, path with empty downstream not ok + next_hop: "124.0.0.3" + }], + peer: "124.0.0.3", + path: [98, 99, 80, 100] + }, + type: "ris_message" } - }); - }, 1000); + ]; + break; - }); + default: + return; + } + + this.timer = setInterval(() => { + updates.forEach(update => { + this._message(update); + if (type === 'visibility') { + let peer = update.data.peer.split('.'); + peer[3] = Math.min(parseInt(peer[3]) + 1, 254); + update.data.peer = peer.join("."); + } + }); + }, 1000); + + return Promise.resolve(); + }; static transform = (message) => { if (message.type === 'ris_message') { - message = message.data; - const components = []; - const announcements = message["announcements"] || []; - const withdrawals = message["withdrawals"] || []; - const aggregator = message["aggregator"] || null; - const peer = message["peer"]; + try { + message = message.data; + const components = []; + const announcements = message["announcements"] || []; + const aggregator = message["aggregator"] || null; + const withdrawals = message["withdrawals"] || []; + const peer = message["peer"]; + const communities = message["community"] || []; + const timestamp = message["timestamp"] * 1000; + let path, originAS; - for (let announcement of announcements){ - const nextHop = announcement["next_hop"]; - const prefixes = announcement["prefixes"] || []; - let path = new Path(message["path"].map(i => new AS(i))); - let originAS = path.getLast(); + if (message["path"] && message["path"].length) { + path = new Path(message["path"].map(i => new AS(i))); + originAS = path.getLast(); + } else { + path = new Path([]); + originAS = null; + } - for (let prefix of prefixes){ + if (originAS && path.length()) { + for (let announcement of announcements) { + const nextHop = announcement["next_hop"]; + + if (ipUtils.isValidIP(nextHop)) { + const prefixes = (announcement["prefixes"] || []) + .filter(prefix => ipUtils.isValidPrefix(prefix)); + + for (let prefix of prefixes) { + components.push({ + type: "announcement", + prefix, + peer, + path, + originAS, + nextHop, + aggregator, + timestamp, + communities + }); + } + } + } + } + + for (let prefix of withdrawals) { components.push({ - type: "announcement", + type: "withdrawal", prefix, peer, - path, - originAS, - nextHop, - aggregator + timestamp }) } - } - for (let prefix of withdrawals){ - components.push({ - type: "withdrawal", - prefix, - peer - }) + return components; + } catch (error) { + throw new Error(`Error during transform (${this.name}): ` + error.message); } - - return components; + } else if (message.type === 'ris_error') { + throw new Error("Error from RIS: " + message.data.message); } }; } \ No newline at end of file diff --git a/src/consumer.js b/src/consumer.js index f025bcd0..5f14c9cf 100644 --- a/src/consumer.js +++ b/src/consumer.js @@ -30,28 +30,32 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import env from "./env"; - export default class Consumer { - constructor(){ + constructor(env, input){ + this.logger = env.logger; this.connectors = {}; for (let connector of env.config.connectors) { this.connectors[connector.name] = connector.class } - this.monitors = env.config.monitors - .map(monitor => new monitor.class(monitor.name, monitor.channel, monitor.params || {}, env)); + try { - this.reports = env.config.reports - .map(report => new report.class(report.channels, report.params || {}, env)); + this.monitors = env.config.monitors + .map(monitor => new monitor.class(monitor.name, monitor.channel, monitor.params || {}, env, input)); - process.on('message', this.dispatch); - env.pubSub.subscribe('data', (type, data) => { - this.dispatch(data); - }); + this.reports = env.config.reports + .map(report => new report.class(report.channels, report.params || {}, env)); + } catch (error) { + this.logger.log({ + level: 'error', + message: error + }); + } + process.on('message', this.dispatch); + env.pubSub.subscribe('data', this.dispatch); }; dispatch = (buffer) => { @@ -71,7 +75,7 @@ export default class Consumer { monitor .monitor(message) .catch(error => { - env.logger.log({ + this.logger.log({ level: 'error', message: error }); @@ -80,7 +84,7 @@ export default class Consumer { } } } catch (error) { - env.logger.log({ + this.logger.log({ level: 'error', message: error.message }); diff --git a/src/env.js b/src/env.js index f41d0871..f5ac465d 100644 --- a/src/env.js +++ b/src/env.js @@ -30,154 +30,83 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import yaml from "js-yaml"; import fs from "fs"; -import path from "path"; -import PubSub from './pubSub'; -import FileLogger from './fileLogger'; -import Input from "./inputs/inputYml"; -import {version} from '../package.json'; -import axios from 'axios'; - -const defaultConfigFilePath = path.resolve(process.cwd(), 'config.yml'); +import PubSub from './utils/pubSub'; +import FileLogger from 'fast-file-logger'; +import { version } from '../package.json'; +import Storage from './utils/storages/storageFile'; +import url from 'url'; +import RpkiUtils from './utils/rpkiUtils'; +import ConfigYml from './config/configYml'; +import Config from "./config/config"; +import { v4 as uuidv4 } from 'uuid'; + +const configConnector = new (global.EXTERNAL_CONFIG_CONNECTOR || ConfigYml); const vector = { version: global.EXTERNAL_VERSION_FOR_TEST || version, - configFile: global.EXTERNAL_CONFIG_FILE || defaultConfigFilePath, clientId: Buffer.from("bnR0LWJncGFsZXJ0ZXI=", 'base64').toString('ascii') }; -let config = { - environment: "production", - connectors: [ - { - file: "connectorRIS", - name: "ris", - params: { - carefulSubscription: true, - url: "wss://ris-live.ripe.net/v1/ws/", - perMessageDeflate: true, - subscription: { - moreSpecific: true, - type: "UPDATE", - host: null, - socketOptions: { - includeRaw: false - } - } - } - } - ], - monitors: [ - { - file: "monitorHijack", - channel: "hijack", - name: "basic-hijack-detection", - params: { - thresholdMinPeers: 2 - } - }, - { - file: "monitorPath", - channel: "path", - name: "path-matching", - params: { - thresholdMinPeers: 0 - } - }, - { - file: "monitorNewPrefix", - channel: "newprefix", - name: "prefix-detection", - params: { - thresholdMinPeers: 2 - } - }, - { - file: "monitorVisibility", - channel: "visibility", - name: "withdrawal-detection", - params: { - thresholdMinPeers: 10 - } - }, - { - file: "monitorAS", - channel: "misconfiguration", - name: "as-monitor", - params: { - thresholdMinPeers: 2 - } +const config = configConnector.retrieve(); + +if (global.DRY_RUN) { + config.connectors = [{ + file: "connectorTest", + name: "tes", + params: { + testType: "hijack" } - ], - reports: [ - { - file: "reportFile", - channels: ["hijack", "newprefix", "visibility", "path", "misconfiguration"] + }]; + config.monitors = [{ + file: "monitorPassthrough", + channel: "hijack", + name: "monitor-passthrough", + params: { + showPaths: 0, + thresholdMinPeers: 0 } - ], - notificationIntervalSeconds: 14400, - alarmOnlyOnce: false, - monitoredPrefixesFiles: ["prefixes.yml"], - logging: { - directory: "logs", - logRotatePattern: "YYYY-MM-DD", - backlogSize: 1000, - maxRetainedFiles: 10, - maxFileSizeMB: 15, - compressOnRotation: false, - }, - checkForUpdatesAtBoot: false, - checkForUpdatesInterval: 1000 * 3600 * 24 * 5, // Check every 5 days - checkForUpdates: false, - pidFile: "bgpalerter.pid", - fadeOffSeconds: 360, - checkFadeOffGroupsSeconds: 30 -}; + }]; +} + +config.volume = config.volume || global.EXTERNAL_VOLUME_DIRECTORY || ""; +if (config.volume && config.volume.length) { + if (config.volume.slice(-1) !== "/") { + config.volume += "/"; + } -if (fs.existsSync(vector.configFile)) { - try { - config = yaml.safeLoad(fs.readFileSync(vector.configFile, 'utf8')) || config; - } catch (error) { - throw new Error("The file " + vector.configFile + " is not valid yml: " + error.message.split(":")[0]); + if (!fs.existsSync(config.volume)) { + fs.mkdirSync(config.volume); } -} else { - console.log("Impossible to load config.yml. A default configuration file has been generated."); - //axios({ - // url: 'https://raw.githubusercontent.com/nttgin/BGPalerter/master/config.yml.example', - // method: 'GET', - // responseType: 'blob', // important - //}) - // .then((response) => { - // fs.writeFileSync(defaultConfigFilePath, response.data); - // }) - // .catch(() => { - // fs.writeFileSync(defaultConfigFilePath, yaml.dump(config)); - // }) +} +if (!config.configVersion || config.configVersion < Config.configVersion) { + console.log("Your config.yml file is old. It works, but it may not support all the new features. Update your config file or generate a new one (e.g., rename the file into config.yml.bak, run BGPalerter and proceed with the auto configuration, apply to the new config.yml the personalizations you did in config.yml.bak."); } const errorTransport = new FileLogger({ logRotatePattern: config.logging.logRotatePattern, filename: 'error-%DATE%.log', - directory: config.logging.directory, - backlogSize: config.logging.backlogSize, + symLink: 'error.log', + directory: config.volume + config.logging.directory, maxRetainedFiles: config.logging.maxRetainedFiles, maxFileSizeMB: config.logging.maxFileSizeMB, compressOnRotation: config.logging.compressOnRotation, label: config.environment, + useUTC: !!config.logging.useUTC, format: ({data, timestamp}) => `${timestamp} ${data.level}: ${data.message}` }); const verboseTransport = new FileLogger({ logRotatePattern: config.logging.logRotatePattern, filename: 'reports-%DATE%.log', - directory: config.logging.directory, - backlogSize: config.logging.backlogSize, + symLink: 'reports.log', + directory: config.volume + config.logging.directory, maxRetainedFiles: config.logging.maxRetainedFiles, maxFileSizeMB: config.logging.maxFileSizeMB, compressOnRotation: config.logging.compressOnRotation, label: config.environment, + useUTC: !!config.logging.useUTC, format: ({data, timestamp}) => `${timestamp} ${data.level}: ${data.message}` }); @@ -194,13 +123,7 @@ const wlogger = { } }; - config.monitors = (config.monitors || []); -config.monitors.push({ - file: "monitorSwUpdates", - channel: "software-update", - name: "software-update", -}); config.monitors = config.monitors .map(item => { @@ -221,17 +144,19 @@ config.reports = (config.reports || []) }; } return { + file: item.file, class: require("./reports/" + item.file).default, - channels: [...item.channels, "software-update"], + channels: item.channels, params: item.params }; }); + +if (!config.reports.some(report => report.channels.includes("software-update"))) { // Check if software-update channel is declared + config.reports.forEach(report => report.channels.push("software-update")); // If not, declare it everywhere +} + config.connectors = config.connectors || []; -config.connectors.push( { - file: "connectorSwUpdates", - name: "upd" -}); if ([...new Set(config.connectors)].length !== config.connectors.length) { throw new Error('Connectors names MUST be unique'); @@ -252,12 +177,16 @@ config.connectors = config.connectors }); +if (config.httpProxy) { + const HttpsProxyAgent = require("https-proxy-agent"); + vector.agent = new HttpsProxyAgent(url.parse(config.httpProxy)); +} -const input = new Input(config); - +vector.storage = new Storage({}, config); vector.config = config; vector.logger = wlogger; -vector.input = input; vector.pubSub = new PubSub(); +vector.rpki = new RpkiUtils(vector); +vector.instanceId = uuidv4(); module.exports = vector; diff --git a/src/fileLogger.js b/src/fileLogger.js deleted file mode 100644 index 82e69ee4..00000000 --- a/src/fileLogger.js +++ /dev/null @@ -1,169 +0,0 @@ -var fs = require('fs'); -var moment = require('moment'); -const zlib = require('zlib'); - -export default class FileLogger { - - constructor(params) { - - this.format = params.format || this.defaultFormat; - this.logRotatePattern = params.logRotatePattern; - this.filename = params.filename; - this.directory = params.directory; - this.levels = params.levels || ['error', 'info', 'verbose']; - - // File rotation - this.compressOnRotation = params.compressOnRotation; - this.maxFileSizeMB = parseFloat(params.maxFileSizeMB || 20); - this.maxRetainedFiles = parseFloat(params.maxRetainedFiles || 20); - - this.backlog = []; - this.staleTimer = null; - this.backlogSize = parseFloat(params.backlogSize || 100); - - this.wstream = null; - - - if (!fs.existsSync(this.directory)){ - fs.mkdirSync(this.directory); - } - - this._currentFile = this.getCurrentFile(); - }; - - getRotatedFileName = (number) => { - return this._currentFile + '.' + number + ((this.compressOnRotation) ? '.gz' : ''); - }; - - rotateOldFiles = () => { - for (let n=this.maxRetainedFiles; n >= 0; n--) { - const fileName = this.getRotatedFileName(n); - - if (fs.existsSync(fileName)) { - fs.renameSync(fileName, this.getRotatedFileName(n + 1)); - } - } - - }; - - applyFileNumberLimit = () => { - - try { - - let files = fs.readdirSync(this.directory) - .filter(i => i.indexOf('.log') > 0) - .sort((file1, file2) => { - const v1 = file1.replace('.gz', '').split('.').pop(); - const v2 = file2.replace('.gz', '').split('.').pop(); - return parseInt(v1) - parseInt(v2); - }); - - if (files.length >= this.maxRetainedFiles - 1) { - files = files.slice(this.maxRetainedFiles); - files - .forEach(file => { - fs.unlinkSync(this.directory + '/' + file); - }); - } - } catch { - // Nothing - } - }; - - hasToBeRotated = () => { - const stat = fs.statSync(this._currentFile); - const fileSizeInMegabytes = stat.size / 1000000.0; - return fileSizeInMegabytes > this.maxFileSizeMB; - }; - - rotate = () => { - this.close(); - const currentRotatedFile = this.getRotatedFileName(0); - const firstRotatedFile = this.getRotatedFileName(1); - fs.renameSync(this._currentFile, currentRotatedFile); - - this.rotateOldFiles(); - if (this.compressOnRotation) { - fs.writeFileSync(firstRotatedFile, zlib.gzipSync(fs.readFileSync(firstRotatedFile, 'utf8'))); - } - - this.applyFileNumberLimit(); - this.open(); - }; - - getCurrentFile = () => { - return this.directory + '/' + this.filename.replace("%DATE%", moment().format(this.logRotatePattern)); - }; - - currentFileChanged = () => { - const file = this.getCurrentFile(); - return this._currentFile && this._currentFile !== file; - }; - - defaultFormat = (json) => { - return JSON.stringify(json); - }; - - log = (data) => { - - const item = this.format({ - timestamp: moment().format('YYYY-MM-DDTHH:mm:ssZ'), - data - }); - - - if (this.staleTimer) { - clearTimeout(this.staleTimer); - delete this.staleTimer; - } - - if (this.currentFileChanged()) { - this.flush(); - this.rotate(); - - this.backlog.push(item); - - } else { - - this.backlog.push(item); - - if (this.backlog.length >= this.backlogSize) { - this.flush(); - if (this.hasToBeRotated()){ - this.rotate(); - } - } else { - this.staleTimer = setTimeout(this.flushAndClose, 1000); - } - } - - - - }; - - flushAndClose = () => { - this.flush(); - this.close(); - }; - - flush = () => { - const string = this.backlog.join('\n') + '\n'; - this.backlog = []; - if (this.wstream === null) { - this.open(); - } - fs.appendFileSync(this.wstream, string, 'utf8'); - }; - - open = () => { - this._currentFile = this.getCurrentFile(); - this.wstream = fs.openSync(this._currentFile, 'a'); - }; - - close = () => { - if (this.wstream !== null) - fs.closeSync(this.wstream); - this.wstream = null; - } - -}; \ No newline at end of file diff --git a/src/generatePrefixesList.js b/src/generatePrefixesList.js index 29e4396c..3b817bed 100644 --- a/src/generatePrefixesList.js +++ b/src/generatePrefixesList.js @@ -1,23 +1,55 @@ import axios from "axios"; +import url from "url"; import brembo from "brembo"; -import yaml from "js-yaml"; -import fs from "fs"; -import https from 'https'; +import merge from "deepmerge"; +import batchPromises from "batch-promises"; +import RpkiValidator from "rpki-validator"; +import { AS } from "./model"; -const batchPromises = require('batch-promises'); +const apiTimeout = 120000; +const clientId = "ntt-bgpalerter"; +const rpki = new RpkiValidator({clientId}); +import axiosEnrich from "./utils/axiosEnrich"; -const httpsAgentOptions = { - keepAlive: (process.env.KEEP_ALIVE || 'true') === 'true', - maxSockets: parseInt(process.env.MAX_SOCKETS || '10', 10) -} -console.log(`Using HTTPS Agent Options: ${JSON.stringify(httpsAgentOptions)}`); -axios.defaults.httpsAgent = new https.Agent(httpsAgentOptions); -module.exports = function generatePrefixes(asnList, outputFile, exclude, excludeDelegated, prefixes, monitoredASes) { +module.exports = function generatePrefixes(inputParameters) { + let { + asnList, + exclude, + excludeDelegated, + prefixes, + monitoredASes, + httpProxy, + debug, + historical, + group, + append, + logger, + getCurrentPrefixesList, + enriched, + upstreams, + downstreams + } = inputParameters; + + exclude = exclude || []; + logger = logger || console.log; + group = group || "noc"; + const generateList = {}; const allOrigins = {}; let someNotValidatedPrefixes = false; + let proxy; + if (httpProxy) { + const HttpsProxyAgent = require("https-proxy-agent"); + proxy = new HttpsProxyAgent(url.parse(httpProxy)); + } + axiosEnrich(axios, proxy, clientId); + + if (historical) { + logger("WARNING: you are using historical visibility data for generating the prefix list."); + } + if (!asnList && !prefixes) { throw new Error("You need to specify at least an AS number or a list of prefixes."); } @@ -26,22 +58,89 @@ module.exports = function generatePrefixes(asnList, outputFile, exclude, exclude throw new Error("You can specify an AS number or a list of prefixes, not both."); } - if (!outputFile) { - throw new Error("Output file not specified"); + if (asnList && asnList.length) { + asnList = asnList.map(i => i.replace("AS", "")); + if (asnList.some(i => !new AS([i]).isValid())) { + throw new Error("One of the AS number is not valid"); + } + } + if (monitoredASes && monitoredASes.length) { + monitoredASes = monitoredASes.map(i => i.replace("AS", "")); + if (monitoredASes.some(i => !new AS([i]).isValid())) { + throw new Error("One of the AS number is not valid"); + } } + const getNeighbors = (asn) => { + const url = brembo.build("https://stat.ripe.net", { + path: ["data", "asn-neighbours", "data.json"], + params: { + client: clientId, + resource: asn + } + }); + + if (debug) { + logger("Query", url) + } + + return axios({ + url, + method: 'GET', + responseType: 'json', + timeout: apiTimeout + }) + .then(data => { + let neighbors = []; + + if (data.data && data.data.data && data.data.data.neighbours){ + const items = data.data.data.neighbours; + + for (let item of items) { + if (item.type === "left" || item.type === "right") { + neighbors.push({asn: item.asn, type: item.type}); + } + } + + } + + const uncertain = neighbors.filter(i => i.type === "uncertain"); + const out = { + asn, + upstreams: neighbors.filter(i => i.type === "left").concat(uncertain).map(i => i.asn), + downstreams: neighbors.filter(i => i.type === "right").concat(uncertain).map(i => i.asn) + }; + + logger(`Detected upstreams for ${out.asn}: ${out.upstreams.join(", ")}`); + logger(`Detected downstreams for ${out.asn}: ${out.downstreams.join(", ")}`); + + return out; + }) + .catch((error) => { + logger(error); + logger(`RIPEstat asn-neighbours query failed: cannot retrieve information for ${asn}`); + }); + }; + const getMultipleOrigins = (prefix) => { const url = brembo.build("https://stat.ripe.net", { path: ["data", "prefix-overview", "data.json"], params: { + min_peers_seeing: 0, + client: clientId, resource: prefix } }); + if (debug) { + logger("Query", url) + } + return axios({ url, method: 'GET', - responseType: 'json' + responseType: 'json', + timeout: apiTimeout }) .then(data => { let asns = []; @@ -52,25 +151,30 @@ module.exports = function generatePrefixes(asnList, outputFile, exclude, exclude return asns; }) .catch((error) => { - console.log("RIPEstat prefix-overview query failed: cannot retrieve information for " + prefix); - console.log(error.toJSON()); - throw error; + logger(error); + logger(`RIPEstat prefix-overview query failed: cannot retrieve information for ${prefix}`); }); }; const getAnnouncedMoreSpecifics = (prefix) => { - console.log("Generating monitoring rule for", prefix); + logger(`Generating monitoring rule for ${prefix}`); const url = brembo.build("https://stat.ripe.net", { path: ["data", "related-prefixes", "data.json"], params: { + client: clientId, resource: prefix } }); + if (debug) { + logger(`Query ${url}`); + } + return axios({ url, method: 'GET', - responseType: 'json' + responseType: 'json', + timeout: apiTimeout }) .then(data => { let prefixes = []; @@ -78,7 +182,7 @@ module.exports = function generatePrefixes(asnList, outputFile, exclude, exclude prefixes = data.data.data.prefixes .filter(i => i.relationship === "Overlap - More Specific") .map(i => { - console.log("Detected more specific " + i.prefix); + logger(`Detected more specific ${i.prefix}`); return { asn: i.origin_asn, description: i.asn_name, @@ -90,7 +194,7 @@ module.exports = function generatePrefixes(asnList, outputFile, exclude, exclude return prefixes; }) .catch(() => { - console.log("RIPEstat related-prefixes query failed: cannot retrieve information for " + prefix); + logger(`RIPEstat related-prefixes query failed: cannot retrieve information for ${prefix}`); }); }; @@ -99,34 +203,50 @@ module.exports = function generatePrefixes(asnList, outputFile, exclude, exclude getMultipleOrigins(prefix) .then(asns => { - if (asns.length) { + let origins = [parseInt(asn)]; + + if (asns && asns.length) { const origin = (asns && asns.length) ? asns : [asn]; for (let o of origin) { allOrigins[o] = true; } - generateList[prefix] = { - description: description || "No description provided", - asn: origin.map(i => parseInt(i)), - ignoreMorespecifics: ignoreMorespecifics, - ignore: excludeDelegated - }; + origins = origin.map(i => parseInt(i)); + } else { + logger("RIPEstat is having issues in returning the origin ASes of some prefixes. The prefix.yml configuration may be incomplete."); } + + generateList[prefix] = generateList[prefix] || { + description: description || "No description provided", + asn: origins, + ignoreMorespecifics: ignoreMorespecifics, + ignore: excludeDelegated, + group: group + }; + }); const getAnnouncedPrefixes = (asn) => { const url = brembo.build("https://stat.ripe.net", { path: ["data", "announced-prefixes", "data.json"], params: { + client: clientId, resource: asn } }); + logger(`Getting announced prefixes of AS${asn}`); + + if (debug) { + logger(`Query ${url}`); + } + return axios({ url, method: 'GET', - responseType: 'json' + responseType: 'json', + timeout: apiTimeout }) .then(data => { if (data.data && data.data.data && data.data.data.prefixes) { @@ -137,113 +257,161 @@ module.exports = function generatePrefixes(asnList, outputFile, exclude, exclude .sort((a,b) => a-b) .pop(); - return latest.getTime() + (3600 * 24 * 1000) > new Date().getTime(); + const validityPeriodDays = (historical) ? + (3600 * 1000 * 24 * 7) : // 7 days + (3600 * 1000 * 28); // 28 hours (1 day and 4 hours margin) + return latest.getTime() + validityPeriodDays > new Date().getTime(); }) - } return []; }) - .then(list => list.filter(i => !exclude.includes(i.prefix))) .then(list => { - return batchPromises(10, list, i => generateRule(i.prefix, asn, false, null, false)) - .then(() => list.map(i => i.prefix)) + if (list.length === 0) { + logger(`WARNING: no announced prefixes were detected for AS${asn}. If you are sure the AS provided is announcing at least one prefix, this could be an issue with the data source (RIPEstat). Try to run the generate command with the option -H.`); + } + + return list; }) + .then(list => list.filter(i => !exclude.includes(i.prefix))) + .then(list => { + + return batchPromises(40, list, i => { + return generateRule(i.prefix, asn, false, null, false); + }) + .then(() => list.map(i => i.prefix)); + }); }; const validatePrefix = (asn, prefix) => { - const url = brembo.build("https://stat.ripe.net", { - path: ["data", "rpki-validation", "data.json"], - params: { - resource: asn, - prefix - } - }); - - return axios({ - url, - method: 'GET', - responseType: 'json' - }) - .then(data => { - if (data.data && data.data.data && data.data.data.validating_roas) { - return data.data.data.validating_roas.map(i => i.validity).some(i => i === 'valid'); + return rpki + .validate(prefix, asn, false) + .then(isValid => { + if (enriched) { + generateList[prefix].valid = isValid; } - return false; - }) - .then((isValid) => { - if (isValid) { - generateList[prefix].description += ' (valid ROA available)'; + + if (isValid === true) { + // All good + } else if (isValid === false) { + delete generateList[prefix]; + logger(`RPKI invalid: ${prefix} ${asn}`); } else { + generateList[prefix].description += ' (No ROA available)'; someNotValidatedPrefixes = true; } }) - .catch(() => { - console.log("RIPEstat rpki-validation query failed: cannot retrieve information for " + prefix); + .catch((error) => { + logger(`RPKI validation query failed: cannot retrieve information for ${prefix}`); }); }; - const getBaseRules = () => { + const getBaseRules = (prefixes) => { if (prefixes) { - return Promise - .all(prefixes.map(p => generateRule(p, null, false, null, false))) + return batchPromises(40, prefixes, p => { + return generateRule(p, null, false, null, false); + }) .then(() => prefixes); } else { - return Promise.all(asnList.map(getAnnouncedPrefixes)); + let prefixes = []; + return batchPromises(1, asnList, asn => { + return getAnnouncedPrefixes(asn) + .catch(error => { + logger(`It was not possible to retrieve the announced prefixes of ${asn}. ${error}`); + return prefixes; + }) + .then(plist => prefixes = prefixes.concat(plist)); + }) + .then(() => { + logger(`Total prefixes detected: ${prefixes.length}`); + return prefixes; + }); } }; - return getBaseRules() + return getBaseRules(prefixes) .then(items => [].concat.apply([], items)) .then(prefixes => { - return batchPromises(10, prefixes, prefix => { + return batchPromises(1, prefixes, prefix => { return getAnnouncedMoreSpecifics(prefix) - .then((items) => Promise - .all(items.map(item => generateRule(item.prefix, item.asn, true, item.description, excludeDelegated)))) + .then(items => { + return batchPromises(1, items, item => { + return generateRule(item.prefix, item.asn, true, item.description, excludeDelegated) + }); + }) .catch((e) => { - console.log("Cannot download more specific prefixes of", prefix, e); + logger(`Cannot download more specific prefixes of ${prefix} ${e}`); }) }) .catch((e) => { - console.log("Cannot download more specific prefixes", e); + logger(`Cannot download more specific prefixes ${e}`); }) }) + .then(() => rpki.preCache()) .then(() => { // Check - return batchPromises(10, Object.keys(generateList), prefix => validatePrefix(generateList[prefix].asn[0], prefix)) + return Promise + .all(Object.keys(generateList).map(prefix => validatePrefix(generateList[prefix].asn[0], prefix))) .catch((e) => { - console.log("ROA check failed due to error", e); + logger(`ROA check failed due to error ${e}`); }) }) .then(() => { // Add the options for monitorASns - const generateMonitoredAsObject = function (list) { + const generateMonitoredAsObject = function (list, asnNeighbors) { generateList.options = generateList.options || {}; generateList.options.monitorASns = generateList.options.monitorASns || {}; for (let monitoredAs of list) { - console.log("Generating generic monitoring rule for AS", monitoredAs); + logger(`Generating generic monitoring rule for AS${monitoredAs}`); + const neighbors = asnNeighbors.filter(i => i.asn.toString() === monitoredAs.toString()); generateList.options.monitorASns[monitoredAs] = { - group: 'default' + group: group, + upstreams: upstreams && neighbors.length ? neighbors[0].upstreams : null, + downstreams: downstreams && neighbors.length ? neighbors[0].downstreams : null }; } }; + + let createASesRules = []; if (monitoredASes === true) { - generateMonitoredAsObject(asnList); + createASesRules = asnList; } else if (monitoredASes.length) { - generateMonitoredAsObject(monitoredASes); + createASesRules = monitoredASes; } + + return batchPromises(1, asnList, getNeighbors) + .then(asnNeighbors => { + generateMonitoredAsObject(createASesRules, asnNeighbors); + }) // Otherwise nothing }) - .then(() => { // write everything into the file - const yamlContent = yaml.dump(generateList); - fs.writeFileSync(outputFile, yamlContent); - + .then(() => { if (someNotValidatedPrefixes) { - console.log("WARNING: The generated configuration is a snapshot of what is currently announced. Some of the prefixes don't have ROA objects associated or are RPKI invalid. Please, verify the config file by hand!"); + logger("WARNING: the generated configuration is a snapshot of what is currently announced. Some of the prefixes don't have ROA objects associated. Please, verify the config file by hand!"); } - console.log("Done!"); }) - .catch((e) => { - console.log("Something went wrong", e); + .then(() => { + generateList.options = generateList.options || {}; + generateList.options.generate = { + asnList, + exclude, + excludeDelegated, + prefixes, + monitoredASes, + historical, + group + }; + + return (append) + ? getCurrentPrefixesList() + .then(current => merge(current, generateList, { + arrayMerge: (destinationArray, sourceArray) => { + return [...new Set([...destinationArray, ...sourceArray])]; + } + })) + : generateList; }) + .catch((e) => { + logger(`Something went wrong ${e}`); + }); }; diff --git a/src/inputs/input.js b/src/inputs/input.js index da6ac605..9337778a 100644 --- a/src/inputs/input.js +++ b/src/inputs/input.js @@ -1,4 +1,3 @@ - /* * BSD 3-Clause License * @@ -33,37 +32,66 @@ import ipUtils from "ip-sub"; import inquirer from "inquirer"; +import generatePrefixes from "../generatePrefixesList"; export default class Input { - constructor(config){ + constructor(env){ this.prefixes = []; this.asns = []; - this.cache = {}; - this.config = config; + this.cache = { + af: {}, + binaries: {}, + matched: {} + }; + this.config = env.config; + this.storage = env.storage; + this.logger = env.logger; this.callbacks = []; + this.prefixListDiffFailThreshold = 50; + + // This implements a fast basic fixed space cache, other approaches lru-like use too much cpu + setInterval(() => { + if (Object.keys(this.cache.matched).length > 10000) { + this.cache.matched = {}; + } + }, 10000); + // This is to load the prefixes after the application is booted setTimeout(() => { this.loadPrefixes() .then(() => { this._change(); }) .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); console.log(error); process.exit(); }); }, 200); - }; _isAlreadyContained = (prefix, lessSpecifics) => { - const p1b = ipUtils.getNetmask(prefix); + let p1af, p1b; - for (let p2 of lessSpecifics) { - const p2b = ipUtils.getNetmask(p2.prefix); + try { + p1af = ipUtils.getAddressFamily(prefix); + p1b = ipUtils.applyNetmask(prefix, p1af); + } catch (error) { + throw new Error(`${error.message}: ${prefix}`); + } - if (ipUtils.isSubnetBinary(p2b, p1b)) { - return true; + for (let p2 of lessSpecifics) { + try { + const p2af = ipUtils.getAddressFamily(p2.prefix); + if (p1af === p2af && ipUtils.isSubnetBinary(ipUtils.applyNetmask(p2.prefix, p2af), p1b)) { + return true; + } + } catch (error) { + throw new Error(`${error.message}: ${p2}`); } } @@ -75,9 +103,9 @@ export default class Input { }; _change = () => { - for (let call of this.callbacks) { - call(); - } + for (let call of this.callbacks) { + call(); + } }; getMonitoredLessSpecifics = () => { @@ -87,15 +115,24 @@ export default class Input { } const lessSpecifics = []; - let prefixes = this.prefixes; - lessSpecifics.push(prefixes[prefixes.length - 1]); - for (let n=prefixes.length - 2; n>=0; n--) { - const p1 = prefixes[n]; - if (!this._isAlreadyContained(p1.prefix, lessSpecifics)){ - lessSpecifics.push(p1); + try { + let prefixes = this.prefixes; + lessSpecifics.push(prefixes[prefixes.length - 1]); + + for (let n = prefixes.length - 2; n >= 0; n--) { + const p1 = prefixes[n]; + if (!this._isAlreadyContained(p1.prefix, lessSpecifics)) { + lessSpecifics.push(p1); + } } + } catch (error) { + this.logger.log({ + level: 'error', + message: error.message + }); } + return lessSpecifics; }; @@ -108,21 +145,36 @@ export default class Input { }; getMoreSpecificMatch = (prefix, includeIgnoredMorespecifics) => { + const key = `${prefix}-${includeIgnoredMorespecifics}`; + const cached = this.cache.matched[key]; - for (let p of this.prefixes) { - if (ipUtils._isEqualPrefix(p.prefix, prefix)) { // Used internal method to avoid validation overhead - return p; - } else { - if (!this.cache[p.prefix]) { - this.cache[p.prefix] = ipUtils.getNetmask(p.prefix); - } - const p2 = ipUtils.getNetmask(prefix); + if (cached !== undefined) { + return cached; + } else { + for (let p of this.prefixes) { + if (ipUtils._isEqualPrefix(p.prefix, prefix)) { + this.cache.matched[key] = p; + return p; + } else { - if (ipUtils.isSubnetBinary(this.cache[p.prefix], p2)) { - if (includeIgnoredMorespecifics || !p.ignoreMorespecifics) { - return p; - } else { - return null; + if (!this.cache.af[p.prefix]) { + this.cache.af[p.prefix] = ipUtils.getAddressFamily(p.prefix); + this.cache.binaries[p.prefix] = ipUtils.applyNetmask(p.prefix, this.cache.af[p.prefix]); + } + const prefixAf = ipUtils.getAddressFamily(prefix); + + if (prefixAf === this.cache.af[p.prefix]) { + + const prefixBinary = ipUtils.applyNetmask(prefix, prefixAf); + if (ipUtils.isSubnetBinary(this.cache.binaries[p.prefix], prefixBinary)) { + if (includeIgnoredMorespecifics || !p.ignoreMorespecifics) { + this.cache.matched[key] = p; + return p; + } else { + this.cache.matched[key] = null; + return null; + } + } } } } @@ -139,10 +191,14 @@ export default class Input { throw new Error('The method loadPrefixes MUST be implemented'); }; - save = () => { + save = (data) => { throw new Error('The method save MUST be implemented'); }; + retrieve = () => { + throw new Error('The method retrieve MUST be implemented'); + }; + generate = () => { return inquirer .prompt([ @@ -160,8 +216,8 @@ export default class Input { { type: 'input', name: 'asns', - message: "Which Autonomous System(s) you want to monitor? (comma-separated, e.g. 2914,3333)", - default: true, + message: "Which Autonomous System(s) you want to monitor? (comma-separated, e.g., 2914,3333)", + default: null, validate: function(value) { const asns = value.split(",").filter(i => i !== "" && !isNaN(i)); return asns.length > 0; @@ -170,36 +226,160 @@ export default class Input { { type: 'confirm', - name: 'i', - message: "Are there sub-prefixes delegated to other ASes? (e.g. sub-prefixes announced by customers)", + name: 'm', + message: "Do you want to be notified when your AS is announcing a new prefix?", default: true }, { type: 'confirm', - name: 'm', - message: "Do you want to be notified when your AS is announcing a new prefix?", + name: 'upstreams', + message: "Do you want to be notified when a new upstream AS appears in a BGP path?", + default: true + }, + { + type: 'confirm', + name: 'downstreams', + message: "Do you want to be notified when a new downstream AS appears in a BGP path?", default: true } ]) .then((answer) => { - const generatePrefixes = require("../generatePrefixesList"); const asns = answer.asns.split(","); - return generatePrefixes( - asns, - "prefixes.yml", - [], - answer.i, - null, - answer.m ? asns : [] - ); + + const inputParameters = { + asnList: asns, + exclude: [], + excludeDelegated: true, + prefixes: null, + monitoredASes: answer.m ? asns : [], + httpProxy: this.config.httpProxy || null, + debug: false, + historical: false, + group: null, + append: false, + logger: null, + upstreams: !!answer.upstreams, + downstreams: !!answer.downstreams, + getCurrentPrefixesList: () => { + return this.retrieve(); + } + }; + + return generatePrefixes(inputParameters); + }); } else { throw new Error("Nothing to monitor."); } + }) + .then(this.save) + .then(() => { + console.log("Done!"); + }) + .catch(error => { + console.log(error); + this.logger.log({ + level: 'error', + message: error + }); + process.exit(); }); + }; + + _reGeneratePrefixList = () => { + this.logger.log({ + level: 'info', + message: "Updating prefix list" + }); + + this.setReGeneratePrefixList(); + + return this.retrieve() + .then(oldPrefixList => { + const inputParameters = oldPrefixList.options.generate; + + if (!inputParameters) { + throw new Error("The prefix list cannot be refreshed because it was not generated automatically."); + } + + inputParameters.httpProxy = this.config.httpProxy || null; + inputParameters.logger = (message) => { + // Nothing, ignore logs in this case (too many otherwise) + }; + + return generatePrefixes(inputParameters) + .then(newPrefixList => { + + newPrefixList.options.monitorASns = oldPrefixList.options.monitorASns; + + if (Object.keys(newPrefixList).length <= (Object.keys(oldPrefixList).length / 100) * this.prefixListDiffFailThreshold) { + throw new Error("Prefix list generation failed."); + } + const newPrefixesNotMergeable = []; + const uniquePrefixes = [...new Set(Object.keys(oldPrefixList).concat(Object.keys(newPrefixList)))] + .filter(prefix => ipUtils.isValidPrefix(prefix)); + const asns = [...new Set(Object + .values(oldPrefixList) + .map(i => i.asn) + .concat(Object.keys((oldPrefixList.options || {}).monitorASns || {})))]; + for (let prefix of uniquePrefixes) { + const oldPrefix = oldPrefixList[prefix]; + const newPrefix = newPrefixList[prefix]; + + // Apply old description to the prefix + if (newPrefix && oldPrefix) { + newPrefixList[prefix] = oldPrefix; + } + + // The prefix didn't exist + if (newPrefix && !oldPrefix) { + // The prefix is not RPKI valid + if (!newPrefix.valid) { + // The prefix is not announced by a monitored ASn + if (!newPrefix.asn.some(p => asns.includes(p))) { + newPrefixesNotMergeable.push(prefix); + delete newPrefixList[prefix]; + } + } + } + } + + if (newPrefixesNotMergeable.length) { + this.logger.log({ + level: 'info', + message: `The rules about ${newPrefixesNotMergeable.join(", ")} cannot be automatically added to the prefix list since their origin cannot be validated. They are not RPKI valid and they are not announced by a monitored AS. Add the prefixes manually if you want to start monitoring them.` + }); + } + + return newPrefixList; + }); + }) + .then(this.save) + .then(() => { + this.logger.log({ + level: 'info', + message: `Prefix list updated.` + }); + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + }; + + setReGeneratePrefixList = () => { + if (this.config.generatePrefixListEveryDays >= 1) { + const refreshTimer = Math.ceil(this.config.generatePrefixListEveryDays) * 24 * 3600 * 1000; + if (this.regeneratePrefixListTimer) { + clearTimeout(this.regeneratePrefixListTimer); + } + this.regeneratePrefixListTimer = setTimeout(this._reGeneratePrefixList, refreshTimer); + } }; } \ No newline at end of file diff --git a/src/inputs/inputYml.js b/src/inputs/inputYml.js index b5e2f14a..9d60cd0c 100644 --- a/src/inputs/inputYml.js +++ b/src/inputs/inputYml.js @@ -36,21 +36,25 @@ import Input from "./input"; import ipUtils from "ip-sub"; import { AS } from "../model"; - export default class InputYml extends Input { - constructor(config){ - super(config); + constructor(env){ + super(env); this.prefixes = []; this.asns = []; + this.options = {}; + this.watcherSet = false; - if (!config.monitoredPrefixesFiles || config.monitoredPrefixesFiles.length === 0) { + if (!this.config.monitoredPrefixesFiles || this.config.monitoredPrefixesFiles.length === 0) { throw new Error("The monitoredPrefixesFiles key is missing in the config file"); + } else if (this.config.monitoredPrefixesFiles.length === 1) { + this.setReGeneratePrefixList(); } }; loadPrefixes = () => { - if (!fs.existsSync('./' + this.config.monitoredPrefixesFiles[0])) { + this.defaultPrefixFile = this.config.volume + this.config.monitoredPrefixesFiles[0]; + if (!fs.existsSync(this.defaultPrefixFile)) { return this.generate() .then(() => this._loadPrefixes()); } @@ -58,42 +62,89 @@ export default class InputYml extends Input { return this._loadPrefixes(); }; + _watchPrefixFile = (file) => { + if (!this.watcherSet) { + this.watcherSet = true; + + fs.watchFile(file, () => { + if (this._watchPrefixFileTimer) { + clearTimeout(this._watchPrefixFileTimer) + } + this._watchPrefixFileTimer = setTimeout(() => { + this.prefixes = []; + this.asns = []; + this._loadPrefixes() + .then(() => { + return this._change(); + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + }, 5000); + }); + } + }; + _loadPrefixes = () => new Promise((resolve, reject) => { const uniquePrefixes = {}; const uniqueAsns = {}; + for (let prefixesFile of this.config.monitoredPrefixesFiles) { + const file = this.config.volume + prefixesFile; let monitoredPrefixesFile = {}; let fileContent; - if (fs.existsSync('./' + prefixesFile)) { - fileContent = fs.readFileSync('./' + prefixesFile, 'utf8'); + if (fs.existsSync(file)) { + fileContent = fs.readFileSync(file, 'utf8'); try { - monitoredPrefixesFile = yaml.safeLoad(fileContent) || {}; + monitoredPrefixesFile = yaml.load(fileContent) || {}; + this._watchPrefixFile(file); } catch (error) { - throw new Error("The file " + prefixesFile + " is not valid yml: " + error.message.split(":")[0]); + reject(new Error("The file " + prefixesFile + " is not valid yml: " + error.message.split(":")[0])); + return; } if (Object.keys(monitoredPrefixesFile).length === 0) { - throw new Error("No prefixes to monitor in " + prefixesFile + ". Please read https://github.com/nttgin/BGPalerter/blob/master/docs/prefixes.md"); + reject(new Error("No prefixes to monitor in " + prefixesFile + ". Please read https://github.com/nttgin/BGPalerter/blob/main/docs/prefixes.md")); + return; } if (this.validate(monitoredPrefixesFile)) { - - if (monitoredPrefixesFile.options && monitoredPrefixesFile.options.monitorASns) { - this.asns = Object - .keys(monitoredPrefixesFile.options.monitorASns) - .map(asn => { - if (uniqueAsns[asn]) { - throw new Error("Duplicate entry for monitored AS " + asn); - } - uniqueAsns[asn] = true; - return Object.assign({ - asn: new AS(asn), - group: 'default' - }, monitoredPrefixesFile.options.monitorASns[asn]); - }); + if (monitoredPrefixesFile.options) { + + this.options = Object.assign(this.options, monitoredPrefixesFile.options); + + if (monitoredPrefixesFile.options.monitorASns) { + const newAsnSet = Object + .keys(monitoredPrefixesFile.options.monitorASns) + .map(asn => { + if (uniqueAsns[asn]) { + reject(new Error("Duplicate entry for monitored AS " + asn)); + return; + } + uniqueAsns[asn] = true; + const item = Object.assign({ + asn: new AS(asn), + group: 'default' + }, monitoredPrefixesFile.options.monitorASns[asn]); + + if (item.upstreams && item.upstreams.length) { + item.upstreams = new AS(item.upstreams); + } + if (item.downstreams && item.downstreams.length) { + item.downstreams = new AS(item.downstreams); + } + + return item; + }); + + this.asns = this.asns.concat(newAsnSet); + } } const monitoredPrefixes = Object @@ -101,7 +152,8 @@ export default class InputYml extends Input { .filter(i => i !== "options") .map(i => { if (uniquePrefixes[i]) { - throw new Error("Duplicate entry for " + i); + reject(new Error("Duplicate entry for " + i)); + return; } uniquePrefixes[i] = true; monitoredPrefixesFile[i].asn = new AS(monitoredPrefixesFile[i].asn); @@ -136,13 +188,20 @@ export default class InputYml extends Input { const options = fileContent.options; if (options && options.monitorASns) { - optionsError = Object - .keys(options.monitorASns) - .map(asn => { - if (!new AS(asn).isValid()) { - return "Not a valid AS number in monitorASns"; - } - }); + + for (let item in options.monitorASns) { + if (!new AS(item).isValid()) { + optionsError.push("Not a valid AS number in monitorASns"); + } + const upstreams = options.monitorASns[item].upstreams; + const downstreams = options.monitorASns[item].downstreams; + if (upstreams && upstreams.length && !new AS(upstreams).isValid()) { + optionsError.push(`One of the upstream ASes provided for AS${item} is not valid`); + } + if (downstreams && downstreams.length && !new AS(downstreams).isValid()) { + optionsError.push(`One of the downstream ASes provided for AS${item} is not valid`); + } + } } prefixesError = Object @@ -199,18 +258,22 @@ export default class InputYml extends Input { } if (item.path) { - if (!item.path.matchDescription){ - return "No matchDescription set"; - } - this._validateRegex(item.path.match); - this._validateRegex(item.path.notMatch); - if (item.path.maxLength && !(typeof(item.path.maxLength) == "number" && item.path.maxLength > 1)) { - return "Not valid maxLength"; - } + ((item.path.length) ? item.path : [item.path]) + .map(rule => { + if (!rule.matchDescription){ + return "No matchDescription set"; + } + this._validateRegex(rule.match); + this._validateRegex(rule.notMatch); + if (rule.maxLength && !(typeof(rule.maxLength) == "number" && rule.maxLength > 1)) { + return "Not valid maxLength"; + } + + if (rule.minLength && !(typeof(rule.minLength) == "number" && rule.minLength > 1)) { + return "Not valid minLength"; + } + }) - if (item.path.minLength && !(typeof(item.path.minLength) == "number" && item.path.minLength > 1)) { - return "Not valid minLength"; - } } return null; @@ -249,37 +312,48 @@ export default class InputYml extends Input { return this.asns; }; - save = () => + save = (content) => new Promise((resolve, reject) => { - const prefixes = {}; - for (let rule of this.prefixes) { - const prefix = rule.prefix; - prefixes[prefix] = { - asn: rule.asn.getValue(), - description: rule.description, - group: rule.group, - ignore: rule.ignore, - ignoreMorespecifics: rule.ignoreMorespecifics, - }; - - if (rule.excludeMonitors.length) prefixes[prefix].excludeMonitors = rule.excludeMonitors; - if (rule.includeMonitors.length) prefixes[prefix].includeMonitors = rule.includeMonitors; + if (content && typeof(content) === "object" && Object.keys(content).length > 0) { + try { + fs.writeFileSync(this.defaultPrefixFile, yaml.dump(content)); + resolve(); + } catch (error) { + reject(error); + } + } else { + reject(new Error("Empty or not valid prefix list")); } + }); - const options = { - options: { - monitorASns: { - } + retrieve = () => + new Promise((resolve, reject) => { + const prefixes = {}; + const monitorASns = {}; + + for (let rule of this.prefixes) { + const item = JSON.parse(JSON.stringify(rule)); + prefixes[rule.prefix] = item; + item.asn = rule.asn.getValue(); + delete item.prefix; + if (!item.includeMonitors.length) { + delete item.includeMonitors; + } + if (!item.excludeMonitors.length) { + delete item.excludeMonitors; } - }; + } for (let asnRule of this.asns) { - options.options.monitorASns[asnRule.asn.getValue()] = { - group: asnRule.group + monitorASns[asnRule.asn.getValue()] = { + group: asnRule.group, + upstreams: asnRule.upstreams ? asnRule.upstreams.numbers : null, + downstreams: asnRule.downstreams ? asnRule.downstreams.numbers : null, }; } - fs.writeFileSync("prefixes.yml", yaml.dump({ ...prefixes, ...options })); - resolve(true) + const options = Object.assign({}, this.options, { monitorASns }); + + resolve({ ...prefixes, options }); }); } \ No newline at end of file diff --git a/src/model.js b/src/model.js index 1ba1faef..e655c424 100644 --- a/src/model.js +++ b/src/model.js @@ -7,6 +7,10 @@ export class Path { return this.value[this.value.length - 1]; }; + length () { + return this.value.length; + }; + toString () { return JSON.stringify(this.toJSON()); }; @@ -17,7 +21,27 @@ export class Path { toJSON () { return this.getValues(); - } + }; + + getNeighbors (asn) { + const path = this.value; + const length = path.length - 1 + for (let n=0; n < length; n++) { + const current = path[n] || null; + if (current.getId() === asn.getId()) { + const left = path[n - 1] || null; + const right = path[n + 1] || null; + + return [left, current, right]; + } + } + + return [null, null, null]; + }; + + includes (asn) { + return this.value.some(i => i.includes(asn)); + }; } @@ -32,24 +56,22 @@ export class AS { if (["string", "number"].includes(typeof(numbers))) { this.numbers = [ numbers ]; } else if (numbers instanceof Array && numbers.length){ - this.ASset = true; - if (numbers.length === 1) { - this.numbers = [ numbers[0] ]; - } else { - this.numbers = numbers; + if (numbers.length > 1) { + this.ASset = true; } + this.numbers = numbers; } if (this.isValid()) { this.numbers = this.numbers.map(i => parseInt(i)); - } - const key = this.numbers.join("-"); - if (!!AS._instances[key]) { - return AS._instances[key]; - } + const key = this.numbers.join("-"); + if (!!AS._instances[key]) { + return AS._instances[key]; + } - AS._instances[key] = this; + AS._instances[key] = this; + } } getId () { @@ -58,7 +80,8 @@ export class AS { isValid () { if (this._valid === null) { - this._valid = this.numbers.length > 0 && + this._valid = this.numbers && + this.numbers.length > 0 && this.numbers .every(asn => { @@ -105,5 +128,5 @@ export class AS { toJSON () { return this.numbers; - } + }; } diff --git a/src/monitors/monitor.js b/src/monitors/monitor.js index 7340f6b1..0e972a9e 100644 --- a/src/monitors/monitor.js +++ b/src/monitors/monitor.js @@ -31,27 +31,37 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import axios from "axios"; +import axiosEnrich from "../utils/axiosEnrich"; + export default class Monitor { - constructor(name, channel, params, env) { + constructor(name, channel, params, env, input) { this.config = env.config; this.pubSub = env.pubSub; this.logger = env.logger; - this.input = env.input; + this.rpki = env.rpki; + this.input = input; + this.storage = env.storage; this.params = params || {}; this.maxDataSamples = this.params.maxDataSamples || 1000; this.name = name; this.channel = channel; this.monitored = []; + this.axios = axiosEnrich(axios, + (!this.params.noProxy && env.agent) ? env.agent : null, + `${env.clientId}/${env.version}`); + this.alerts = {}; // Dictionary containing the alerts . The id is the "group" key of the alert. this.sent = {}; // Dictionary containing the last sent unix timestamp of each group this.truncated = {}; // Dictionary containing if the alerts Array for "id" is truncated according to maxDataSamples - this.fadeOff = {}; // Dictionary containing the last alert unix timestamp of each group which contains alerts that have been triggered but are not ready yet to be sent (e.g. thresholdMinPeers not yet reached) + this.fadeOff = {}; // Dictionary containing the last alert unix timestamp of each group which contains alerts that have been triggered but are not ready yet to be sent (e.g., thresholdMinPeers not yet reached) + this._retrieveStatus(); this.internalConfig = { notificationInterval: (this.config.notificationIntervalSeconds || 14400) * 1000, - checkFadeOffGroups: this.config.checkFadeOffGroupsSeconds || 30 * 1000, + checkFadeOffGroups: (this.config.checkFadeOffGroupsSeconds || 30) * 1000, fadeOff: this.config.fadeOffSeconds * 1000 || 60 * 6 * 1000 }; @@ -81,28 +91,30 @@ export default class Monitor { _squash = (id) => { - const alerts = this.alerts[id]; - const message = this.squashAlerts(alerts); + const alerts = this.alerts[id] || []; + if (alerts && alerts.length) { + const message = this.squashAlerts(alerts); - if (message) { - const firstAlert = alerts[0]; - let earliest = Infinity; - let latest = -Infinity; + if (message) { + const firstAlert = alerts[0]; + let earliest = Infinity; + let latest = -Infinity; - for (let alert of alerts) { - earliest = Math.min(alert.timestamp, earliest); - latest = Math.max(alert.timestamp, latest); - } + for (let alert of alerts) { + earliest = Math.min(alert.timestamp, earliest); + latest = Math.max(alert.timestamp, latest); + } - return { - id, - truncated: this.truncated[id] || false, - origin: this.name, - earliest, - latest, - affected: firstAlert.affected, - message, - data: alerts + return { + id, + truncated: this.truncated[id] || false, + origin: this.name, + earliest, + latest, + affected: firstAlert.affected, + message, + data: alerts + } } } }; @@ -124,8 +136,10 @@ export default class Monitor { this.alerts[id].push(context); // Check if for each alert group the maxDataSamples parameter is respected - if (!this.truncated[id] && this.alerts[id].length > this.maxDataSamples) { - this.truncated[id] = this.alerts[id][0].timestamp; // Mark as truncated + if (this.alerts[id].length > this.maxDataSamples) { + if (!this.truncated[id]) { + this.truncated[id] = this.alerts[id][0].timestamp; // Mark as truncated + } this.alerts[id] = this.alerts[id].slice(-this.maxDataSamples); // Truncate } @@ -153,6 +167,52 @@ export default class Monitor { } }; + _retrieveStatus = () => { + if (this.config.persistStatus && this.storage) { + this.storage + .get(`status-${this.name}`) + .then(({ sent={}, truncated={}, fadeOff={} }) => { + this.sent = sent; + this.truncated = truncated; + this.fadeOff = fadeOff; + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + } + }; + + _persistStatus = () => { + if (this._persistStatusTimer){ + clearTimeout(this._persistStatusTimer); + } + this._persistStatusTimer = setTimeout(this._persistStatusHelper, 5000); + }; + + _persistStatusHelper = () => { + if (this.config.persistStatus && this.storage) { + const status = { + sent: this.sent, + truncated: this.truncated, + fadeOff: this.fadeOff + }; + + if (Object.values(status).some(i => Object.keys(i).length > 0)) { // If there is anything in the cache + this.storage + .set(`status-${this.name}`, status) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + } + } + }; + _publishGroupId = (id, now) => { const group = this._squash(id); @@ -180,20 +240,48 @@ export default class Monitor { _publishOnChannel = (alert) => { this.pubSub.publish(this.channel, alert); + this._persistStatus(); return alert; }; - getMoreSpecificMatch = (prefix, includeIgnoredMorespecifics) => { + getMonitoredAsMatch = (originAS) => { + const monitored = this.input.getMonitoredASns(); + + for (let m of monitored) { + if (originAS.includes(m.asn)) { + return m; + } + } + + return null; + }; + + _included = (matched) => { + if (matched.includeMonitors.length > 0) { + return matched.includeMonitors.includes(this.name); + } else { + return !matched.excludeMonitors.includes(this.name); + } + }; + + getMoreSpecificMatch = (prefix, includeIgnoredMorespecifics, verbose=false) => { const matched = this.input.getMoreSpecificMatch(prefix, includeIgnoredMorespecifics); if (matched) { - if (matched.includeMonitors.length > 0 && !matched.includeMonitors.includes(this.name)) { - return null; + const included = this._included(matched); + + if (verbose) { + return { + matched, + included + }; + } else if (included) { + return matched; } - - return (matched.excludeMonitors.includes(this.name)) ? null : matched; } + + return null; }; } \ No newline at end of file diff --git a/src/monitors/monitorAS.js b/src/monitors/monitorAS.js index 981ed259..77529495 100644 --- a/src/monitors/monitorAS.js +++ b/src/monitors/monitorAS.js @@ -34,14 +34,14 @@ import Monitor from "./monitor"; export default class MonitorAS extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); - this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 0; + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); + this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 3; this.updateMonitoredResources(); }; updateMonitoredResources = () => { - this.monitored = this.input.getMonitoredASns(); + // nothing }; filter = (message) => { @@ -69,28 +69,17 @@ export default class MonitorAS extends Monitor { return `${matchedMessages[0].originAS} is announcing some prefixes which are not in the configured list of announced prefixes: ${prefixesOut}` } else if (prefixesOut.length === 1) { return `${matchedMessages[0].originAS} is announcing ${matchedMessages[0].prefix} but this prefix is not in the configured list of announced prefixes`; - } return false; }; - _getMonitoredAS = (message) => { - const monitored = this.monitored; - - for (let m of monitored) { - if (message.originAS.includes(m.asn)) { - return m; - } - } - }; - monitor = (message) => new Promise((resolve, reject) => { const messageOrigin = message.originAS; const messagePrefix = message.prefix; - const matchedRule = this._getMonitoredAS(message); + const matchedRule = this.getMonitoredAsMatch(messageOrigin); if (matchedRule) { diff --git a/src/monitors/monitorHijack.js b/src/monitors/monitorHijack.js index 538f5e24..544d5390 100644 --- a/src/monitors/monitorHijack.js +++ b/src/monitors/monitorHijack.js @@ -35,8 +35,8 @@ import ipUtils from "ip-sub"; export default class MonitorHijack extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 2; this.updateMonitoredResources(); }; @@ -66,22 +66,29 @@ export default class MonitorHijack extends Monitor { return false; }; + validate = (message, matchedRule) => { + this.rpki.addToValidationQueue(message, matchedRule, this._validate); + }; + + _validate = (result, message, matchedRule) => { + if (!result.valid) { + this.publishAlert(message.originAS.getId() + "-" + message.prefix, + matchedRule.asn.getId(), + matchedRule, + message, + {}); + } + } + monitor = (message) => new Promise((resolve, reject) => { - const messagePrefix = message.prefix; const matchedRule = this.getMoreSpecificMatch(messagePrefix, false); if (matchedRule && !matchedRule.ignore && !matchedRule.asn.includes(message.originAS)) { - - this.publishAlert(message.originAS.getId() + "-" + message.prefix, - matchedRule.asn.getId(), - matchedRule, - message, - {}); + this.validate(message, matchedRule); + resolve(true); } - - resolve(true); }); } \ No newline at end of file diff --git a/src/monitors/monitorNewPrefix.js b/src/monitors/monitorNewPrefix.js index 3744752b..1e341e36 100644 --- a/src/monitors/monitorNewPrefix.js +++ b/src/monitors/monitorNewPrefix.js @@ -31,12 +31,13 @@ */ import Monitor from "./monitor"; +import ipUtils from "ip-sub"; export default class MonitorNewPrefix extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); - this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 2; + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); + this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 3; this.updateMonitoredResources(); }; @@ -56,7 +57,6 @@ export default class MonitorNewPrefix extends Monitor { const matchedRule = alerts[0].matchedRule; return `Possible change of configuration. A new prefix ${message.prefix} is announced by ${message.originAS}. It is a more specific of ${matchedRule.prefix} (${matchedRule.description})`; - } return false; @@ -68,7 +68,9 @@ export default class MonitorNewPrefix extends Monitor { const messagePrefix = message.prefix; const matchedRule = this.getMoreSpecificMatch(messagePrefix, false); - if (matchedRule && !matchedRule.ignore && matchedRule.asn.includes(message.originAS) && matchedRule.prefix !== messagePrefix) { + if (matchedRule && !matchedRule.ignore && + matchedRule.asn.includes(message.originAS) && + !ipUtils._isEqualPrefix(matchedRule.prefix, messagePrefix)) { this.publishAlert(message.originAS.getId() + "-" + message.prefix, matchedRule.asn.getId(), diff --git a/src/monitors/monitorPassthrough.js b/src/monitors/monitorPassthrough.js index 411e2ac7..bf8d5b82 100644 --- a/src/monitors/monitorPassthrough.js +++ b/src/monitors/monitorPassthrough.js @@ -2,17 +2,22 @@ import Monitor from "./monitor"; export default class monitorPassthrough extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); this.count = 0; }; + updateMonitoredResources = () => { + // nothing + }; + filter = () => { return true }; squashAlerts = (alerts) => { - return JSON.stringify(alerts[0]); + const item = alerts[0].matchedMessage; + return `type:${item.type} timestamp:${alerts[0].timestamp} prefix:${item.prefix} peer:${item.peer} path:${item.path} nextHop:${item.nextHop} aggregator:${item.aggregator}`; }; monitor = (message) => @@ -20,16 +25,16 @@ export default class monitorPassthrough extends Monitor { const prefix = message.prefix; this.publishAlert(this.count, prefix, - {}, + { + prefix: "0.0.0.0/0", + asn: "1234", + description: "test" + }, message, {}); this.count++; resolve(true); - }); - - - } diff --git a/src/monitors/monitorPath.js b/src/monitors/monitorPath.js index 1c8955c9..1e7b0548 100644 --- a/src/monitors/monitorPath.js +++ b/src/monitors/monitorPath.js @@ -34,9 +34,9 @@ import Monitor from "./monitor"; export default class MonitorPath extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); - this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 0; + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); + this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 1; this.updateMonitoredResources(); }; @@ -54,12 +54,56 @@ export default class MonitorPath extends Monitor { if (peers >= this.thresholdMinPeers) { const lengthViolation = (alerts.some(i => i.extra.lengthViolation)) ? "(including length violation) " : ""; - return `Matched ${alerts[0].matchedRule.path.matchDescription} on prefix ${alerts[0].matchedMessage.prefix} ${lengthViolation}${alerts.length} times`; + return `Matched ${alerts[0].extra.matchDescription} on prefix ${alerts[0].matchedMessage.prefix} ${lengthViolation}${alerts.length} times`; } return false; }; + pathRuleCheck = (pathRule, index, message, matchedRule) => { + const messagePrefix = message.prefix; + const pathString = message.path.getValues().join(","); + + let expMatch = true; + let expNotMatch = true; + let correctLength = true; + + if (pathRule.match) { + expMatch = (new RegExp(pathRule.match)).test(pathString); + if (!expMatch) { + return; + } + } + + if (pathRule.notMatch){ + expNotMatch = !(new RegExp(pathRule.notMatch)).test(pathString); + if (!expNotMatch) { + return; + } + } + + if (pathRule.maxLength && message.path.getValues().length > pathRule.maxLength) { + correctLength = false; + } + + if (pathRule.minLength && message.path.getValues().length < pathRule.minLength) { + correctLength = false; + } + + if (expMatch && expNotMatch && + ((!pathRule.maxLength && !pathRule.maxLength) || !correctLength)) { + + this.publishAlert(`${messagePrefix}-${index}`, + matchedRule.prefix, + matchedRule, + message, + { + lengthViolation: !correctLength, + matchDescription: pathRule.matchDescription + }); + } + }; + monitor = (message) => new Promise((resolve, reject) => { @@ -67,47 +111,10 @@ export default class MonitorPath extends Monitor { const matchedRule = this.getMoreSpecificMatch(messagePrefix, false); if (matchedRule && !matchedRule.ignore && matchedRule.path) { - const pathString = message.path.getValues().join(","); - - let expMatch = true; - let expNotMatch = true; - let correctLength = true; - - if (matchedRule.path.match) { - expMatch = (new RegExp(matchedRule.path.match)).test(pathString); - if (!expMatch) { - resolve(true); - return; - } - } - - if (matchedRule.path.notMatch){ - expNotMatch = !(new RegExp(matchedRule.path.notMatch)).test(pathString); - if (!expNotMatch) { - resolve(true); - return; - } - } - - if (matchedRule.path.maxLength && message.path.getValues().length > matchedRule.path.maxLength) { - correctLength = false; - } - - if (matchedRule.path.minLength && message.path.getValues().length < matchedRule.path.minLength) { - correctLength = false; - } - - if (expMatch && expNotMatch && - ((!matchedRule.path.maxLength && !matchedRule.path.maxLength) || !correctLength)) { - - this.publishAlert(messagePrefix, - matchedRule.prefix, - matchedRule, - message, - { - lengthViolation: !correctLength - }); - } + const pathRules = (matchedRule.path.length) ? matchedRule.path : [ matchedRule.path ]; + + pathRules.map((pathRule, position) => this.pathRuleCheck(pathRule, position, message, matchedRule)); + } resolve(true); diff --git a/src/monitors/monitorPathNeighbors.js b/src/monitors/monitorPathNeighbors.js new file mode 100644 index 00000000..72c5ea17 --- /dev/null +++ b/src/monitors/monitorPathNeighbors.js @@ -0,0 +1,121 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import Monitor from "./monitor"; + +export default class MonitorPathNeighbors extends Monitor { + + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); + this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 0; + this.updateMonitoredResources(); + }; + + updateMonitoredResources = () => { + this.monitored = this.input.getMonitoredASns(); + }; + + filter = (message) => { + return message.type === 'announcement'; + }; + + squashAlerts = (alerts) => { + const peers = [...new Set(alerts.map(alert => alert.matchedMessage.peer))].length; + + if (peers >= this.thresholdMinPeers) { + const matchedRule = alerts[0].matchedRule; + const extra = alerts[0].extra; + const asnText = matchedRule.asn; + + return `A new ${extra.side} of ${asnText} has been detected: AS${extra.neighbor}`; + } + + return false; + }; + + monitor = (message) => + new Promise((resolve, reject) => { + const path = message.path; + + for (let monitoredAs of this.monitored) { + if (monitoredAs.upstreams || monitoredAs.downstreams) { + const [left, _, right] = path.getNeighbors(monitoredAs.asn); + + if (!!left || !!right) { + let match = false; + let side = null; + let id = null; + + if (left) { + if (monitoredAs.upstreams === null) { + side = "upstream"; + id = left.getId(); + match = true; + } else if (monitoredAs.upstreams && !monitoredAs.upstreams.includes(left)) { + side = "upstream"; + id = left.getId(); + match = true; + } + } + + if (right) { + if (monitoredAs.downstreams === null) { + side = "downstream"; + id = right.getId(); + match = true; + } else if (monitoredAs.downstreams && !monitoredAs.downstreams.includes(right)) { + side = "downstream"; + id = right.getId(); + match = true; + } + } + + + if (match) { + const monitoredId = monitoredAs.asn.getId(); + + if (monitoredId !== id) { // Skip prepending + this.publishAlert([monitoredId, id].join("-"), + monitoredId, + monitoredAs, + message, + {side, neighbor: id}); + } + } + } + } + } + + resolve(true); + }); + +} \ No newline at end of file diff --git a/src/monitors/monitorROAS.js b/src/monitors/monitorROAS.js new file mode 100644 index 00000000..af7a7de7 --- /dev/null +++ b/src/monitors/monitorROAS.js @@ -0,0 +1,299 @@ +import Monitor from "./monitor"; +import md5 from "md5"; +import { getPrefixes, getRelevant, diff } from "../utils/rpkiDiffingTool"; +import {AS} from "../model"; +import moment from "moment"; + +export default class MonitorROAS extends Monitor { + + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); + this.logger = env.logger; + this.rpki = env.rpki; + + // Enabled checks + this.enableDiffAlerts = params.enableDiffAlerts != null ? params.enableDiffAlerts : true; + this.enableExpirationAlerts = params.enableExpirationAlerts != null ? params.enableExpirationAlerts : true; + this.enableExpirationCheckTA = params.enableExpirationCheckTA != null ? params.enableExpirationCheckTA : true; + this.enableDeletedCheckTA = params.enableDeletedCheckTA != null ? params.enableDeletedCheckTA : true; + + // Default parameters + this.roaExpirationAlertHours = params.roaExpirationAlertHours || 2; + this.checkOnlyASns = params.checkOnlyASns != null ? params.checkOnlyASns : true; + + this.toleranceExpiredRoasTA = params.toleranceExpiredRoasTA || 20; + this.toleranceDeletedRoasTA = params.toleranceDeletedRoasTA || 20; + this.timesDeletedTAs = {}; + this.seenTAs = {}; + this.monitored = { + asns: [], + prefixes: [] + }; + + if (this.enableDiffAlerts || this.enableDeletedCheckTA) { + setInterval(this._diffVrps, 30000); + } + if (this.enableExpirationAlerts || this.enableExpirationCheckTA) { + setInterval(this._verifyExpiration, global.EXTERNAL_ROA_EXPIRATION_TEST || 600000); + } + }; + + _calculateSizes = (vrps) => { + const times = {}; + + for (let ta in this.seenTAs) { + times[ta] = 0; + } + + for (let vrp of vrps) { + times[vrp.ta] = times[vrp.ta] || 0; + times[vrp.ta]++ + this.seenTAs[vrp.ta] = true; + } + + return times; + }; + + _checkDeletedRoasTAs = (vrps) => { + const sizes = this._calculateSizes(vrps); + + for (let ta in sizes) { + if (this.timesDeletedTAs[ta]) { + const min = Math.min(this.timesDeletedTAs[ta], sizes[ta]); + const max = Math.max(this.timesDeletedTAs[ta], sizes[ta]); + const diff = max - min; + const percentage = 100 / max * diff; + + if (percentage > this.toleranceDeletedRoasTA) { + const message = `Possible TA malfunction: ${percentage.toFixed(2)}% of the ROAs disappeared from ${ta}`; + + this.publishAlert(`disappeared-${ta}`, // The hash will prevent alert duplications in case multiple ASes/prefixes are involved + ta, + { + group: "default" + }, + message, + {}); + } + } + } + this.timesDeletedTAs = sizes; + }; + + _checkExpirationTAs = (vrps, expiringVrps) => { + const sizes = this._calculateSizes(vrps); + const expiringSizes = this._calculateSizes(expiringVrps); + + for (let ta in sizes) { + const min = expiringSizes[ta]; + const max = sizes[ta]; + const percentage = (100 / max) * min; + + if (percentage > this.toleranceExpiredRoasTA) { + const message = `Possible TA malfunction: ${percentage.toFixed(2)}% of the ROAs are expiring in ${ta}`; + + this.publishAlert(`expiring-${ta}`, // The hash will prevent alert duplications in case multiple ASes/prefixes are involved + ta, + { group: "default" }, + message, + {}); + } + } + }; + + _verifyExpiration = () => { + const roas = this.rpki.getVrps(); + const expiringRoas = roas + .filter(i => !!i.expires && (i.expires - moment.utc().unix() < this.roaExpirationAlertHours * 3600)); + + if (this.enableExpirationCheckTA) { + this._checkExpirationTAs(roas, expiringRoas); // Check for TA malfunctions + } + + if (this.enableExpirationAlerts) { + const prefixesIn = this.monitored.prefixes.map(i => i.prefix); + const asnsIn = this.monitored.asns.map(i => i.asn.getValue()); + const relevantVrps = getRelevant(expiringRoas, prefixesIn, asnsIn); + + let alerts = []; + if (relevantVrps.length) { + if (!this.checkOnlyASns) { + alerts = this._checkExpirationPrefixes(relevantVrps); + } + for (let asn of asnsIn) { + this._checkExpirationAs(relevantVrps, asn, alerts); + } + } + } + }; + + _checkExpirationPrefixes = (vrps) => { + let alerts = []; + + for (let prefix of [...new Set(vrps.map(i => i.prefix))]) { + + const roas = vrps.filter(i => i.prefix === prefix); // Get only the ROAs for this prefix + const matchedRule = this.getMoreSpecificMatch(prefix, false); // Get the matching rule + if (matchedRule) { + const alertsStrings = [...new Set(roas.map(this._roaToString))]; + const message = `The following ROAs will expire in less than ${this.roaExpirationAlertHours} hours: ${alertsStrings.join("; ")}`; + alerts = alerts.concat(alertsStrings); + + this.publishAlert(md5(message), // The hash will prevent alert duplications in case multiple ASes/prefixes are involved + matchedRule.prefix, + matchedRule, + message, + {}); + } + } + + return alerts; + }; + + _checkExpirationAs = (vrps, asn, sent) => { + try { + let alerts = []; + const impactedASes = [...new Set(vrps.map(i => i.asn))]; + const matchedRules = impactedASes.map(asn => this.getMonitoredAsMatch(new AS(asn))); + + for (let matchedRule of matchedRules.filter(i => !!i)) { // An alert for each AS involved (they may have different user group) + const alertsStrings = [...new Set(vrps.map(this._roaToString))].filter(i => !sent.includes(i)); + if (alertsStrings.length) { + const message = `The following ROAs will expire in less than ${this.roaExpirationAlertHours} hours: ${alertsStrings.join("; ")}`; + alerts = alerts.concat(alertsStrings); + + this.publishAlert(md5(message), // The hash will prevent alert duplications in case multiple ASes/prefixes are involved + matchedRule.asn.getId(), + matchedRule, + message, + {}); + } + } + + return alerts; + } catch (error) { + this.logger.log({ + level: 'error', + message: error + }); + } + }; + + _diffVrps = () => { + const newVrps = this.rpki.getVrps(); // Get all the vrps as retrieved from the rpki validator + + if (this.enableDeletedCheckTA) { + this._checkDeletedRoasTAs(newVrps); // Check for TA malfunctions for too many deleted roas + } + + if (this.enableDiffAlerts) { + if (this._oldVrps) { // No diff if there were no vrps before + const prefixesIn = this.monitored.prefixes.map(i => i.prefix); + const asns = this.monitored.asns.map(i => i.asn.getValue()); + let alerts = []; + if (!this.checkOnlyASns) { + alerts = this._diffVrpsPrefixes(this._oldVrps, newVrps, prefixesIn); + } + for (let asn of asns) { + this._diffVrpsAs(this._oldVrps, newVrps, asn, alerts); + } + } + + if (newVrps.length) { + this._oldVrps = newVrps; + } + } + }; + + _diffVrpsPrefixes = (oldVrps, newVrps, prefixesIn) => { + try { + const roaDiff = diff(oldVrps, newVrps, [], prefixesIn); + let alerts = []; + + if (roaDiff && roaDiff.length) { // Differences found + for (let prefix of [...new Set(roaDiff.map(i => i.prefix))]) { + + const roas = roaDiff.filter(i => i.prefix === prefix); // Get only the ROAs for this prefix + const matchedRule = this.getMoreSpecificMatch(prefix, false); // Get the matching rule + if (matchedRule) { + const alertsStrings = [...new Set(roas.map(this._roaToString))]; + const message = `ROAs change detected: ${alertsStrings.join("; ")}`; + alerts = alerts.concat(alertsStrings); + + this.publishAlert(md5(message), // The hash will prevent alert duplications in case multiple ASes/prefixes are involved + matchedRule.prefix, + matchedRule, + message, + {}); + } + } + } + + return alerts; + } catch (error) { + this.logger.log({ + level: 'error', + message: error + }); + } + }; + + _diffVrpsAs = (oldVrps, newVrps, asn, sent) => { + try { + const roaDiff = diff(oldVrps, newVrps, asn, []); + let alerts = []; + + if (roaDiff && roaDiff.length) { // Differences found + + const impactedASes = [...new Set(roaDiff.map(i => i.asn))]; + const matchedRules = impactedASes.map(asn => this.getMonitoredAsMatch(new AS(asn))); + + for (let matchedRule of matchedRules.filter(i => !!i)) { // An alert for each AS involved (they may have different user group) + const alertsStrings = [...new Set(roaDiff.map(this._roaToString))].filter(i => !sent.includes(i)); + if (alertsStrings.length) { + const message = `ROAs change detected: ${alertsStrings.join("; ")}`; + alerts = alerts.concat(alertsStrings); + + this.publishAlert(md5(message), // The hash will prevent alert duplications in case multiple ASes/prefixes are involved + matchedRule.asn.getId(), + matchedRule, + message, + {}); + } + } + } + + return alerts; + } catch (error) { + this.logger.log({ + level: 'error', + message: error + }); + } + }; + + _roaToString = (roa) => { + if (roa.status) { + return `${roa.status} <${roa.prefix}, ${roa.asn}, ${roa.maxLength}, ${roa.ta || ""}>`; + } else { + return `<${roa.prefix}, ${roa.asn}, ${roa.maxLength}, ${roa.ta || ""}>`; + } + }; + + updateMonitoredResources = () => { + this.monitored = { + asns: this.input.getMonitoredASns(), + prefixes: this.input.getMonitoredPrefixes() + } + }; + + filter = (message) => false; + + squashAlerts = (alerts) => { + return (alerts[0]) ? alerts[0].matchedMessage : false; + }; + + monitor = (message) => { + return Promise.resolve(true); + }; +} diff --git a/src/monitors/monitorRPKI.js b/src/monitors/monitorRPKI.js index 2037ba6e..f13b72f2 100644 --- a/src/monitors/monitorRPKI.js +++ b/src/monitors/monitorRPKI.js @@ -1,79 +1,207 @@ + +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + import Monitor from "./monitor"; -import rpki from "rpki-validator"; export default class MonitorRPKI extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); + + // Warn about deprecated config parameters + for (const configParamKey of Object.keys(params)) { + const deprecated = ["preCacheROAs", "refreshVrpListMinutes", "vrpFile", "vrpProvider"]; + if (deprecated.includes(configParamKey)) { + this.logger.log({ + level: 'error', + message: `The parameters ${deprecated.join(",")} are deprecated in monitorRPKI. Please use see here: https://github.com/nttgin/BGPalerter/blob/main/docs/rpki.md` + }); + } + } + + this.rpki = env.rpki; + this.cacheValidPrefixesMs = (this.params.cacheValidPrefixesSeconds || 3600 * 24 * 7) * 1000; this.input.onChange(() => { this.updateMonitoredResources(); }); - this.validationQueue = []; - - rpki.preCache(60) - .then(() => { - setInterval(this.validateBatch, 400); - }) - - }; - validateBatch = () => { - const queue = this.validationQueue; - this.validationQueue = []; + this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 1; + this.seenRpkiValidAnnouncementsKey = "seen-rpki-valid-announcements"; - queue.forEach(this.validate); + this.storage // Reload the previously discovered ROAs (needed to alert in case of disappearing ROAs) + .get(this.seenRpkiValidAnnouncementsKey) + .then(prefixes => { + this.seenRpkiValidAnnouncements = (prefixes) ? prefixes : {}; + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + + this.queue = []; }; - updateMonitoredPrefixes = () => { - this.monitored = this.input.getMonitoredPrefixes(); + updateMonitoredResources = () => { + this.monitored = this.input.getMonitoredASns(); }; filter = (message) => { - return message.type === 'announcement' && message.originAS.numbers.length == 1; + return message.type === 'announcement'; }; squashAlerts = (alerts) => { - const message = alerts[0].matchedMessage; - const covering = (alerts[0].extra.covering && alerts[0].extra.covering[0]) ? alerts[0].extra.covering[0] : false; - const coveringString = (covering) ? `Valid ROA: origin AS${covering.origin} prefix ${covering.prefix} max length ${covering.maxLength}` : ''; - - return `The route ${message.prefix} announced by ${message.originAS} is not RPKI valid. Accepted with AS path: ${message.path}. ${coveringString}`; + const peers = [...new Set(alerts.map(alert => alert.matchedMessage.peer))].length; + + if (peers >= this.thresholdMinPeers) { + const firstAlert = alerts[0]; + const message = firstAlert.matchedMessage; + const extra = firstAlert.extra; + const covering = (extra.covering && extra.covering.length) ? extra.covering.map(i => `${i.prefix}|AS${i.asn}|maxLength:${i.maxLength}`).join(", ") : false; + const coveringString = (covering) ? `. Valid ROAs: ${covering}`: ''; + + if (extra.roaDisappeared && this.params.checkDisappearing) { + return `The route ${message.prefix} announced by ${message.originAS} is no longer covered by a ROA`; + } else if (extra.valid === null && this.params.checkUncovered) { + return `The route ${message.prefix} announced by ${message.originAS} is not covered by a ROA`; + } else if (extra.valid === false) { + return `The route ${message.prefix} announced by ${message.originAS} is not RPKI valid${coveringString}`; + } + } }; + _validate = (result, message, matchedRule) => { + const prefix = result.prefix; + const origin = result.origin.getValue(); + if (result) { - validate = ({ message, matchedRule} ) => { - const prefix = message.prefix; - const origin = message.originAS.getValue(); - - const result = rpki.validateFromCacheSync(prefix, origin, true); - - if (result.valid === false) { - const key = "a" + [prefix, origin] - .join("AS") + const cacheKey = "a" + [prefix, origin] + .join("-") .replace(/\./g, "_") .replace(/\:/g, "_") .replace(/\//g, "_"); - this.publishAlert(key, - prefix, - matchedRule, - message, - { covering: result.covering }); + const key = `${cacheKey}-${result.valid}`; + + if (result.valid === null) { + + const cache = this.seenRpkiValidAnnouncements[cacheKey]; + if (cache && cache.rpkiValid && (cache.date + this.cacheValidPrefixesMs) >= new Date().getTime()) { // valid cache + this.publishAlert(key, + prefix, + matchedRule, + message, + { covering: null, valid: null, roaDisappeared: true }); + } else if (this.params.checkUncovered) { + this.publishAlert(key, + prefix, + matchedRule, + message, + { covering: null, valid: null }); + } + } else if (result.valid === false) { + this.publishAlert(key, + prefix, + matchedRule, + message, + { covering: result.covering, valid: false }); + + } else if (result.valid) { + + // Refresh dictionary + this.seenRpkiValidAnnouncements[cacheKey] = { + date: new Date().getTime(), + rpkiValid: true + }; + + if (this.seenRpkiValidAnnouncementsTimer) { + clearTimeout(this.seenRpkiValidAnnouncementsTimer); + } + + // Store dictionary + this.seenRpkiValidAnnouncementsTimer = setTimeout(() => { + const now = new Date().getTime(); + + // Delete old cache items + for (let roa of Object.keys(this.seenRpkiValidAnnouncements)) { + if (this.seenRpkiValidAnnouncements[roa].date + this.cacheValidPrefixesMs < now) { + delete this.seenRpkiValidAnnouncements[roa]; + } + } + this.storage + .set(this.seenRpkiValidAnnouncementsKey, this.seenRpkiValidAnnouncements) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + }, 1000); + + } } }; + validate = (message, matchedRule) => { + this.rpki.addToValidationQueue(message, matchedRule, this._validate); + }; monitor = (message) => { - const prefix = message.prefix; - const matchedRule = this.input.getMoreSpecificMatch(prefix, false); - - if (matchedRule) { - this.validationQueue.push({ message, matchedRule }); + try { + const messageOrigin = message.originAS; + const prefix = message.prefix; + + const matchedPrefixRule = this.getMoreSpecificMatch(prefix, false, true); + + if (matchedPrefixRule && matchedPrefixRule.matched) { // There is a prefix match + if (!matchedPrefixRule.matched.ignore && matchedPrefixRule.included) { // The prefix match is not excluded in any way + this.validate(message, matchedPrefixRule.matched); + } + } else { // No prefix match + const matchedASRule = this.getMonitoredAsMatch(messageOrigin); // Try AS match + if (matchedASRule) { + this.validate(message, matchedASRule); + } + } + } catch (error) { + this.logger.log({ + level: 'error', + message: error + }); } + return Promise.resolve(true); }; - - - } diff --git a/src/monitors/monitorSwUpdates.js b/src/monitors/monitorSwUpdates.js index b4cba0e0..53f091b5 100644 --- a/src/monitors/monitorSwUpdates.js +++ b/src/monitors/monitorSwUpdates.js @@ -34,12 +34,12 @@ import Monitor from "./monitor"; export default class MonitorSwUpdates extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); }; updateMonitoredResources = () => { - // throw new Error('The method updateMonitoredResources must be implemented in ' + this.name); + // nothing }; filter = (message) => { diff --git a/src/monitors/monitorVisibility.js b/src/monitors/monitorVisibility.js index 07450441..2b9271a7 100644 --- a/src/monitors/monitorVisibility.js +++ b/src/monitors/monitorVisibility.js @@ -35,9 +35,9 @@ import ipUtils from "ip-sub"; export default class MonitorVisibility extends Monitor { - constructor(name, channel, params, env){ - super(name, channel, params, env); - this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 10; + constructor(name, channel, params, env, input){ + super(name, channel, params, env, input); + this.thresholdMinPeers = (params && params.thresholdMinPeers != null) ? params.thresholdMinPeers : 40; if (params.threshold) { this.logger.log({ level: 'error', diff --git a/src/processMonitors/uptime.js b/src/processMonitors/uptime.js index b4f8145e..53f9e288 100644 --- a/src/processMonitors/uptime.js +++ b/src/processMonitors/uptime.js @@ -30,11 +30,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import axios from "axios"; +import env from "../env"; +import axiosEnrich from "../utils/axiosEnrich"; + export default class Uptime { constructor(connectors, params){ this.connectors = connectors; this.params = params; + + this.axios = axiosEnrich(axios, + (!this.params.noProxy && env.agent) ? env.agent : null, + `${env.clientId}/${env.version}`); }; @@ -42,7 +50,7 @@ export default class Uptime { const connectors = this.connectors .getConnectors() .filter(connector => { - return connector.constructor.name != "ConnectorSwUpdates"; + return connector.constructor.name !== "ConnectorSwUpdates"; }) .map(connector => { return { @@ -52,9 +60,14 @@ export default class Uptime { }); const disconnected = connectors.some(connector => !connector.connected); + const rpki = env.rpki.getStatus(); + + const warning = disconnected || !rpki.data || rpki.stale; + return { - warning: disconnected, + warning, connectors, + rpki }; }; diff --git a/src/processMonitors/uptimeApi.js b/src/processMonitors/uptimeApi.js index aad93b6f..e1405f5a 100644 --- a/src/processMonitors/uptimeApi.js +++ b/src/processMonitors/uptimeApi.js @@ -31,8 +31,8 @@ */ import Uptime from "./uptime"; -import restify from "restify"; import env from "../env"; +import RestApi from "../utils/restApi"; export default class UptimeApi extends Uptime { @@ -40,22 +40,16 @@ export default class UptimeApi extends Uptime { super(connectors, params); this.server = null; this.connectors = connectors; - try { - this.server = restify.createServer(); - this.server.pre(restify.pre.sanitizePath()); - this.server.get('/status', this.respond); - this.server.head('/status', this.respond); - if (params.host) { - this.server.listen(params.port, params.host); - } else { - this.server.listen(params.port); - } - } catch (error) { - env.logger.log({ - level: 'error', - message: error + let restDefault = env.config.rest || { port: params.port, host: params.host }; + const rest = new RestApi(restDefault); + + rest.addUrl('/status', this.respond) + .catch(error => { + env.logger.log({ + level: 'error', + message: error + }); }); - } }; respond = (req, res, next) => { diff --git a/src/processMonitors/uptimeHealthcheck.js b/src/processMonitors/uptimeHealthcheck.js index ab47b8f1..f65f61ed 100644 --- a/src/processMonitors/uptimeHealthcheck.js +++ b/src/processMonitors/uptimeHealthcheck.js @@ -31,7 +31,6 @@ */ import Uptime from "./uptime"; -import axios from "axios"; import env from "../env"; export default class UptimeHealthcheck extends Uptime { @@ -57,11 +56,11 @@ export default class UptimeHealthcheck extends Uptime { query.data = status; } - axios(query) + this.axios(query) .catch(error => { env.logger.log({ level: 'error', - message: error + message: `UptimeHealthcheck cannot send heartbeat: ${error.message}` }); }); } diff --git a/src/reports/email_templates/emailTemplates.js b/src/reports/email_templates/emailTemplates.js index b32db27e..fa41afe9 100644 --- a/src/reports/email_templates/emailTemplates.js +++ b/src/reports/email_templates/emailTemplates.js @@ -69,7 +69,18 @@ Last event: ${latest} UTC\n\ Detected by peers: ${peers}\n\ See in BGPlay: ${bgplay}'; -const templatePath = '${summary}'; +const templatePath = '${summary}\n\ +\n\ +\n\ +DETAILS:\n\ +------------------------------------------------------\n\ +Event type: ${type}\n\ +When event started: ${earliest} UTC\n\ +Last event: ${latest} UTC\n\ +\n\ +\n\ +Top ${pathNumber} triggering AS paths:\n\ +${paths}'; const templateSoftwareUpdate = '${summary}'; @@ -89,9 +100,27 @@ See in BGPlay: ${bgplay}'; const templateMisconfiguration = '${summary}\n\ \n\ +\n\ +DETAILS:\n\ +------------------------------------------------------\n\ +Event type: ${type}\n\ +When event started: ${earliest} UTC\n\ +Last event: ${latest} UTC\n\ +\n\ +\n\ Top ${pathNumber} most used AS paths:\n\ ${paths}'; + +const templateRPKI = '${summary}\n\ +\n\ +\n\ +DETAILS:\n\ +------------------------------------------------------\n\ +Event type: ${type}\n\ +When event started: ${earliest} UTC\n\ +Last event: ${latest} UTC'; + const defaultTemplate = '${summary}'; export default class emailTemplates { @@ -122,6 +151,10 @@ export default class emailTemplates { { channel: 'misconfiguration', content: templateMisconfiguration + }, + { + channel: 'rpki', + content: templateRPKI } ]; diff --git a/src/reports/email_templates/misconfiguration.txt b/src/reports/email_templates/misconfiguration.txt index d11e4ec0..a0ad34ab 100644 --- a/src/reports/email_templates/misconfiguration.txt +++ b/src/reports/email_templates/misconfiguration.txt @@ -1,5 +1,10 @@ ${summary} +DETAILS: +------------------------------------------------------ +Event type: ${type} +When event started: ${earliest} UTC +Last event: ${latest} UTC Top ${pathNumber} most used AS paths: ${paths} \ No newline at end of file diff --git a/src/reports/email_templates/path.txt b/src/reports/email_templates/path.txt index 1e96ae79..84340952 100644 --- a/src/reports/email_templates/path.txt +++ b/src/reports/email_templates/path.txt @@ -1 +1,12 @@ -${summary} \ No newline at end of file +${summary} + + +DETAILS: +------------------------------------------------------ +Event type: ${type} +When event started: ${earliest} UTC +Last event: ${latest} UTC + + +Top ${pathNumber} triggering AS paths: +${paths} diff --git a/src/reports/email_templates/rpki.txt b/src/reports/email_templates/rpki.txt new file mode 100644 index 00000000..0ea2a9dc --- /dev/null +++ b/src/reports/email_templates/rpki.txt @@ -0,0 +1,7 @@ +${summary} + +DETAILS: +------------------------------------------------------ +Event type: ${type} +When event started: ${earliest} UTC +Last event: ${latest} UTC \ No newline at end of file diff --git a/src/reports/report.js b/src/reports/report.js index 530918e7..c34a872c 100644 --- a/src/reports/report.js +++ b/src/reports/report.js @@ -33,21 +33,27 @@ import moment from "moment"; import brembo from "brembo"; +import axios from "axios"; +import axiosEnrich from "../utils/axiosEnrich"; export default class Report { - constructor(channels, params, env) { this.config = env.config; this.logger = env.logger; this.pubSub = env.pubSub; this.params = params; + this.enabled = true; for (let channel of channels){ - env.pubSub.subscribe(channel, (message, content) => { + env.pubSub.subscribe(channel, (content, message) => { return this.report(message, content); }); } + + this.axios = axiosEnrich(axios, + (!this.params.noProxy && env.agent) ? env.agent : null, + `${env.clientId}/${env.version}`); } getBGPlayLink = (prefix, start, end, instant = null, rrcs = [0,1,2,5,6,7,10,11,13,14,15,16,18,20]) => { @@ -68,99 +74,126 @@ export default class Report { }; getContext = (channel, content) => { - let context = { - summary: content.message, - earliest: moment(content.earliest).utc().format("YYYY-MM-DD hh:mm:ss"), - latest: moment(content.latest).utc().format("YYYY-MM-DD hh:mm:ss"), - channel, - type: content.origin, - }; - - let matched = null; - let pathsCount = {}; - let sortedPathIndex; - - if (this.params.showPaths > 0) { - content.data - .filter(i => !!i.matchedMessage && !!i.matchedMessage.path) - .map(i => JSON.stringify(i.matchedMessage.path.getValues().slice(1))) - .forEach(path => { - if (!pathsCount[path]) { - pathsCount[path] = 0; - } - pathsCount[path]++; - }); - - sortedPathIndex = Object.keys(pathsCount) - .map(key => [key, pathsCount[key]]); - - sortedPathIndex.sort((first, second) => second[1] - first[1]); - context.pathNumber = Math.min(this.params.showPaths, sortedPathIndex.length); - context.paths = sortedPathIndex - .slice(0, this.params.showPaths) - .map(i => i[0]).join("\n"); - } else { - context.pathNumber = ""; - context.paths = "Disabled"; - } + try { + let context = { + summary: content.message, + earliest: moment(content.earliest).utc().format("YYYY-MM-DD HH:mm:ss"), + latest: moment(content.latest).utc().format("YYYY-MM-DD HH:mm:ss"), + channel, + type: content.origin, + }; + + let matched = null; + let pathsCount = {}; + let sortedPathIndex; + + if (this.params.showPaths > 0) { + content.data + .filter(i => !!i.matchedMessage && !!i.matchedMessage.path) + .map(i => JSON.stringify(i.matchedMessage.path.getValues().slice(channel === "path" ? 0 : 1))) + .forEach(path => { + if (!pathsCount[path]) { + pathsCount[path] = 0; + } + pathsCount[path]++; + }); + + + sortedPathIndex = Object.keys(pathsCount) + .map(key => [key, pathsCount[key]]); + + sortedPathIndex.sort((first, second) => second[1] - first[1]); + context.pathNumber = Math.min(this.params.showPaths, sortedPathIndex.length); + context.paths = sortedPathIndex + .slice(0, this.params.showPaths) + .map(i => i[0]) + .join(","); + } else { + context.pathNumber = 0; + context.paths = ""; + } - switch(channel){ - case "hijack": - matched = content.data[0].matchedRule; - context.prefix = matched.prefix; - context.description = matched.description; - context.asn = matched.asn.toString(); - context.peers = [...new Set(content.data.map(alert => alert.matchedMessage.peer))].length; - context.neworigin = content.data[0].matchedMessage.originAS; - context.newprefix = content.data[0].matchedMessage.prefix; - context.bgplay = this.getBGPlayLink(matched.prefix, content.earliest, content.latest); - - break; - - case "visibility": - matched = content.data[0].matchedRule; - context.prefix = matched.prefix; - context.description = matched.description; - context.asn = matched.asn.toString(); - context.peers = [...new Set(content.data.map(alert => alert.matchedMessage.peer))].length; - context.bgplay = this.getBGPlayLink(matched.prefix, content.earliest, content.latest); - break; - - case "newprefix": - matched = content.data[0].matchedRule; - context.prefix = matched.prefix; - context.description = matched.description; - context.asn = matched.asn.toString(); - context.peers = [...new Set(content.data.map(alert => alert.matchedMessage.peer))].length; - context.neworigin = content.data[0].matchedMessage.originAS; - context.newprefix = content.data[0].matchedMessage.prefix; - context.bgplay = this.getBGPlayLink(matched.prefix, content.earliest, content.latest); - break; - - case "software-update": - break; - - case "path": - break; - - case "misconfiguration": - context.asn = content.data[0].matchedRule.asn.toString(); - - break; - - default: - return false; - } + switch(channel){ + case "hijack": + matched = content.data[0].matchedRule; + context.prefix = matched.prefix; + context.description = matched.description; + context.asn = matched.asn.toString(); + context.peers = [...new Set(content.data.map(alert => alert.matchedMessage.peer))].length; + context.neworigin = content.data[0].matchedMessage.originAS; + context.newprefix = content.data[0].matchedMessage.prefix; + context.bgplay = this.getBGPlayLink(matched.prefix, content.earliest, content.latest); + + break; + + case "visibility": + matched = content.data[0].matchedRule; + context.prefix = matched.prefix; + context.description = matched.description; + context.asn = matched.asn.toString(); + context.peers = [...new Set(content.data.map(alert => alert.matchedMessage.peer))].length; + context.bgplay = this.getBGPlayLink(matched.prefix, content.earliest, content.latest); + break; + + case "newprefix": + matched = content.data[0].matchedRule; + context.prefix = matched.prefix; + context.description = matched.description; + context.asn = matched.asn.toString(); + context.peers = [...new Set(content.data.map(alert => alert.matchedMessage.peer))].length; + context.neworigin = content.data[0].matchedMessage.originAS; + context.newprefix = content.data[0].matchedMessage.prefix; + context.bgplay = this.getBGPlayLink(matched.prefix, content.earliest, content.latest); + break; + + case "software-update": + break; + + case "path": + break; + + case "misconfiguration": + context.asn = content.data[0].matchedRule.asn.toString(); + break; + + case "rpki": + matched = content.data[0].matchedRule; + context.asn = (matched.asn || "").toString(); + context.prefix = matched.prefix || content.data[0].matchedMessage.prefix; + context.description = matched.description || ""; + break; + + default: + matched = content.data[0].matchedRule; + context.prefix = matched.prefix; + context.description = matched.description || ""; + context.asn = (matched.asn || "").toString(); + } + + return context; + + } catch (error) { // This MUST never happen. But if it happens we need do send a basic alert anyway and don't crash + this.logger.log({ + level: 'error', + message: `It was not possible to generate a context: ${error}` + }); - return context; + return { + summary: content.message + } + } }; parseTemplate = (template, context) => { return template.replace(/\${([^}]*)}/g, (r,k)=>context[k]); }; - report = (message, content) => { throw new Error('The method report must be implemented'); - } + }; + + getUserGroup = (group) => { + throw new Error('The method getUserGroup must be implemented'); + }; + } diff --git a/src/reports/reportAlerta.js b/src/reports/reportAlerta.js index 1116f631..e7cb577d 100644 --- a/src/reports/reportAlerta.js +++ b/src/reports/reportAlerta.js @@ -31,7 +31,6 @@ */ import Report from "./report"; -import axios from "axios"; export default class ReportAlerta extends Report { @@ -40,19 +39,12 @@ export default class ReportAlerta extends Report { this.environment = env.config.environment; this.enabled = true; - if (!this.params.urls || !Object.keys(this.params.urls).length){ + if (!this.getUserGroup("default")) { this.logger.log({ level: 'error', - message: "Alerta reporting is not enabled: no group is defined" + message: "Alerta reporting is not enabled: no default group defined" }); this.enabled = false; - } else { - if (!this.params.urls["default"]) { - this.logger.log({ - level: 'error', - message: "In urls, for reportAlerta, a group named 'default' is required for communications to the admin." - }); - } } this.headers = { @@ -76,16 +68,13 @@ export default class ReportAlerta extends Report { if (this.params.resource_templates) { this.logger.log({ level: 'info', - message: "The resource_templates parameter will be soon deprecated in favour of resourceTemplates. Please update your config.yml file accordingly." + message: "The resource_templates parameter is deprecated in favour of resourceTemplates. Please update your config.yml file accordingly." }); } - const resource = this.params.resourceTemplates[channel] || - this.params.resource_templates[channel] || - this.params.resourceTemplates["default"] || - this.params.resource_templates["default"]; + const resource = this.params.resourceTemplates[channel] || this.params.resourceTemplates["default"]; - axios({ + this.axios({ url: url + "/alert", method: "POST", headers: this.headers, @@ -108,18 +97,24 @@ export default class ReportAlerta extends Report { }) }; + getUserGroup = (group) => { + const groups = this.params.urls || this.params.userGroups; + + return groups[group] || groups["default"]; + }; + report = (channel, content) => { if (this.enabled){ let groups = content.data.map(i => i.matchedRule.group).filter(i => i != null); - groups = (groups.length) ? [...new Set(groups)] : Object.keys(this.params.urls); // If there are no groups defined, send to all of them + groups = (groups.length) ? [...new Set(groups)] : ["default"]; for (let group of groups) { - if (this.params.urls[group]) { - this._createAlertaAlert(this.params.urls[group], channel, content); + const url = this.getUserGroup(group); + if (url) { + this._createAlertaAlert(url, channel, content); } } } - - } + }; } diff --git a/src/reports/reportEmail.js b/src/reports/reportEmail.js index d9fd0455..4bd984a4 100644 --- a/src/reports/reportEmail.js +++ b/src/reports/reportEmail.js @@ -36,42 +36,20 @@ import emailTemplates from "./email_templates/emailTemplates"; export default class ReportEmail extends Report { - constructor(channels,params, env) { + constructor(channels, params, env) { super(channels, params, env); this.emailTemplates = new emailTemplates(this.logger); - this.templates = {}; this.emailBacklog = []; - if (!this.params.notifiedEmails || !Object.keys(this.params.notifiedEmails).length) { + if (!this.getUserGroup("default")) { + this.enabled = false; this.logger.log({ level: 'error', - message: "Email reporting is not enabled: no group is defined" + message: "In notifiedEmails, for reportEmail, a group named 'default' is required for communications to the admin." }); - } else { - if (!this.params.notifiedEmails["default"] || !this.params.notifiedEmails["default"].length) { - this.logger.log({ - level: 'error', - message: "In notifiedEmails, for reportEmail, a group named 'default' is required for communications to the admin." - }); - } - - if (this.params.smtp.host !== null) { - this.logger.log({ - level: 'info', - message: `SMTP Host ${this.params.smtp.host} PORT: ${this.params.smtp.port}` - }); - } - - if (this.params.smtp.auth !== null) { - this.logger.log({ - level: 'info', - message: `User: ${this.params.smtp.auth.user}` - }); - } - this.transporter = nodemailer.createTransport(this.params.smtp); this.transporter.verify(function(error, success) { @@ -97,6 +75,14 @@ export default class ReportEmail extends Report { } } + if (Object.keys(this.templates).length === 0) { + this.enabled = false; + this.logger.log({ + level: 'error', + message: "Email templates cannot be associated to channels." + }); + } + setInterval(() => { const nextEmail = this.emailBacklog.pop(); if (nextEmail) { @@ -104,13 +90,20 @@ export default class ReportEmail extends Report { } }, 3000); } - } + }; + + + getUserGroup = (group) => { + const groups = this.params.notifiedEmails || this.params.userGroups; + + return groups[group] || groups["default"]; + }; getEmails = (content) => { const users = content.data .map(item => { - if (item.matchedRule && item.matchedRule.group){ - return item.matchedRule.group; + if (item.matchedRule){ + return item.matchedRule.group || "default"; } else { return false; } @@ -118,13 +111,9 @@ export default class ReportEmail extends Report { .filter(item => !!item); try { - const emails = [...new Set(users)] - .map(user => { - return this.params.notifiedEmails[user]; - }) + return [...new Set(users)] + .map(user => this.getUserGroup(user)) .filter(item => !!item); - - return (emails.length) ? emails : [this.params.notifiedEmails["default"]]; } catch (error) { this.logger.log({ level: 'error', @@ -136,28 +125,26 @@ export default class ReportEmail extends Report { }; getEmailText = (channel, content) => { - return this.parseTemplate(this.templates[channel], this.getContext(channel, content)); + const context = this.getContext(channel, content); + const paths = JSON.parse(`[${context.paths}]`); + context.paths = paths.length ? paths.join("\n") : "Disabled" + return this.parseTemplate(this.templates[channel], context); }; _sendEmail = (email) => { - if (this.transporter) { - this.transporter - .sendMail(email) - .catch(error => { - this.logger.log({ - level: 'error', - message: error - }); - }) - } + this.transporter + .sendMail(email) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); }; report = (channel, content) => { - if (Object.keys(this.templates).length > 0 && - this.params.notifiedEmails && - this.params.notifiedEmails["default"] && - this.params.notifiedEmails["default"].length) { + if (this.enabled) { const emailGroups = this.getEmails(content); for (let emails of emailGroups) { @@ -173,7 +160,6 @@ export default class ReportEmail extends Report { }); } } - } } } \ No newline at end of file diff --git a/src/reports/reportFile.js b/src/reports/reportFile.js index d9106f40..6f1dcb66 100644 --- a/src/reports/reportFile.js +++ b/src/reports/reportFile.js @@ -39,7 +39,7 @@ export default class ReportFile extends Report { super(channels, params, env); this.persistAlerts = params.persistAlertData; - this.alertsDirectory = params.alertDataDirectory; + this.alertsDirectory = env.config.volume + params.alertDataDirectory; if (this.persistAlerts && !this.alertsDirectory) { this.persistAlerts = false; this.logger.log({ diff --git a/src/reports/reportHTTP.js b/src/reports/reportHTTP.js new file mode 100644 index 00000000..82cfcaf0 --- /dev/null +++ b/src/reports/reportHTTP.js @@ -0,0 +1,103 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import Report from "./report"; + +export default class ReportHTTP extends Report { + + constructor(channels, params, env) { + super(channels, params, env); + + this.name = "reportHTTP" || this.params.name; + this.enabled = true; + + if (!this.getUserGroup("default")) { + this.logger.log({ + level: 'error', + message: `${this.name} reporting is not enabled: no default group defined` + }); + this.enabled = false; + } + + this.headers = this.params.headers || {}; + if (this.params.isTemplateJSON) { + this.headers["Content-Type"] = "application/json"; + } + } + + getUserGroup = (group) => { + const groups = this.params.hooks || this.params.userGroups || {}; + + return groups[group] || groups["default"]; + }; + + getTemplate = (group, channel, content) => { + return this.params.templates[channel] || this.params.templates["default"]; + }; + + _sendHTTPMessage = (group, channel, content) => { + const url = this.getUserGroup(group); + if (url) { + const context = this.getContext(channel, content); + + if (this.params.showPaths > 0 && context.pathNumber > 0) { + context.summary = `${context.summary}. Top ${context.pathNumber} most used AS paths: ${context.paths}.`; + } + const blob = this.parseTemplate(this.getTemplate(group, channel, content), context); + + this.axios({ + url: url, + method: "POST", + headers: this.headers, + data: (this.params.isTemplateJSON) ? JSON.parse(blob) : blob + }) + .catch((error) => { + this.logger.log({ + level: 'error', + message: error + }); + }); + } + }; + + report = (channel, content) => { + if (this.enabled) { + let groups = content.data.map(i => i.matchedRule.group).filter(i => i != null); + + groups = (groups.length) ? [...new Set(groups)] : ["default"]; + + for (let group of groups) { + this._sendHTTPMessage(group, channel, content); + } + } + }; +} \ No newline at end of file diff --git a/src/reports/reportKafka.js b/src/reports/reportKafka.js index b479eb29..c5a86228 100644 --- a/src/reports/reportKafka.js +++ b/src/reports/reportKafka.js @@ -31,13 +31,14 @@ */ import Report from "./report"; -import kafka from "kafka-node"; +import { Kafka, logLevel } from "kafkajs"; export default class ReportKafka extends Report { constructor(channels,params, env) { super(channels, params, env); this.client = null; + this.clientId = env.clientId; this.producer = null; this.connected = false; this.host = [ this.params.host || "localhost", this.params.port ].filter(i => i != null).join(":"); @@ -60,62 +61,52 @@ export default class ReportKafka extends Report { _connectToKafka = () => { if (!this.connecting) { - this.connecting = new Promise((resolve, reject) => { - if (this.connected) { - resolve(true); - } else { - this.client = new kafka.KafkaClient({kafkaHost: this.host}); - this.producer = new kafka.HighLevelProducer(this.client); + this.client = new Kafka({ + logLevel: logLevel.ERROR, + clientId: this.clientId, + brokers: [].concat.apply([], [this.host]) + }); - this.producer.setMaxListeners(0); - this.producer - .on('ready', () => { - this.connected = true; - resolve(true); - }); + this.producer = this.client.producer(); - this.producer - .on('error', (error) => { - this.logger.log({ - level: 'error', - message: 'Kafka connector error: ' + error - }); - }); - } - }); + this.connecting = this.producer + .connect() + .then(() => { + this.connected = true; + }) + .catch((error) => { + this.logger.log({ + level: 'error', + message: 'Kafka connector error: ' + error + }); + }); } return this.connecting; }; - _sendOutcome = (error, data) => { - this.logger.log({ - level: 'error', - message: 'Kafka error during send: ' + JSON.stringify(data) - }); - }; - _getPayload = (topic, channel, message) => { - return [{ + return { topic: topic, - messages: JSON.stringify(message), + messages: [ { value: JSON.stringify(message) }], key: channel, attributes: 1, timestamp: Date.now() - }]; + }; }; report = (channel, content) => { return this._connectToKafka() .then(() => { const topic = this._getTopic(channel); - this.producer.send(this._getPayload(topic, channel, content), this._sendOutcome); + return this.producer + .send(this._getPayload(topic, channel, content)); }) .catch(error => { this.logger.log({ level: 'error', - message: 'Kafka disconnected: ' + error + message: error }); }); } diff --git a/src/reports/reportPullAPI.js b/src/reports/reportPullAPI.js new file mode 100644 index 00000000..5717aff5 --- /dev/null +++ b/src/reports/reportPullAPI.js @@ -0,0 +1,123 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import Report from "./report"; +import RestApi from "../utils/restApi"; +import md5 from "md5"; + +export default class ReportPullAPI extends Report { + + constructor(channels, params, env) { + super(channels, params, env); + + this.name = "reportPullAPI" || this.params.name; + this.enabled = true; + this.maxAlertsAmount = this.params.maxAlertsAmount || 100; + this.lastQuery = null; + + let restDefault = env.config.rest || { port: params.port, host: params.host }; + const rest = new RestApi(restDefault); + + rest.addUrl('/alerts', this.respond) + .catch(error => { + env.logger.log({ + level: 'error', + message: error + }); + }); + + rest.addUrl('/alerts/:hash', this.respond) + .catch(error => { + env.logger.log({ + level: 'error', + message: error + }); + }); + + rest.addUrl('/alerts/groups/:group', this.respond) + .catch(error => { + env.logger.log({ + level: 'error', + message: error + }); + }); + + this.alerts = []; + }; + + respond = (req, res, next) => { + res.contentType = 'json'; + res.send({ + meta: { + lastQuery: this.lastQuery + }, + data: this._getAlerts(req.params) + }); + next(); + this.lastQuery = new Date().getTime(); + }; + + _getAlerts = ({ hash, group }) => { + let alerts; + + if (group) { + alerts = this.alerts.filter(i => i.group === group); + } else if (hash) { + alerts = this.alerts.filter(i => i.alert.hash === hash); + } else { + alerts = this.alerts; + } + + return alerts.map(i => i.alert); + } + + getUserGroup = (group) => { + return null; + }; + + report = (channel, content) => { + if (this.enabled) { + let groups = content.data.map(i => i.matchedRule.group).filter(i => i != null); + + groups = (groups.length) ? [...new Set(groups)] : ["default"]; + content.hash = md5(content.id); + + for (let group of groups) { + this.alerts.push({ + group, + alert: content + }); + this.alerts = this.alerts.slice(-this.maxAlertsAmount); + } + } + }; +} \ No newline at end of file diff --git a/src/reports/reportSlack.js b/src/reports/reportSlack.js index e97b73f9..6d00183d 100644 --- a/src/reports/reportSlack.js +++ b/src/reports/reportSlack.js @@ -30,78 +30,40 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import Report from "./report"; -import axios from "axios"; - -export default class ReportSlack extends Report { +import ReportHTTP from "./reportHTTP"; +export default class ReportSlack extends ReportHTTP { constructor(channels, params, env) { - super(channels, params, env); - - - this.enabled = true; - if (!this.params.hooks || !Object.keys(this.params.hooks).length){ - this.logger.log({ - level: 'error', - message: "Slack reporting is not enabled: no group is defined" - }); - this.enabled = false; - } else { - if (!this.params.hooks["default"]) { - this.logger.log({ - level: 'error', - message: "In hooks, for reportSlack, a group named 'default' is required for communications to the admin." - }); - } - } - - } + const templates = {}; + const defaultColor = '#4287f5'; + const colors = params.colors || {}; - _sendSlackMessage = (url, channel, content, context) => { - let message = content.message; - const color = (this.params && this.params.colors && this.params.colors[channel]) - ? this.params.colors[channel] - : '#4287f5'; - - if (this.params.showPaths > 0) { - message += `${content.message}. Top ${context.pathNumber} most used AS paths: \n ${context.paths}`; - } - - axios({ - url: url, - method: "POST", - resposnseType: "json", - data: { + const getTemplateItem = (color) => { + return JSON.stringify({ attachments: [ { color: color, - title: channel, - text: message + title: "${channel}", + text: "${summary}" } ] - } - }) - .catch((error) => { - this.logger.log({ - level: 'error', - message: error - }); - }) - }; - - report = (channel, content) => { - if (this.enabled) { - const context = this.getContext(channel, content); - let groups = content.data.map(i => i.matchedRule.group).filter(i => i != null); - - groups = (groups.length) ? [...new Set(groups)] : Object.keys(this.params.hooks); // If there are no groups defined, send to all of them + }); + }; - for (let group of groups) { - if (this.params.hooks[group]) { - this._sendSlackMessage(this.params.hooks[group], channel, content, context); - } - } + for (let channel of channels) { + templates[channel] = getTemplateItem(colors[channel] || defaultColor); } + templates["default"] = getTemplateItem(defaultColor); + + const slackParams = { + headers: {}, + isTemplateJSON: true, + showPaths: params.showPaths, + hooks: params.hooks, + name: "reportSlack", + templates + }; + super(channels, slackParams, env); } } \ No newline at end of file diff --git a/src/reports/reportSyslog.js b/src/reports/reportSyslog.js index 259e5162..6b984306 100644 --- a/src/reports/reportSyslog.js +++ b/src/reports/reportSyslog.js @@ -43,12 +43,11 @@ export default class ReportSyslog extends Report { this.host = params.host; this.options = { syslogHostname: params.host, - transport: syslog.Transport.Udp, + transport: (params.transport === 'tcp') ? syslog.Transport.Tcp : syslog.Transport.Udp, port: params.port }; } - _getMessage = (channel, content) => { return this.parseTemplate(this.params.templates[channel] || this.params.templates["default"], this.getContext(channel, content)); }; @@ -62,14 +61,14 @@ export default class ReportSyslog extends Report { this.client = syslog.createClient(this.host, this.options); this.connected = true; - this.client.on("close", function () { + this.client.on("close", function (error) { this.logger.log({ level: 'error', message: 'Syslog disconnected: ' + error }); }); - this.client.on("error", function () { + this.client.on("error", function (error) { this.logger.log({ level: 'error', message: 'Syslog: ' + error diff --git a/src/reports/reportTelegram.js b/src/reports/reportTelegram.js new file mode 100644 index 00000000..e5381c53 --- /dev/null +++ b/src/reports/reportTelegram.js @@ -0,0 +1,82 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import ReportHTTP from "./reportHTTP"; + +export default class reportTelegram extends ReportHTTP { + + constructor(channels, params, env) { + const hooks = {}; + + for (let userGroup in params.chatIds) { + hooks[userGroup] = params.botUrl; + } + hooks["default"] = params.botUrl; + + const telegramParams = { + headers: {}, + isTemplateJSON: true, + showPaths: params.showPaths, + hooks: hooks, + name: "reportTelegram", + templates: {} + }; + + super(channels, telegramParams, env); + this.chatIds = params.chatIds; + + if (!params.botUrl) { + this.logger.log({ + level: 'error', + message: `${this.name} reporting is not enabled: no botUrl provided` + }); + this.enabled = false; + } + + if (!params.chatIds || !params.chatIds["default"]) { + this.logger.log({ + level: 'error', + message: `${this.name} reporting is not enabled: no default chat id provided` + }); + this.enabled = false; + } + }; + + getTemplate = (group, channel, content) => { + return JSON.stringify({ + "chat_id": this.chatIds[group] || this.chatIds["default"], + "text": "${summary}", + "parse_mode": 'HTML', + "disable_web_page_preview": true + }); + }; +} \ No newline at end of file diff --git a/src/reports/reportWebex.js b/src/reports/reportWebex.js index dd8efe5d..fc0f6e76 100644 --- a/src/reports/reportWebex.js +++ b/src/reports/reportWebex.js @@ -31,7 +31,6 @@ */ import Report from "./report"; -import axios from "axios"; export default class ReportWebex extends Report { @@ -39,32 +38,29 @@ export default class ReportWebex extends Report { super(channels, params, env); - this.enabled = true; - if (!this.params.hooks || !Object.keys(this.params.hooks).length){ + if (!this.getUserGroup("default")) { this.logger.log({ level: 'error', - message: "Webex reporting is not enabled: no group is defined" + message: `Webex reporting is not enabled: no default group defined` }); this.enabled = false; - } else { - if (!this.params.hooks["default"]) { - this.logger.log({ - level: 'error', - message: "In hooks, for reportWebex, a group named 'default' is required for communications to the admin." - }); - } } + }; - } + getUserGroup = (group) => { + const groups = this.params.hooks || this.params.userGroups; + + return groups[group] || groups["default"]; + }; _sendWebexMessage = (url, message, content) => { - axios({ - url: url, + this.axios({ + url, method: "POST", resposnseType: "json", data: { - markdown: `**${message}**: ${content.message}` + markdown: `**${message}**: ${content.message}` } }) .catch((error) => { @@ -79,11 +75,12 @@ export default class ReportWebex extends Report { if (this.enabled){ let groups = content.data.map(i => i.matchedRule.group).filter(i => i != null); - groups = (groups.length) ? [...new Set(groups)] : Object.keys(this.params.hooks); // If there are no groups defined, send to all of them + groups = (groups.length) ? [...new Set(groups)] : ["default"]; for (let group of groups) { - if (this.params.hooks[group]) { - this._sendWebexMessage(this.params.hooks[group], message, content); + const url = this.getUserGroup(group); + if (url) { + this._sendWebexMessage(url, message, content); } } } diff --git a/src/utils/WebSocket.js b/src/utils/WebSocket.js new file mode 100644 index 00000000..dd6e4fcc --- /dev/null +++ b/src/utils/WebSocket.js @@ -0,0 +1,162 @@ +import _ws from "ws"; +import PubSub from "../utils/pubSub"; +import brembo from "brembo"; +import { v4 as uuidv4 } from 'uuid'; +import nodeCleanup from "node-cleanup"; + +export default class WebSocket { + constructor(host, options) { + + this.pubsub = new PubSub(); + this.host = host; + this.options = options; + this.ws = null; + this.alive = false; + this.pingInterval = options.pingIntervalSeconds ? options.pingIntervalSeconds * 1000 : 40000; + this.reconnectSeconds = options.reconnectSeconds ? options.reconnectSeconds * 1000 : 30000; + this.connectionDelay = 8000; + this.openConnectionTimeoutSeconds = 40000; + this.lastPingReceived = null; + + nodeCleanup(() => { + if (this.ws) { + this.pubsub.publish("close", "process termination"); + this.disconnect(); + } + }); + } + + _ping = () => { + if (this.ws) { + try { + this.ws.ping(); + } catch (e) { + // Nothing to do + } + } + }; + + _pingReceived = () => { + this.lastPingReceived = new Date().getTime(); + }; + + _pingCheck = () => { + const nPings = 6; + if (this.ws) { + if (this.lastPingReceived + (this.pingInterval * nPings) < new Date().getTime()) { + this._publishError(`The WebSocket client didn't receive ${nPings} pings. Disconnecting.`) + this.disconnect(); + this.connect(); + } else { + this._ping(); + } + } + }; + + _startPing = () => { + if (this.pingIntervalTimer) { + clearInterval(this.pingIntervalTimer); + } + this._pingReceived(); // Set initial ping timestamp + this.pingIntervalTimer = setInterval(() => { + this._pingCheck(); + }, this.pingInterval); + }; + + _connect = () => { + const connectionId = uuidv4(); + const url = brembo.build(this.host.split("?")[0], { + params: { + ...brembo.parse(this.host).params, + connection: connectionId + } + }); + + this.ws = new _ws(url, this.options); + this.setOpenTimeout(true); + + this.ws.on('message', (data) => { + this.pubsub.publish("message", data); + }); + this.ws.on('close', data => { + this.alive = false; + this.setOpenTimeout(false); + this.pubsub.publish("close", data); + }); + this.ws.on('pong', this._pingReceived); + this.ws.on('error', message => { + this._publishError(message, {connection: connectionId}); + }); + this.ws.on('open', () => { + this.alive = true; + this.setOpenTimeout(false); + this.pubsub.publish("open", { connection: connectionId }); + }); + + this._startPing(); + }; + + send = (data) => { + return new Promise((resolve, reject) => { + try { + this.ws.send(data); + resolve(); + } catch (error) { + reject(error); + } + }); + }; + + connect = () => { + if (this.ws) { + this.disconnect(); + } + + if (this.connectTimeout) { + clearTimeout(this.connectTimeout); + } + this.connectTimeout = setTimeout(this._connect, this.connectionDelay); + + this.connectionDelay = this.reconnectSeconds; + }; + + _publishError = (message, extra={}) => { + this.pubsub.publish("error", { type: "error", message, ...extra }); + }; + + setOpenTimeout = (setting) => { + if (this.openConnectionTimeout) { + clearTimeout(this.openConnectionTimeout); + } + if (setting) { + this.openConnectionTimeout = setTimeout(() => { + this._publishError("connection timed out"); + if (this.ws) { + this.disconnect(); + this.connect(); + } + }, this.openConnectionTimeoutSeconds); + } + }; + + disconnect = () => { + try { + this.ws.removeAllListeners("message"); + this.ws.removeAllListeners("close"); + this.ws.removeAllListeners("error"); + this.ws.removeAllListeners("open"); + this.ws.removeAllListeners("pong"); + this.ws.terminate(); + this.ws = null; + this.alive = false; + if (this.pingIntervalTimer) { + clearInterval(this.pingIntervalTimer); + } + } catch (e) { + // Nobody cares + } + }; + + + on = (event, callback) => this.pubsub.subscribe(event, callback); +} \ No newline at end of file diff --git a/src/utils/axiosEnrich.js b/src/utils/axiosEnrich.js new file mode 100644 index 00000000..fbbe6c53 --- /dev/null +++ b/src/utils/axiosEnrich.js @@ -0,0 +1,46 @@ +import md5 from "md5"; + +const attempts = {}; +const numAttempts = 2; + +const retry = function (axios, error) { + return new Promise((resolve, reject) => { + setTimeout(() => { + const key = md5(JSON.stringify(error.config)); + attempts[key] = attempts[key] || 0; + attempts[key]++; + if (attempts[key] <= numAttempts) { + resolve(axios.request(error.config)); + } else { + reject(error); + } + }, 2000); + }); +} + +export default function(axios, httpsAgent, userAgent) { + + // Set agent/proxy + if (httpsAgent) { + axios.defaults.httpsAgent = httpsAgent; + } + + // Set User Agent + if (userAgent) { + axios.defaults.headers.common = { + "User-Agent": userAgent + }; + } + + // Retry + axios.interceptors.response.use( + response => response, + error => { + if (error.config) { + return retry(axios, error); + } + return Promise.reject(error); + }); + + return axios; +} \ No newline at end of file diff --git a/src/lossyBuffer.js b/src/utils/lossyBuffer.js similarity index 100% rename from src/lossyBuffer.js rename to src/utils/lossyBuffer.js diff --git a/src/pubSub.js b/src/utils/pubSub.js similarity index 71% rename from src/pubSub.js rename to src/utils/pubSub.js index 1ac33961..f6a74cf5 100644 --- a/src/pubSub.js +++ b/src/utils/pubSub.js @@ -1,4 +1,4 @@ -export default class PubSub{ +export default class PubSub { constructor() { this.callbacks = {}; }; @@ -9,10 +9,9 @@ export default class PubSub{ }; publish(channel, content) { - const callbacks = this.callbacks[channel] || []; - for (let clb of callbacks) { + for (let clb of this.callbacks[channel] || []) { new Promise(function(resolve, reject){ - clb(channel, content); + clb(content, channel); resolve(true); }) .catch(console.log); diff --git a/src/utils/restApi.js b/src/utils/restApi.js new file mode 100644 index 00000000..4903c463 --- /dev/null +++ b/src/utils/restApi.js @@ -0,0 +1,63 @@ +import restify from "restify"; + +export default class RestApi { + static _instance; + + constructor(params) { + + this.params = params; + this.port = this.params.port || 8011; + this.host = this.params.host || null; + this.enabled = false; + this.urls = {}; + this._serverPromise = null; + + if (!!RestApi._instance) { + return RestApi._instance; + } + + RestApi._instance = this; + } + + _startServer = () => { + if (!this._serverPromise) { + this._serverPromise = new Promise((resolve, reject) => { + try { + if (this.host && this.port) { + this.server = restify.createServer(); + this.server.pre(restify.pre.sanitizePath()); + this.server.listen(this.port, this.host); + this.enabled = true; + resolve(); + } else if (this.port) { + this.server = restify.createServer(); + this.server.pre(restify.pre.sanitizePath()); + this.server.listen(this.port); + this.enabled = true; + resolve(); + } else { + this.enabled = false + reject("The port parameter must be specified to start the REST API.") + } + } catch (error) { + this.enabled = false; + reject(error); + } + }); + } + + return this._serverPromise; + }; + + addUrl = (url, callback) => { + if (this.urls[url]) { + return Promise.reject("The URL for the REST API already exists and cannot be replaced"); + } else { + this.urls[url] = callback; + return this._startServer() + .then(() => { + this.server.get(url, this.urls[url]); + }) + } + } +} \ No newline at end of file diff --git a/src/utils/rpkiDiffingTool.js b/src/utils/rpkiDiffingTool.js new file mode 100644 index 00000000..0b500979 --- /dev/null +++ b/src/utils/rpkiDiffingTool.js @@ -0,0 +1,45 @@ + +function getPrefixes(vrp, asn) { + return [...new Set(vrp.filter(i => i.asn === asn).map(i => i.prefix))]; +} + +function getRelevant(vrp, prefixes, asns=[]){ + return vrp.filter(i => asns.includes(i.asn) || prefixes.includes(i.prefix)); +} + +function diff(vrpsOld, vrpsNew, asn, prefixesIn=[]) { + asn = parseInt(asn); + + let prefixes; + if (asn) { + prefixes = [...new Set(prefixesIn)]; + } else { + prefixes = [...new Set([...prefixesIn, ...getPrefixes(vrpsOld, asn), ...getPrefixes(vrpsNew, asn)])]; + } + const filteredVrpsOld = JSON.parse(JSON.stringify(getRelevant(vrpsOld, prefixes, [asn]))) + .map(i => { + i.status = "removed"; + return i; + }); + const filteredVrpsNew = JSON.parse(JSON.stringify(getRelevant(vrpsNew, prefixes, [asn]))) + .map(i => { + i.status = "added"; + return i; + }); + + const index = {}; + + for (let vrp of filteredVrpsOld.concat(filteredVrpsNew)) { + const key = `${vrp.ta}-${vrp.prefix}-${vrp.asn}-${vrp.maxLength}`; + index[key] = index[key] || []; + index[key].push(vrp); + } + + return Object.values(index).filter(i => i.length === 1).map(i => i[0]); +} + +export { + getPrefixes, + getRelevant, + diff +}; \ No newline at end of file diff --git a/src/utils/rpkiUtils.js b/src/utils/rpkiUtils.js new file mode 100644 index 00000000..c06ab007 --- /dev/null +++ b/src/utils/rpkiUtils.js @@ -0,0 +1,306 @@ +import rpki from "rpki-validator"; +import fs from "fs"; +import md5 from "md5"; +import axiosEnrich from "./axiosEnrich"; +import axios from "axios"; + +export default class RpkiUtils { + constructor(env) { + this.config = env.config; + this.agent = env.agent; + this.params = this.config.rpki || {}; + this.clientId = env.clientId || ""; + this.logger = env.logger; + this.userAgent = `${this.clientId}/${env.version}`; + + const defaultMarkDataAsStaleAfterMinutes = 60; + + const providers = ["rpkiclient", "ntt", "ripe", "cloudflare", "external", "api"]; // First provider is the default one + + if (this.params.url || this.params.vrpProvider === "api") { + this.params.vrpProvider = "api"; + this.params.preCacheROAs = true; + if (!this.params.url) { + this.params.vrpProvider = providers[0]; + this.params.url = null; + this.logger.log({ + level: 'error', + message: "No url provided for the vrps api. Using default vrpProvider." + }); + } + } + + if (this.params.vrpFile) { + this.params.vrpProvider = "external"; + this.params.refreshVrpListMinutes = null; + this.params.preCacheROAs = true; + } else { + if (!this.params.vrpProvider) { + this.params.vrpProvider = providers[0]; + } else if (!providers.includes(this.params.vrpProvider)) { + this.params.vrpProvider = providers[0]; + this.logger.log({ + level: 'error', + message: "The specified vrpProvider is not valid. Using default vrpProvider." + }); + } + this.params.refreshVrpListMinutes = Math.max(this.params.refreshVrpListMinutes || 0, 5); + this.params.preCacheROAs = this.params.preCacheROAs !== false; + } + + if (this.params.markDataAsStaleAfterMinutes !== undefined) { + if (this.params.markDataAsStaleAfterMinutes <= this.params.refreshVrpListMinutes) { + this.logger.log({ + level: 'error', + message: `The specified markDataAsStaleAfterMinutes cannot be <= of refreshVrpListMinutes (${defaultMarkDataAsStaleAfterMinutes} minutes will be used).` + }); + this.params.markDataAsStaleAfterMinutes = defaultMarkDataAsStaleAfterMinutes; + } + } + + this.status = { + data: true, + stale: false, + provider: this.params.vrpProvider + }; + + this._loadRpkiValidator(); + + if (this.params.markDataAsStaleAfterMinutes > 0) { + setInterval(this._markAsStale, this.params.markDataAsStaleAfterMinutes * 60 * 1000); + } + + this.queue = []; + setInterval(this._validateQueue, 500); // Periodically validate prefixes-origin pairs + }; + + _loadRpkiValidatorFromVrpProvider = () => { + + if (!this.rpki) { + const rpkiValidatorOptions = { + connector: this.params.vrpProvider, + clientId: this.clientId, + axios: axiosEnrich(axios, (!this.params.noProxy && this.agent) ? this.agent : null, this.userAgent) + }; + + if (this.params.url) { + rpkiValidatorOptions.url = this.params.url; + } + this.rpki = new rpki(rpkiValidatorOptions); + + if (!!this.params.preCacheROAs) { + this._preCache(); + } + } + }; + + _watchVrpFile = (vrpFile) => { + const reload = () => { // Watch the external file to refresh the list + if (this.watchFileTimer) { + clearTimeout(this.watchFileTimer); + } + this.watchFileTimer = setTimeout(() => { + this.logger.log({ + level: 'info', + message: "VRPs reloaded due to file change." + }); + this._loadRpkiValidatorFromVrpFile(vrpFile); + }, 3000); + }; + + fs.watchFile(vrpFile, reload); + }; + + _loadRpkiValidatorFromVrpFile = (vrpFile) => { + + if (fs.existsSync(vrpFile)) { + try { + let vrps = JSON.parse(fs.readFileSync(vrpFile, 'utf8')); + + if (vrps) { + if (vrps.roas && vrps.roas.length) { + vrps = vrps.roas; + } + if (vrps.length > 0) { + + if (this.rpki) { + this.rpki.destroy(); + } + + this.rpki = new rpki({ + connector: "external", + clientId: this.clientId + }); + + this.rpki.setVRPs(vrps); + this._preCache(); + + } else { + this.logger.log({ + level: 'error', + message: "The provided VRPs file is empty. Using default vrpProvider." + }); + } + } + + } catch (error) { + this.logger.log({ + level: 'error', + message: "The provided VRPs file cannot be parsed. Using default vrpProvider." + }); + } + } else { + this.logger.log({ + level: 'error', + message: "The provided VRPs file cannot be found. Using default vrpProvider." + }); + } + + return this._loadRpkiValidatorFromVrpProvider(); + }; + + _loadRpkiValidator = () => { + if (!!this.params.vrpFile) { + const vrpFile = this.config.volume + this.params.vrpFile; + this._loadRpkiValidatorFromVrpFile(vrpFile); + this._watchVrpFile(vrpFile); + } else { + this._loadRpkiValidatorFromVrpProvider(); + } + }; + + _preCache = () => { + if (!!this.params.preCacheROAs) { + return this.rpki + .preCache(this.params.refreshVrpListMinutes) + .then(data => { + this.status.data = true; + this.status.stale = false; + + return data; + }) + .catch(() => { + if (!this._cannotDownloadErrorOnce) { + this.logger.log({ + level: 'error', + message: "The VRP list cannot be downloaded. The RPKI monitoring should be working anyway with one of the on-line providers." + }); + } + this._cannotDownloadErrorOnce = true; + }) + } else { + this.status.data = true; + this.status.stale = false; + return Promise.resolve(); + } + }; + + _validateQueue = () => { + const batch = {}; + + for (let { message, matchedRule, callback } of this.queue) { + const key = message.originAS.getId() + "-" + message.prefix; + batch[key] = batch[key] || []; + batch[key].push({ message, matchedRule, callback }); + } + this.queue = []; + + this.validateBatch(Object + .values(batch) + .map((elements) => { + const { message } = elements[0]; + return { + prefix: message.prefix, + origin: message.originAS + }; + })) + .then(results => { + for (let result of results) { + const key = result.origin.getId() + "-" + result.prefix; + for (let { message, matchedRule, callback } of batch[key]) { + callback(result, message, matchedRule); + } + } + }) + .catch(error => { + this.logger.log({ + level: 'error', + message: error + }); + }); + }; + + addToValidationQueue = (message, matchedRule, callback) => { + this.queue.push({ message, matchedRule, callback }); + }; + + validate = (prefix, origin) => { + return this.validateBatch([{ prefix, origin }]) + .then(results => results[0]); + }; + + validateBatch = (batch) => { + return this._preCache() + .then(() => { + return Promise.all(batch + .map(({ prefix, origin }) => { + const origins = [].concat.apply([], [origin.getValue()]); + return Promise + .all(origins.map(asn => this.rpki.validate(prefix, asn, true))) // Validate each origin + .then(results => { + if (results.length === 1) { // Only one result = only one origin, just return + return { ...results[0], prefix, origin }; + } else { // Multiple origin + if (results.every(result => result && result.valid)) { // All valid + return { + valid: true, + covering: [].concat.apply([], results.map(i => i.covering)), + prefix, + origin + }; + } else if (results.some(result => result && !result.valid)) { // At least one not valid + return { + valid: false, + covering: [].concat.apply([], results.map(i => i.covering)), + prefix, + origin + }; + } else { // return not covered + return { + valid: null, + covering: [].concat.apply([], results.map(i => i.covering)), + prefix, + origin + }; + } + } + }); + })) + .catch(error => { + this.logger.log({ + level: 'error', + message: "RPKI validation failed due to:" + error + }); + }) + }); + }; + + getVrps = () => { + return this.rpki.toArray(); + }; + + getStatus = () => { + return this.status; + }; + + _markAsStale = () => { + if (!!this.params.preCacheROAs) { + const digest = md5(JSON.stringify(this.getVrps())); + if (this.oldDigest) { + this.status.stale = this.oldDigest === digest; + } + + this.oldDigest = digest; + } + }; +} \ No newline at end of file diff --git a/src/utils/storage.js b/src/utils/storage.js new file mode 100644 index 00000000..fb2b8e56 --- /dev/null +++ b/src/utils/storage.js @@ -0,0 +1,45 @@ +export default class Storage { + + constructor(params, config){ + this.config = config; + this.params = params; + this.validity = (this.params.validitySeconds ? (this.params.validitySeconds * 1000) : null) || Infinity; + }; + + set = (key, value) => { + if (/^[A-Za-z0-9\-_]+$/i.test(key)) { + const envelop = { + date: new Date().getTime(), + value + }; + + return this._set(key, envelop); + } else { + + return Promise.reject("Not a valid key. Use only chars and dashes."); + } + }; + + get = (key) => { + return this._get(key) + .then((data) => { + if (!!data) { + const { date, value } = data; + const now = new Date().getTime(); + if (date + this.validity >= now) { + return value; + } + } + return {}; + }); + }; + + _set = (key, value) => { + throw new Error("The set method must be implemented"); + }; + + _get = (key) => { + throw new Error("The get method must be implemented"); + }; + +} diff --git a/src/utils/storages/storageFile.js b/src/utils/storages/storageFile.js new file mode 100644 index 00000000..3a200d6f --- /dev/null +++ b/src/utils/storages/storageFile.js @@ -0,0 +1,50 @@ +import Storage from "../storage"; +import fs from "fs"; + +export default class StorageFile extends Storage{ + constructor(params, config){ + super(params, config); + this.directory = this.config.volume + (this.params.directory || ".cache/"); + this.enabled = true; + try { + if (!fs.existsSync(this.directory)) { + fs.mkdirSync(this.directory); + } + } catch(error) { + this.enabled = false; + } + }; + + _set = (key, value) => + new Promise((resolve, reject) => { + if (this.enabled) { + const file = this.directory + key + ".json"; + try { + fs.writeFileSync(file, JSON.stringify(value)); + resolve(true); + } catch (error) { + reject(error); + } + } else { + reject("The .cache/ directory is not writeable"); + } + }); + + _get = (key) => + new Promise((resolve, reject) => { + if (this.enabled) { + const file = this.directory + key + ".json"; + try { + if (fs.existsSync(file)) { + resolve(JSON.parse(fs.readFileSync(file, 'utf8'))); + } else { + resolve(null); + } + } catch (error) { + reject(error); + } + } else { + reject("The .cache/ directory is not writeable"); + } + }); +} diff --git a/src/worker.js b/src/worker.js index 4aa095a1..ed3791c9 100644 --- a/src/worker.js +++ b/src/worker.js @@ -30,52 +30,53 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import Consumer from "./consumer"; -import LossyBuffer from "./lossyBuffer"; -import ConnectorFactory from "./connectorFactory"; import cluster from "cluster"; import fs from "fs"; +import inputYml from "./inputs/inputYml"; // Default input connector export default class Worker { - constructor(configFile) { - global.EXTERNAL_CONFIG_FILE = configFile; + constructor({ configFile, volume, configConnector, inputConnector, groupFile }) { + global.EXTERNAL_CONFIG_CONNECTOR = global.EXTERNAL_CONFIG_CONNECTOR || configConnector; + global.EXTERNAL_INPUT_CONNECTOR = global.EXTERNAL_INPUT_CONNECTOR || inputConnector; + global.EXTERNAL_CONFIG_FILE = global.EXTERNAL_CONFIG_FILE || configFile; + global.EXTERNAL_GROUP_FILE = global.EXTERNAL_GROUP_FILE || groupFile; + global.EXTERNAL_VOLUME_DIRECTORY = global.EXTERNAL_VOLUME_DIRECTORY || volume; const env = require("./env"); + this.config = env.config; this.logger = env.logger; - this.input = env.input; + this.input = new (global.EXTERNAL_INPUT_CONNECTOR || inputYml)(env); this.pubSub = env.pubSub; this.version = env.version; - this.configFile = env.configFile; - - if (env.sentryDSN) { - const Sentry = require('@sentry/node'); - Sentry.init({ dsn: env.sentryDSN }); - } if (!this.config.multiProcess) { + const Consumer = require("./consumer").default; - this.master(); - new Consumer(); + this.main(); + new Consumer(env, this.input); } else { if (cluster.isMaster) { - this.master(cluster.fork()); + this.main(cluster.fork()); } else { - new Consumer(); + const Consumer = require("./consumer").default; + new Consumer(env, this.input); } } }; - master = (worker) => { + main = (worker) => { + const LossyBuffer = require("./utils/lossyBuffer").default; + const ConnectorFactory = require("./connectorFactory").default; + console.log("BGPalerter, version:", this.version, "environment:", this.config.environment); - console.log("Loaded config:", this.configFile); // Write pid on a file if (this.config.pidFile) { try { - fs.writeFileSync(this.config.pidFile, process.pid); + fs.writeFileSync(this.config.pidFile, (process.pid || "").toString()); } catch (error) { this.logger.log({ level: 'error', @@ -89,7 +90,7 @@ export default class Worker { if (this.config.uptimeMonitor) { this.logger.log({ level: 'error', - message: "The uptime monitor configuration changed. Please see the documentation https://github.com/nttgin/BGPalerter/blob/master/docs/process-monitors.md" + message: "The uptime monitor configuration changed. Please see the documentation https://github.com/nttgin/BGPalerter/blob/main/docs/process-monitors.md" }); } @@ -104,9 +105,9 @@ export default class Worker { this.config.maxMessagesPerSecond = this.config.maxMessagesPerSecond || 6000; const buffer = new LossyBuffer(parseInt(this.config.maxMessagesPerSecond /(1000/bufferCleaningInterval)), bufferCleaningInterval, this.logger); connectorFactory.loadConnectors(); - return connectorFactory.connectConnectors() + return connectorFactory + .connectConnectors(this.input) .then(() => { - for (const connector of connectorFactory.getConnectors()) { connector.onMessage((message) => { @@ -127,7 +128,6 @@ export default class Worker { } }) - .then(() => connectorFactory.subscribeConnectors(this.input)) .catch(error => { this.logger.log({ level: 'error', diff --git a/tests/.cache_clone/status-asn-monitor.json b/tests/.cache_clone/status-asn-monitor.json new file mode 100644 index 00000000..f2ae5505 --- /dev/null +++ b/tests/.cache_clone/status-asn-monitor.json @@ -0,0 +1 @@ +{"date":1593370458186,"value":{"alerts":{"2914-2.2.2.5/22":[{"timestamp":1593370453180,"affected":2914,"matchedRule":{"asn":[2914],"group":"default"},"matchedMessage":{"type":"announcement","prefix":"2.2.2.5/22","peer":"124.0.0.3","path":[1,2,3,4321,5060,2914],"originAS":[2914],"nextHop":"124.0.0.3","aggregator":null},"extra":{}},{"timestamp":1593370454189,"affected":2914,"matchedRule":{"asn":[2914],"group":"default"},"matchedMessage":{"type":"announcement","prefix":"2.2.2.5/22","peer":"124.0.0.3","path":[1,2,3,4321,5060,2914],"originAS":[2914],"nextHop":"124.0.0.3","aggregator":null},"extra":{}},{"timestamp":1593370455202,"affected":2914,"matchedRule":{"asn":[2914],"group":"default"},"matchedMessage":{"type":"announcement","prefix":"2.2.2.5/22","peer":"124.0.0.3","path":[1,2,3,4321,5060,2914],"originAS":[2914],"nextHop":"124.0.0.3","aggregator":null},"extra":{}},{"timestamp":1593370456215,"affected":2914,"matchedRule":{"asn":[2914],"group":"default"},"matchedMessage":{"type":"announcement","prefix":"2.2.2.5/22","peer":"124.0.0.3","path":[1,2,3,4321,5060,2914],"originAS":[2914],"nextHop":"124.0.0.3","aggregator":null},"extra":{}},{"timestamp":1593370457223,"affected":2914,"matchedRule":{"asn":[2914],"group":"default"},"matchedMessage":{"type":"announcement","prefix":"2.2.2.5/22","peer":"124.0.0.3","path":[1,2,3,4321,5060,2914],"originAS":[2914],"nextHop":"124.0.0.3","aggregator":null},"extra":{}}]},"sent":{"2914-2.2.2.3/22":1593370453181,"2914-2001:db9:123::/49":1593370453182},"truncated":{},"fadeOff":{"2914-2.2.2.5/22":1593370453180}}} \ No newline at end of file diff --git a/tests/.cache_clone/status-basic-hijack-detection.json b/tests/.cache_clone/status-basic-hijack-detection.json new file mode 100644 index 00000000..254be2b8 --- /dev/null +++ b/tests/.cache_clone/status-basic-hijack-detection.json @@ -0,0 +1 @@ +{"date":1593370439943,"value":{"alerts":{},"sent":{"15562-4-165.254.255.0/25":1593370434937,"208585-2a00:5884:ffff::/48":1593370434938,"15563-2a00:5884::/32":1593370434939},"truncated":{},"fadeOff":{}}} \ No newline at end of file diff --git a/tests/.cache_clone/status-path-matching.json b/tests/.cache_clone/status-path-matching.json new file mode 100644 index 00000000..5044b430 --- /dev/null +++ b/tests/.cache_clone/status-path-matching.json @@ -0,0 +1 @@ +{"date":1593370452107,"value":{"alerts":{},"sent":{"98.5.4.3/22-1":1593370447102,"99.5.4.3/22-0":1593370447103},"truncated":{},"fadeOff":{}}} \ No newline at end of file diff --git a/tests/.cache_clone/status-prefix-detection.json b/tests/.cache_clone/status-prefix-detection.json new file mode 100644 index 00000000..2a0392a1 --- /dev/null +++ b/tests/.cache_clone/status-prefix-detection.json @@ -0,0 +1 @@ +{"date":1593370446017,"value":{"alerts":{},"sent":{"15562-165.254.255.0/25":1593370441013,"204092-2a00:5884:ffff::/48":1593370441014,"1234-175.254.205.0/25":1593370441016,"1234-170.254.205.0/25":1593370441016},"truncated":{},"fadeOff":{}}} \ No newline at end of file diff --git a/tests/.cache_clone/status-software-update.json b/tests/.cache_clone/status-software-update.json new file mode 100644 index 00000000..177d7b28 --- /dev/null +++ b/tests/.cache_clone/status-software-update.json @@ -0,0 +1 @@ +{"date":1593370429793,"value":{"alerts":{},"sent":{"software-update":1593370424789},"truncated":{},"fadeOff":{}}} \ No newline at end of file diff --git a/tests/.cache_clone/status-withdrawal-detection.json b/tests/.cache_clone/status-withdrawal-detection.json new file mode 100644 index 00000000..470aa109 --- /dev/null +++ b/tests/.cache_clone/status-withdrawal-detection.json @@ -0,0 +1 @@ +{"date":1593370433850,"value":{"alerts":{},"sent":{"165.254.225.0/24":1593370428845,"2a00:5884::/32":1593370428846,"2001:db8:123::/48":1593370428847},"truncated":{},"fadeOff":{}}} \ No newline at end of file diff --git a/tests/1_config.js b/tests/1_config.js index af89c61f..beabd491 100644 --- a/tests/1_config.js +++ b/tests/1_config.js @@ -30,36 +30,31 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var chai = require("chai"); -var chaiSubset = require('chai-subset'); -var readLastLines = require('read-last-lines'); -var moment = require('moment'); -var model = require('../src/model'); +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const readLastLines = require('read-last-lines'); +const moment = require('moment'); +const fs = require('fs'); chai.use(chaiSubset); -var expect = chai.expect; -var AS = model.AS; +const expect = chai.expect; +const volume = "volumetests/"; -var asyncTimeout = 20000; global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; -global.EXTERNAL_CONFIG_FILE = "tests/config.test.yml"; +global.EXTERNAL_CONFIG_FILE = volume + "config.test.yml"; -describe("Composition", function() { - describe("Software updates check", function () { - it("new version detected", function (done) { - - var worker = require("../index"); - var pubSub = worker.pubSub; +// Prepare test environment +if (!fs.existsSync(volume)) { + fs.mkdirSync(volume); +} +fs.copyFileSync("tests/config.test.yml", volume + "config.test.yml"); +fs.copyFileSync("tests/prefixes.test.yml", volume + "prefixes.test.yml"); +fs.copyFileSync("tests/groups.test.yml", volume + "groups.test.yml"); - pubSub.subscribe("software-update", function (type, message) { - expect(type).to.equal("software-update"); - done(); - }); - }).timeout(asyncTimeout); - }); +describe("Core functions", function() { describe("Configuration loader", function () { - var worker = require("../index"); - var config = worker.config; + const worker = require("../index"); + const config = worker.config; it("config structure", function () { expect(config).to.have @@ -79,17 +74,29 @@ describe("Composition", function() { "maxMessagesPerSecond", "fadeOffSeconds", "checkFadeOffGroupsSeconds", - "checkForUpdates", - "checkForUpdatesInterval" + "volume", + "groupsFile", + "persistStatus", + "rpki", + "rest" ]); + expect(config.connectors[0]).to.have .property('class') }); + it("volume setting", function () { + expect(config.volume).to.equals(volume); + }); + + it("check for updates setting", function () { + expect(config.checkForUpdatesAtBoot).to.equals(true); + }); + it("loading connectors", function () { expect(config.connectors[0]).to .containSubset({ - "params": {"testType": "withdrawal"}, + "params": { "testType": "withdrawal" }, "name": "tes" }); expect(config.connectors[0]).to.have @@ -98,7 +105,7 @@ describe("Composition", function() { it("loading monitors", function () { - expect(config.monitors.length).to.equal(6); + expect(config.monitors.length).to.equal(8); expect(config.monitors[0]).to .containSubset({ @@ -145,6 +152,22 @@ describe("Composition", function() { } }); + expect(config.monitors[5]).to + .containSubset({ + "channel": "rpki", + "name": "rpki-monitor", + "params": { + "thresholdMinPeers": 1, + "checkUncovered": true + } + }); + + expect(config.monitors[6]).to + .containSubset({ + "channel": "rpki", + "name": "rpki-monitor" + }); + expect(config.monitors[config.monitors.length - 1]).to .containSubset({ "channel": "software-update", @@ -172,19 +195,51 @@ describe("Composition", function() { .property('class') }); + it("rpki config", function () { + expect(config.rpki).to + .containSubset({ + "vrpProvider": "ntt", + "preCacheROAs": true, + "refreshVrpListMinutes": 15, + "markDataAsStaleAfterMinutes": 120 + }); + }); + + }); + + describe("Software updates check", function () { + it("new version detected", function (done) { + + const worker = require("../index"); + const pubSub = worker.pubSub; + + pubSub.subscribe("software-update", function (message, type) { + expect(type).to.equal("software-update"); + done(); + }); + }).timeout(40000); }); describe("Input loader", function () { - var worker = require("../index"); - var input = worker.input; + const worker = require("../index"); + const input = worker.input; it("loading prefixes", function () { - expect(input.prefixes.length).to.equal(14); + expect(input.prefixes.length).to.equal(15); expect(JSON.parse(JSON.stringify(input))).to .containSubset({ "prefixes": [ + { + "asn": [1234], + "description": "rpki valid not monitored AS", + "ignoreMorespecifics": false, + "prefix": "193.0.0.0/21", + "group": "default", + "excludeMonitors" : [], + "includeMonitors": [] + }, { "asn": [15562], "description": "description 1", @@ -298,14 +353,14 @@ describe("Composition", function() { ] }); - expect(input.asns.map(i => i.asn.getValue())).to.eql([ 2914, 3333, 65000 ]); + expect(input.asns.map(i => i.asn.getValue())).to.eql([ 2914, 3333, 13335, 65000 ]); }); }); describe("Logging", function () { - var worker = require("../index"); - var config = worker.config; - var logger = worker.logger; + const worker = require("../index"); + const config = worker.config; + const logger = worker.logger; it("errors logging on the right file", function (done) { const message = "Test message"; @@ -315,7 +370,7 @@ describe("Composition", function() { message: message }); - const file = config.logging.directory + "/error-" + moment().format('YYYY-MM-DD') + ".log"; + const file = volume + config.logging.directory + "/error-" + moment().format('YYYY-MM-DD') + ".log"; readLastLines .read(file, 1) .then((line) => { @@ -336,7 +391,7 @@ describe("Composition", function() { message: message }); - const file = config.logging.directory + "/reports-" + moment().format('YYYY-MM-DD') + ".log"; + const file = volume + config.logging.directory + "/reports-" + moment().format('YYYY-MM-DD') + ".log"; readLastLines .read(file, 1) .then((line) => { diff --git a/tests/2_alerting.js b/tests/2_alerting.js index 85c8dd05..81bbef0b 100644 --- a/tests/2_alerting.js +++ b/tests/2_alerting.js @@ -30,23 +30,24 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var chai = require("chai"); -var chaiSubset = require('chai-subset'); +const chai = require("chai"); +const fs = require("fs"); +const chaiSubset = require('chai-subset'); chai.use(chaiSubset); -var expect = chai.expect; - -let asyncTimeout = 20000; +const expect = chai.expect; +const volume = "volumetests/"; +const cacheCloneDirectory = "tests/.cache_clone/"; +const asyncTimeout = 120000; global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; -global.EXTERNAL_CONFIG_FILE = "tests/config.test.yml"; +global.EXTERNAL_CONFIG_FILE = volume + "config.test.yml"; +const axios = require("axios"); + +const worker = require("../index"); +const pubSub = worker.pubSub; describe("Alerting", function () { - var worker = require("../index"); - var pubSub = worker.pubSub; it("visibility reporting", function(done) { - - pubSub.publish("test-type", "visibility"); - const expectedData = { "165.254.225.0/24": { id: '165.254.225.0/24', @@ -69,43 +70,45 @@ describe("Alerting", function () { }; let visibilityTestCompleted = false; - pubSub.subscribe("visibility", function (type, message) { - - if (!visibilityTestCompleted) { - message = JSON.parse(JSON.stringify(message)); + pubSub.subscribe("visibility", (message, type) => { + try { + if (!visibilityTestCompleted) { + message = JSON.parse(JSON.stringify(message)); - const id = message.id; + const id = message.id; - expect(Object.keys(expectedData).includes(id)).to.equal(true); - expect(expectedData[id] != null).to.equal(true); + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); - expect(message).to - .containSubset(expectedData[id]); + expect(message).to + .containSubset(expectedData[id]); - expect(message).to.contain - .keys([ - "latest", - "earliest" - ]); + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); - delete expectedData[id]; - if (Object.keys(expectedData).length === 0) { - setTimeout(() => { - visibilityTestCompleted = true; - done(); - }, 5000); + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + visibilityTestCompleted = true; + done(); + }, 5000); + } } + } catch (error) { + visibilityTestCompleted = true; + done(error); } }); - + pubSub.publish("test-type", "visibility"); }).timeout(asyncTimeout); it("hijack reporting", function(done) { - pubSub.publish("test-type", "hijack"); - const expectedData = { "15562-4-165.254.255.0/25": { id: '15562-4-165.254.255.0/25', @@ -185,44 +188,41 @@ describe("Alerting", function () { } ] } - }; let hijackTestCompleted = false - pubSub.subscribe("hijack", function(type, message){ - - if (!hijackTestCompleted) { - message = JSON.parse(JSON.stringify(message)); - - const id = message.id; + pubSub.subscribe("hijack", (message, type) => { + try { + if (!hijackTestCompleted) { + message = JSON.parse(JSON.stringify(message)); - expect(Object.keys(expectedData).includes(id)).to.equal(true); - expect(expectedData[id] != null).to.equal(true); + const id = message.id; - expect(message).to - .containSubset(expectedData[id]); + expect(Object.keys(expectedData).includes(id)).to.be.true; + expect(expectedData[id] != null).to.be.true; + expect(message).to.containSubset(expectedData[id]); + expect(message).to.contain.keys(["latest", "earliest"]); - expect(message).to.contain - .keys([ - "latest", - "earliest" - ]); + delete expectedData[id]; - delete expectedData[id]; - if (Object.keys(expectedData).length === 0) { - setTimeout(() => { - hijackTestCompleted = true; - done(); - }, 5000); + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + hijackTestCompleted = true; + done(); + }, 5000); + } } + } catch (error) { + hijackTestCompleted = true; + done(error); } }); + pubSub.publish("test-type", "hijack"); + }).timeout(asyncTimeout); it("newprefix reporting", function (done) { - pubSub.publish("test-type", "newprefix"); - const expectedData = { "1234-175.254.205.0/25": { id: '1234-175.254.205.0/25', @@ -337,109 +337,125 @@ describe("Alerting", function () { }; let newprefixTestCompleted = false; - pubSub.subscribe("newprefix", function (type, message) { - - if (!newprefixTestCompleted) { - message = JSON.parse(JSON.stringify(message)); - - const id = message.id; - - expect(Object.keys(expectedData).includes(id)).to.equal(true); - expect(expectedData[id] != null).to.equal(true); - - expect(message).to - .containSubset(expectedData[id]); - - expect(message).to.contain - .keys([ - "latest", - "earliest" - ]); - - delete expectedData[id]; - if (Object.keys(expectedData).length === 0) { - setTimeout(() => { - newprefixTestCompleted = true; - done(); - }, 5000); + pubSub.subscribe("newprefix", (message, type) => { + try { + if (!newprefixTestCompleted) { + message = JSON.parse(JSON.stringify(message)); + + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + + expect(message).to + .containSubset(expectedData[id]); + + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + newprefixTestCompleted = true; + done(); + }, 5000); + } } + } catch (error) { + newprefixTestCompleted = true; + done(error); } }); - - - + pubSub.publish("test-type", "newprefix"); }).timeout(asyncTimeout); it("path match reporting", function (done) { - - pubSub.publish("test-type", "path"); - const expectedData = { - "98.5.4.3/22": { - id: '98.5.4.3/22', + "98.5.4.3/22-1": { + id: '98.5.4.3/22-1', origin: 'path-matching', affected: "98.5.4.3/22", - message: 'Matched test description on prefix 98.5.4.3/22 (including length violation) 1 times', - data: [ - { - extra: { - lengthViolation: true - }, - matchedRule: { - prefix: '98.5.4.3/22', - group: 'default', - description: 'path matching test regex and maxLength', - asn: [2914], - ignoreMorespecifics: false, - ignore: false, - path: { - match: ".*2914$", - matchDescription: "test description", - maxLength: 3, + message: 'Matched test description2 on prefix 98.5.4.3/22 (including length violation) 1 times', + "data": + [ + { + + "affected": "98.5.4.3/22", + "matchedRule": { + "prefix": "98.5.4.3/22", + "group": "default", + "ignore": false, + "excludeMonitors": [], + "includeMonitors": [], + "description": "path matching test regex and maxLength", + "asn": [2914], + "ignoreMorespecifics": false, + "path": [ + { + "match": ".*2915$", + "maxLength": 4, + "matchDescription": "test description1" + }, { + "match": ".*2914$", + "maxLength": 3, + "matchDescription": "test description2" + } + ] + }, "matchedMessage": { + "type": "announcement", + "prefix": "98.5.4.3/22", + "peer": "124.0.0.3", + "path": [1, 2, 3, 4321, 5060, 2914], + "originAS": [2914], + "nextHop": "124.0.0.3", + "aggregator": null + }, + "extra": { + "lengthViolation": true, + "matchDescription": "test description2" } - }, - matchedMessage: { - type: 'announcement', - prefix: '98.5.4.3/22', - peer: '124.0.0.3', - path: [1, 2, 3, 4321, 5060, 2914], - originAS: [2914], - nextHop: '124.0.0.3' } - } - ] + ] }, - "99.5.4.3/22": { - id: '99.5.4.3/22', + "99.5.4.3/22-0": { + id: '99.5.4.3/22-0', origin: 'path-matching', affected: "99.5.4.3/22", message: 'Matched test description on prefix 99.5.4.3/22 1 times', data: [ { - extra: { - lengthViolation: false - }, - matchedRule: { - prefix: '99.5.4.3/22', - group: 'default', - description: 'path matching test regex and minLength', - asn: [2914], - ignoreMorespecifics: false, - ignore: false, - path: { - match: ".*2914$", - matchDescription: "test description", - minLength: 2, + "affected": "99.5.4.3/22", + "matchedRule": { + "prefix": "99.5.4.3/22", + "group": "default", + "ignore": false, + "excludeMonitors": [], + "includeMonitors": [], + "description": "path matching test regex and minLength", + "asn": [2914], + "ignoreMorespecifics": false, + "path": { + "match": ".*2914$", + "minLength": 2, + "matchDescription": "test description" } }, - matchedMessage: { - type: 'announcement', - prefix: '99.5.4.3/22', - peer: '124.0.0.3', - path: [1, 2, 3, 4321, 5060, 2914], - originAS: [2914], - nextHop: '124.0.0.3' + "matchedMessage": { + "type": "announcement", + "prefix": "99.5.4.3/22", + "peer": "124.0.0.3", + "path": [1, 2, 3, 4321, 5060, 2914], + "originAS": [2914], + "nextHop": "124.0.0.3", + "aggregator": null + }, + "extra": { + "lengthViolation": false, + "matchDescription": "test description" } } ] @@ -447,42 +463,43 @@ describe("Alerting", function () { }; let pathTestCompleted = false; - pubSub.subscribe("path", function (type, message) { - - if (!pathTestCompleted) { - message = JSON.parse(JSON.stringify(message)); - const id = message.id; - - expect(Object.keys(expectedData).includes(id)).to.equal(true); - expect(expectedData[id] != null).to.equal(true); - - expect(message).to - .containSubset(expectedData[id]); - - expect(message).to.contain - .keys([ - "latest", - "earliest" - ]); - - delete expectedData[id]; - if (Object.keys(expectedData).length === 0) { - setTimeout(() => { - pathTestCompleted = true; - done(); - }, 5000); + pubSub.subscribe("path", (message, type) => { + try { + if (!pathTestCompleted) { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + + expect(message).to + .containSubset(expectedData[id]); + + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + pathTestCompleted = true; + done(); + }, 5000); + } } + } catch (error) { + pathTestCompleted = true; + done(error); } }); + pubSub.publish("test-type", "path"); }).timeout(asyncTimeout); - - it("asn monitoring reporting", function (done) { - pubSub.publish("test-type", "misconfiguration"); - const expectedData = { "2914-2.2.2.3/22": { id: "2914-2.2.2.3/22", @@ -499,53 +516,89 @@ describe("Alerting", function () { }; let misconfigurationTestCompleted = false; - pubSub.subscribe("misconfiguration", function (type, message) { + pubSub.subscribe("misconfiguration", (message, type) => { if (!misconfigurationTestCompleted) { - message = JSON.parse(JSON.stringify(message)); - const id = message.id; - - expect(Object.keys(expectedData).includes(id)).to.equal(true); - expect(expectedData[id] != null).to.equal(true); - - expect(message).to - .containSubset(expectedData[id]); - - expect(message).to.contain - .keys([ - "latest", - "earliest" - ]); - - delete expectedData[id]; - if (Object.keys(expectedData).length === 0) { - setTimeout(() => { - misconfigurationTestCompleted = true; - done(); - }, 5000); + try { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + expect(message).to.containSubset(expectedData[id]); + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + misconfigurationTestCompleted = true; + done(); + }, 5000); + } + } catch (error) { + misconfigurationTestCompleted = true; + done(error); } } }); + pubSub.publish("test-type", "misconfiguration"); }).timeout(asyncTimeout); - it("fading alerting", function (done) { - - pubSub.publish("test-type", "fade-off"); - let notReceived = true; setTimeout(() => { if (notReceived){ done(); + } else { + done(new Error("Not received")); } }, 15000); - pubSub.subscribe("visibility", function (type, message) { + pubSub.subscribe("visibility", function (message, type) { notReceived = false; }); + pubSub.publish("test-type", "fade-off"); + }).timeout(asyncTimeout); + it("pull API alerting", function (done) { + + axios({ + url: "http://localhost:8011/alerts/8e402e65f393ba4812df5da0db7605e9", + responseType: "json", + method: "GET" + }) + .then(a => { + expect(a.data.data[0].hash).to.equal("8e402e65f393ba4812df5da0db7605e9"); + done(); + }) }).timeout(asyncTimeout); +}); + +describe("Status storage", function () { + it("alerts stored", function (done) { + + const files = fs.readdirSync(cacheCloneDirectory); + for (let f of files) { + const fileClone = cacheCloneDirectory + f; + const fileOriginal = volume + ".cache/" + f; + const exists = fs.existsSync(fileOriginal); + + expect(exists).to.equal(true); + + if (exists) { + const clone = JSON.parse(fs.readFileSync(fileClone, 'utf8')).value; + const original = JSON.parse(fs.readFileSync(fileOriginal, 'utf8')).value; + expect(original.sent).to.have.keys(Object.keys(clone.sent)); + } + } + done(); + + }).timeout(asyncTimeout); }); diff --git a/tests/3_uptimemonitor.js b/tests/3_uptimemonitor.js index 89cd9bcb..9a4efeff 100644 --- a/tests/3_uptimemonitor.js +++ b/tests/3_uptimemonitor.js @@ -30,47 +30,44 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var chai = require("chai"); -var chaiSubset = require('chai-subset'); -var axios = require('axios'); -var model = require('../src/model'); +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const axios = require('axios'); chai.use(chaiSubset); -var expect = chai.expect; -var AS = model.AS; - -var asyncTimeout = 20000; +const expect = chai.expect; +const volume = "volumetests/"; +const asyncTimeout = 20000; global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; -global.EXTERNAL_CONFIG_FILE = "tests/config.test.yml"; +global.EXTERNAL_CONFIG_FILE = volume + "config.test.yml"; describe("Uptime Monitor", function() { - var worker = require("../index"); - var config = worker.config; + const worker = require("../index"); + const config = worker.config; it("uptime config", function () { expect(config.processMonitors[0]).to .containSubset({ params: { - useStatusCodes: true, - host: null, - port: 8011 + useStatusCodes: true } }); }); it("API format and header", function (done) { - const port = config.processMonitors[0].params.port; - axios({ method: 'get', responseType: 'json', - url: `http://localhost:${port}/status` + url: `http://localhost:8011/status` }) .then(data => { expect(data.status).to.equal(200); expect(data.data.warning).to.equal(false); done(); + }) + .catch(error => { + console.log(error); }); }).timeout(asyncTimeout); diff --git a/tests/4_groups.js b/tests/4_groups.js new file mode 100644 index 00000000..4543416b --- /dev/null +++ b/tests/4_groups.js @@ -0,0 +1,84 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const fs = require("fs"); +const chaiSubset = require('chai-subset'); +chai.use(chaiSubset); +const expect = chai.expect; +const volume = "volumetests/"; +const asyncTimeout = 20000; + +// Prepare test environment +if (!fs.existsSync(volume)) { + fs.mkdirSync(volume); +} +fs.copyFileSync("tests/config.test.yml", volume + "config.test.yml"); +fs.copyFileSync("tests/prefixes.test.yml", volume + "prefixes.test.yml"); +fs.copyFileSync("tests/groups.test.yml", volume + "groups.test.yml"); + +global.EXTERNAL_CONFIG_FILE = volume + "config.test.yml"; + +const worker = require("../index"); + +describe("External groups file", function() { + + it("load groups", function () { + const config = worker.config; + expect(config.groupsFile).to.equal("groups.test.yml"); + expect(config.reports[0].params.userGroups).to + .containSubset({ + test: [ + "filename" + ] + }); + }) + .timeout(asyncTimeout); + + it("watch groups", function (done) { + + fs.copyFileSync("tests/groups.test.after.yml", "volumetests/groups.test.yml"); + + setTimeout(() => { + const config = worker.config; + expect(config.reports[0].params.userGroups).to + .containSubset({ + test: [ + "filename-after" + ] + }); + done(); + }, 10000); + + }) + .timeout(asyncTimeout); +}); \ No newline at end of file diff --git a/tests/config.test.yml b/tests/config.test.yml index 9950eeb3..2e34a11b 100644 --- a/tests/config.test.yml +++ b/tests/config.test.yml @@ -37,6 +37,17 @@ monitors: params: thresholdMinPeers: 2 + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + thresholdMinPeers: 1 + checkUncovered: true + + - file: monitorROAS + channel: rpki + name: rpki-monitor + reports: - file: reportFile channels: @@ -45,39 +56,63 @@ reports: - visibility - path - misconfiguration + - rpki params: persistAlertData: false alertDataDirectory: alertdata/ -notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds -alertOnlyOnce: false -fadeOffSeconds: 10 -checkFadeOffGroupsSeconds: 2 + - file: reportPullAPI + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + maxAlertsAmount: 100 + +rest: + host: null + port: 8011 + # The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example # This is an array (use new lines and dashes!) monitoredPrefixesFiles: - - tests/prefixes.test.yml + - prefixes.test.yml logging: directory: logs logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated - backlogSize: 1 maxRetainedFiles: 10 maxFileSizeMB: 15 compressOnRotation: true -checkForUpdatesAtBoot: true -checkForUpdates: true +checkForUpdatesAtBoot: false +checkForUpdates: false checkForUpdatesInterval: 432000000 +persistStatus: true + +volume: volumetests/ + +groupsFile: groups.test.yml processMonitors: - file: uptimeApi params: useStatusCodes: true - host: null - port: 8011 +tests/config.test.yml +rpki: + vrpProvider: ntt + preCacheROAs: true + refreshVrpListMinutes: 15 + markDataAsStaleAfterMinutes: 120 +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 pidFile: bgpalerter.pid multiProcess: false maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/dump_tests/config.test.yml b/tests/dump_tests/config.test.yml new file mode 100644 index 00000000..aafb084c --- /dev/null +++ b/tests/dump_tests/config.test.yml @@ -0,0 +1,57 @@ +environment: test + +connectors: + - file: connectorRISDump + name: dmp + +monitors: + - file: monitorHijack + channel: hijack + name: basic-hijack-detection + params: + thresholdMinPeers: 0 + +reports: + - file: reportFile + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + persistAlertData: false + alertDataDirectory: alertdata/ + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: true +persistStatus: true + +volume: volumetests/ + + +rpki: + preCacheROAs: true + refreshVrpListMinutes: 15 + vrpFile: tests/dump_tests/vrps.json + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/dump_tests/groups.test.yml b/tests/dump_tests/groups.test.yml new file mode 100644 index 00000000..5db79ce6 --- /dev/null +++ b/tests/dump_tests/groups.test.yml @@ -0,0 +1,4 @@ + +reportFile: + test: + - filename \ No newline at end of file diff --git a/tests/dump_tests/prefixes.test.yml b/tests/dump_tests/prefixes.test.yml new file mode 100644 index 00000000..267e7210 --- /dev/null +++ b/tests/dump_tests/prefixes.test.yml @@ -0,0 +1,7 @@ +193.0.20.0/23: + description: No description provided + asn: + - 1234 + ignoreMorespecifics: false + ignore: false + group: noc \ No newline at end of file diff --git a/tests/dump_tests/tests.js b/tests/dump_tests/tests.js new file mode 100644 index 00000000..ed2bce1f --- /dev/null +++ b/tests/dump_tests/tests.js @@ -0,0 +1,105 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const fs = require("fs"); +const chaiSubset = require('chai-subset'); +const readLastLines = require("read-last-lines"); +chai.use(chaiSubset); +const expect = chai.expect; +const volume = "volumetests/"; +const asyncTimeout = 120000; +global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; +global.EXTERNAL_CONFIG_FILE = volume + "config.test.yml"; + +// Prepare test environment +if (!fs.existsSync(volume)) { + fs.mkdirSync(volume); +} else { + fs.rmdirSync(volume, { recursive: true }); + fs.mkdirSync(volume); +} +fs.copyFileSync("tests/dump_tests/config.test.yml", volume + "config.test.yml"); +fs.copyFileSync("tests/dump_tests/prefixes.test.yml", volume + "prefixes.test.yml"); + +describe("Alerting", function () { + + it("RIS dump test", function (done) { + + const worker = require("../../index"); + const pubSub = worker.pubSub; + + const expectedData = { + "3333-193.0.20.0/23": { + id: '3333-193.0.20.0/23', + truncated: false, + origin: 'basic-hijack-detection', + affected: 1234, + message: 'The prefix 193.0.20.0/23 (No description provided) is announced by AS3333 instead of AS1234', + data: [ + { + affected: 1234 + } + ] + } + }; + + let dumpTestCompleted = false; + pubSub.subscribe("hijack", function (message, type) { + try { + if (!dumpTestCompleted) { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + + expect(message).to + .containSubset(expectedData[id]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + dumpTestCompleted = true; + done(); + }, 5000); + } + } + } catch (error) { + dumpTestCompleted = true; + done(error); + } + }); + + }).timeout(asyncTimeout); + +}); \ No newline at end of file diff --git a/tests/dump_tests/vrps.json b/tests/dump_tests/vrps.json new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/tests/dump_tests/vrps.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/generate_tests/config.test.yml b/tests/generate_tests/config.test.yml new file mode 100644 index 00000000..2268dcc0 --- /dev/null +++ b/tests/generate_tests/config.test.yml @@ -0,0 +1,82 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorHijack + channel: hijack + name: basic-hijack-detection + params: + thresholdMinPeers: 0 + + - file: monitorNewPrefix + channel: newprefix + name: prefix-detection + params: + thresholdMinPeers: 0 + + - file: monitorVisibility + channel: visibility + name: withdrawal-detection + params: + thresholdMinPeers: 4 + + - file: monitorPath + channel: path + name: path-matching + params: + thresholdMinPeers: 0 + + - file: monitorAS + channel: misconfiguration + name: asn-monitor + params: + thresholdMinPeers: 2 + + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + thresholdMinPeers: 1 + checkUncovered: true + + +reports: +# - file: reportFile +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# persistAlertData: false +# alertDataDirectory: alertdata/ + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - prefixes.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: false + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/generate_tests/prefixes.final.append.yml b/tests/generate_tests/prefixes.final.append.yml new file mode 100644 index 00000000..f4470b10 --- /dev/null +++ b/tests/generate_tests/prefixes.final.append.yml @@ -0,0 +1,62 @@ +1.1.1.1/23: + description: No description provided + asn: + - 2222 + ignoreMorespecifics: false + ignore: false + group: default +193.0.18.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +'2001:67c:2e8::/48': + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.10.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.20.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.22.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.0.0/21: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.12.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +options: + monitorASns: + '2222': + group: default + '3333': + group: test \ No newline at end of file diff --git a/tests/generate_tests/prefixes.final.default.yml b/tests/generate_tests/prefixes.final.default.yml new file mode 100644 index 00000000..8c6b365f --- /dev/null +++ b/tests/generate_tests/prefixes.final.default.yml @@ -0,0 +1,46 @@ +193.0.10.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: noc +193.0.20.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: noc +193.0.0.0/21: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: noc +193.0.12.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: noc +193.0.18.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: noc +193.0.22.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: noc +options: + monitorASns: + '3333': + group: noc diff --git a/tests/generate_tests/prefixes.final.group.yml b/tests/generate_tests/prefixes.final.group.yml new file mode 100644 index 00000000..21a9d7e0 --- /dev/null +++ b/tests/generate_tests/prefixes.final.group.yml @@ -0,0 +1,53 @@ +193.0.10.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.20.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.0.0/21: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.12.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.18.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +193.0.22.0/23: + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +'2001:67c:2e8::/48': + description: No description provided + asn: + - 3333 + ignoreMorespecifics: false + ignore: false + group: test +options: + monitorASns: + '3333': + group: test diff --git a/tests/generate_tests/prefixes.initial.append.yml b/tests/generate_tests/prefixes.initial.append.yml new file mode 100644 index 00000000..03aaace8 --- /dev/null +++ b/tests/generate_tests/prefixes.initial.append.yml @@ -0,0 +1,11 @@ +1.1.1.1/23: + description: No description provided + asn: + - 2222 + ignoreMorespecifics: false + ignore: false + group: default +options: + monitorASns: + '2222': + group: default \ No newline at end of file diff --git a/tests/generate_tests/tests.js b/tests/generate_tests/tests.js new file mode 100644 index 00000000..a431a0ff --- /dev/null +++ b/tests/generate_tests/tests.js @@ -0,0 +1,167 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const fs = require("fs"); +const yaml = require("js-yaml"); +const chaiSubset = require('chai-subset'); +const generatePrefixes = require('../../src/generatePrefixesList'); +const expect = chai.expect; +const asyncTimeout = 120000; +chai.use(chaiSubset); + +global.EXTERNAL_CONFIG_FILE = "tests/generate_tests/config.test.yml"; + +describe("Prefix List", function() { + + it("generate file - default group - exclude 1 prefix", function (done) { + const asns = ["3333"]; + const outputFile = "tests/generate_tests/prefixes.yml"; + const originalFile = "tests/generate_tests/prefixes.final.default.yml"; + + const inputParameters = { + asnList: asns, + outputFile, + exclude: ["2001:67c:2e8::/48"], + excludeDelegated: true, + prefixes: null, + monitoredASes: asns, + httpProxy: null, + debug: false, + historical: true, + group: null, + append: false, + logger: () => {} + } + generatePrefixes(inputParameters) + .then(content => { + fs.writeFileSync(outputFile, yaml.dump(content)); + }) + .then(() => { + const result = fs.readFileSync(outputFile, 'utf8'); + fs.unlinkSync(outputFile); + const original = fs.readFileSync(originalFile, 'utf8'); + const resultJson = yaml.load(result) || {}; + const originalJson = yaml.load(original) || {}; + + expect(resultJson).to.contain.keys(Object.keys(originalJson)); + expect(Object.keys(resultJson).length).to.equal(Object.keys(originalJson).length); + + expect(resultJson).to.containSubset(originalJson); + done(); + }); + }).timeout(asyncTimeout); + + it("generate file - specific group - no exclude", function (done) { + const asns = ["3333"]; + const outputFile = "tests/generate_tests/prefixes.yml"; + const originalFile = "tests/generate_tests/prefixes.final.group.yml"; + + const inputParameters = { + asnList: asns, + outputFile, + exclude: [], + excludeDelegated: true, + prefixes: null, + monitoredASes: asns, + httpProxy: null, + debug: false, + historical: true, + group: "test", + append: false, + logger: () => {} + } + + generatePrefixes(inputParameters) + .then(content => { + fs.writeFileSync(outputFile, yaml.dump(content)); + }) + .then(() => { + const result = fs.readFileSync(outputFile, 'utf8'); + fs.unlinkSync(outputFile); + const original = fs.readFileSync(originalFile, 'utf8'); + const resultJson = yaml.load(result) || {}; + const originalJson = yaml.load(original) || {}; + + expect(resultJson).to.contain.keys(Object.keys(originalJson)); + expect(Object.keys(resultJson).length).to.equal(Object.keys(originalJson).length); + + expect(resultJson).to.containSubset(originalJson); + done(); + }); + }).timeout(asyncTimeout); + + it("generate file - append - no exclude", function (done) { + const asns = ["3333"]; + const outputFile = "tests/generate_tests/prefixes.yml"; + const initialFile = "tests/generate_tests/prefixes.initial.append.yml"; + fs.copyFileSync(initialFile, outputFile); + const originalFile = "tests/generate_tests/prefixes.final.append.yml"; + + const inputParameters = { + asnList: asns, + outputFile, + exclude: [], + excludeDelegated: true, + prefixes: null, + monitoredASes: asns, + httpProxy: null, + debug: false, + historical: true, + group: "test", + append: true, + logger: () => {}, + getCurrentPrefixesList: () => { + const content = yaml.load(fs.readFileSync(outputFile, "utf8")); + return Promise.resolve(content); + } + } + + generatePrefixes(inputParameters) + .then(content => { + fs.writeFileSync(outputFile, yaml.dump(content)); + }) + .then(() => { + const result = fs.readFileSync(outputFile, 'utf8'); + fs.unlinkSync(outputFile); + const original = fs.readFileSync(originalFile, 'utf8'); + const resultJson = yaml.load(result) || {}; + const originalJson = yaml.load(original) || {}; + + expect(resultJson).to.contain.keys(Object.keys(originalJson)); + expect(Object.keys(resultJson).length).to.equal(Object.keys(originalJson).length); + + expect(resultJson).to.containSubset(originalJson); + done(); + }); + }).timeout(asyncTimeout); +}); \ No newline at end of file diff --git a/tests/groups.test.after.yml b/tests/groups.test.after.yml new file mode 100644 index 00000000..f41e16cd --- /dev/null +++ b/tests/groups.test.after.yml @@ -0,0 +1,4 @@ + +reportFile: + test: + - filename-after \ No newline at end of file diff --git a/tests/groups.test.yml b/tests/groups.test.yml new file mode 100644 index 00000000..5db79ce6 --- /dev/null +++ b/tests/groups.test.yml @@ -0,0 +1,4 @@ + +reportFile: + test: + - filename \ No newline at end of file diff --git a/tests/kafka_tests/config.kafka.test.yml b/tests/kafka_tests/config.kafka.test.yml new file mode 100644 index 00000000..1f7bc7be --- /dev/null +++ b/tests/kafka_tests/config.kafka.test.yml @@ -0,0 +1,85 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorHijack + channel: hijack + name: basic-hijack-detection + params: + thresholdMinPeers: 0 + + - file: monitorNewPrefix + channel: newprefix + name: prefix-detection + params: + thresholdMinPeers: 0 + + - file: monitorVisibility + channel: visibility + name: withdrawal-detection + params: + thresholdMinPeers: 4 + + - file: monitorPath + channel: path + name: path-matching + params: + thresholdMinPeers: 0 + + - file: monitorAS + channel: misconfiguration + name: asn-monitor + params: + thresholdMinPeers: 2 + +# - file: monitorRPKI +# channel: rpki +# name: rpki-monitor +# params: +# thresholdMinPeers: 1 +# checkUncovered: true + + +reports: + - file: reportKafka + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + host: localhost + port: 9092 + topics: + default: bgpalerter + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - tests/prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: false +persistStatus: false + + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/kafka_tests/testReportKafka.js b/tests/kafka_tests/testReportKafka.js new file mode 100644 index 00000000..72f83e51 --- /dev/null +++ b/tests/kafka_tests/testReportKafka.js @@ -0,0 +1,75 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const asyncTimeout = 60000; +chai.use(chaiSubset); + +const { Kafka } = require('kafkajs') +const kafka = new Kafka({ + clientId: 'bgpalerter', + brokers: ['localhost:9092'] +}); + +global.EXTERNAL_CONFIG_FILE = "tests/kafka_tests/config.kafka.test.yml"; + +describe("Reports 1", function() { + const worker = require("../../index"); + const pubSub = worker.pubSub; + + it("kafka", function (done) { + let doneCalled = false; + const consumer = kafka.consumer({ groupId: 'bgpalerter' }); + consumer.connect() + consumer + .subscribe({ topic: 'bgpalerter', fromBeginning: true }) + .then(() => { + + pubSub.publish("test-type", "visibility"); + consumer.run({ + eachMessage: ({ topic, partition, message }) => { + if (!doneCalled) { + done(); + doneCalled = true; + } + return Promise.resolve() + }, + }); + }) + .catch(error => { + console.log(error); + }); + + }).timeout(asyncTimeout); + +}); \ No newline at end of file diff --git a/tests/neighbor_tests/config.test.yml b/tests/neighbor_tests/config.test.yml new file mode 100644 index 00000000..804da74d --- /dev/null +++ b/tests/neighbor_tests/config.test.yml @@ -0,0 +1,102 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorHijack + channel: hijack + name: basic-hijack-detection + params: + thresholdMinPeers: 0 + + - file: monitorNewPrefix + channel: newprefix + name: prefix-detection + params: + thresholdMinPeers: 0 + + - file: monitorVisibility + channel: visibility + name: withdrawal-detection + params: + thresholdMinPeers: 4 + + - file: monitorPath + channel: path + name: path-matching + params: + thresholdMinPeers: 0 + + - file: monitorAS + channel: misconfiguration + name: asn-monitor + params: + thresholdMinPeers: 2 + + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + thresholdMinPeers: 1 + checkUncovered: true + + - file: monitorROAS + channel: rpki + name: rpki-monitor + + - file: monitorPathNeighbors + channel: path-neighbors + name: path-neighbors + params: + thresholdMinPeers: 0 + +reports: + - file: reportFile + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + persistAlertData: false + alertDataDirectory: alertdata/ + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: true +persistStatus: true + +volume: volumetests/ + +groupsFile: groups.test.yml + +rpki: + vrpProvider: ntt + preCacheROAs: true + refreshVrpListMinutes: 15 + markDataAsStaleAfterMinutes: 120 + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/neighbor_tests/groups.test.yml b/tests/neighbor_tests/groups.test.yml new file mode 100644 index 00000000..5db79ce6 --- /dev/null +++ b/tests/neighbor_tests/groups.test.yml @@ -0,0 +1,4 @@ + +reportFile: + test: + - filename \ No newline at end of file diff --git a/tests/neighbor_tests/prefixes.test.yml b/tests/neighbor_tests/prefixes.test.yml new file mode 100644 index 00000000..4026fee6 --- /dev/null +++ b/tests/neighbor_tests/prefixes.test.yml @@ -0,0 +1,23 @@ +9.5.4.3/22: + description: test + asn: 101 + ignoreMorespecifics: false + +99.5.4.3/22: + description: test + asn: 101 + ignoreMorespecifics: false + +options: + monitorASns: + 101: + groupd: default + upstreams: + - 100 + downstreams: + - 104 + 80: + groupd: default + upstreams: + - 99 + downstreams: diff --git a/tests/neighbor_tests/tests.js b/tests/neighbor_tests/tests.js new file mode 100644 index 00000000..9e5824eb --- /dev/null +++ b/tests/neighbor_tests/tests.js @@ -0,0 +1,186 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const fs = require("fs"); +const chaiSubset = require('chai-subset'); +chai.use(chaiSubset); +const expect = chai.expect; +const volume = "volumetests/"; +const asyncTimeout = 120000; +global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; +global.EXTERNAL_CONFIG_FILE = volume + "config.test.yml"; + +// Prepare test environment +if (!fs.existsSync(volume)) { + fs.mkdirSync(volume); +} else { + fs.rmdirSync(volume, { recursive: true }); + fs.mkdirSync(volume); +} +fs.copyFileSync("tests/neighbor_tests/config.test.yml", volume + "config.test.yml"); +fs.copyFileSync("tests/neighbor_tests/prefixes.test.yml", volume + "prefixes.test.yml"); +fs.copyFileSync("tests/neighbor_tests/groups.test.yml", volume + "groups.test.yml"); + + +const worker = require("../../index"); +const pubSub = worker.pubSub; + +describe("Alerting", function () { + + it("path-neighbors monitoring reporting", function (done) { + + const expectedData = { + "101-30": { + "id": "101-30", + "truncated": false, + "origin": "path-neighbors", + "affected": 101, + "message": "A new upstream of AS101 has been detected: AS30", + "data": [{ + "affected": 101, + "matchedRule": { + "asn": [101], + "group": "default", + "groupd": "default", + "upstreams": [100], + "downstreams": [104] + }, + "matchedMessage": { + "type": "announcement", + "prefix": "99.5.4.3/22", + "peer": "124.0.0.3", + "path": [98, 99, 30, 101, 104], + "originAS": [104], + "nextHop": "124.0.0.3", + "aggregator": null, + "timestamp": null, + "communities": [] + }, + "extra": {"side": "upstream", "neighbor": 30} + }] + }, + + "80-100": { + "id": "80-100", + "truncated": false, + "origin": "path-neighbors", + "affected": 80, + "message": "A new downstream of AS80 has been detected: AS100", + "data": [{ + "affected": 80, + "matchedRule": { + "asn": [80], + "group": "default", + "groupd": "default", + "upstreams": [99], + "downstreams": null + }, + "matchedMessage": { + "type": "announcement", + "prefix": "99.5.4.3/22", + "peer": "124.0.0.3", + "path": [98, 99, 80, 100], + "originAS": [100], + "nextHop": "124.0.0.3", + "aggregator": null, + "communities": [] + }, + "extra": {"side": "downstream", "neighbor": 100} + }] + }, + + "101-106": { + "id": "101-106", + "truncated": false, + "origin": "path-neighbors", + "affected": 101, + "message": "A new downstream of AS101 has been detected: AS106", + "data": [{ + "affected": 101, + "matchedRule": { + "asn": [101], + "group": "default", + "groupd": "default", + "upstreams": [100], + "downstreams": [104] + }, + "matchedMessage": { + "type": "announcement", + "prefix": "9.5.4.3/22", + "peer": "124.0.0.3", + "path": [98, 99, 100, 101, 106], + "originAS": [106], + "nextHop": "124.0.0.3", + "aggregator": null, + "communities": [] + }, + "extra": {"side": "downstream", "neighbor": 106} + }] + } + }; + + let pathNeighborsTestcompleted = false; + pubSub.subscribe("path-neighbors", (message, type) => { + + if (!pathNeighborsTestcompleted) { + try { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + expect(message).to.containSubset(expectedData[id]); + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + pathNeighborsTestcompleted = true; + done(); + }, 5000); + } + } catch (error) { + pathNeighborsTestcompleted = true; + done(error); + } + } + }); + pubSub.publish("test-type", "path-neighbors"); + + }).timeout(asyncTimeout); + +}); \ No newline at end of file diff --git a/tests/npm_tests/testNpmLib.js b/tests/npm_tests/testNpmLib.js new file mode 100644 index 00000000..3faf8f53 --- /dev/null +++ b/tests/npm_tests/testNpmLib.js @@ -0,0 +1,66 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +chai.use(chaiSubset); +const expect = chai.expect; +const volume = "volumetests/"; +const asyncTimeout = 20000; + +const Config = require("../../src/config/config").default; + +const ConfigTest = function () { + + this.retrieve = () => { + const data = (new Config()).default; + + data.test = true; + + return data; + } + + this.save = () => { + return true; + } +}; + +describe("External Connector", function() { + + it("load external connector", function () { + const Worker = require("../../src/worker").default; + const worker = new Worker({ volume, configConnector: ConfigTest }); + const config = worker.config; + expect(config.test).to.equal(true); + }) + .timeout(asyncTimeout); +}); \ No newline at end of file diff --git a/tests/prefixes.test.yml b/tests/prefixes.test.yml index 75f1d064..419e2c1d 100644 --- a/tests/prefixes.test.yml +++ b/tests/prefixes.test.yml @@ -1,3 +1,8 @@ +193.0.0.0/21: + description: rpki valid not monitored AS + asn: 1234 + ignoreMorespecifics: false + 165.254.225.0/24: description: description 1 asn: 15562 @@ -45,6 +50,7 @@ excludeMonitors: - basic-hijack-detection - withdrawal-detection + - rpki-monitor 170.254.205.0/24: description: include exclude test @@ -70,9 +76,12 @@ ignoreMorespecifics: false ignore: false path: - match: ".*2914$" - maxLength: 3 - matchDescription: test description + - match: ".*2915$" + maxLength: 4 + matchDescription: test description1 + - match: ".*2914$" + maxLength: 3 + matchDescription: test description2 99.5.4.3/22: description: path matching test regex and minLength @@ -101,4 +110,6 @@ options: 2914: group: default 3333: + group: default + 13335: group: default \ No newline at end of file diff --git a/tests/proxy_tests/config.proxy.test.yml b/tests/proxy_tests/config.proxy.test.yml new file mode 100644 index 00000000..2e4c27c8 --- /dev/null +++ b/tests/proxy_tests/config.proxy.test.yml @@ -0,0 +1,54 @@ +environment: test + +connectors: + - file: connectorRIS + name: ris + params: + carefulSubscription: true + url: ws://ris-live.ripe.net/v1/ws/ + perMessageDeflate: true + subscription: + moreSpecific: true + type: UPDATE + host: + socketOptions: + includeRaw: false + +monitors: + + +reports: + +httpProxy: http://localhost:8001 + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - tests/prefixes.test.yml + + +checkForUpdatesAtBoot: true + +processMonitors: + - file: uptimeApi + params: + useStatusCodes: true + host: null + port: 8012 + + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +notificationIntervalSeconds: 1800 +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/proxy_tests/tests.js b/tests/proxy_tests/tests.js new file mode 100644 index 00000000..5820b916 --- /dev/null +++ b/tests/proxy_tests/tests.js @@ -0,0 +1,130 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +chai.use(chaiSubset); +const expect = chai.expect; +const axios = require('axios'); + +const asyncTimeout = 120000; +global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; +global.EXTERNAL_CONFIG_FILE = "tests/proxy_tests/config.proxy.test.yml"; + +describe("Composition", function() { + + const worker = require("../../index"); + const pubSub = worker.pubSub; + const config = worker.config; + + describe("Software updates check", function () { + it("new version detected with proxy", function (done) { + + pubSub.subscribe("software-update", function (message, type) { + expect(type).to.equal("software-update"); + done(); + }); + }).timeout(asyncTimeout); + }); + + describe("Configuration loader", function () { + + it("config structure - proxy config loaded", function () { + expect(config).to.have + .keys([ + "alertOnlyOnce", + "checkFadeOffGroupsSeconds", + "checkForUpdatesAtBoot", + "connectors", + "environment", + "fadeOffSeconds", + "httpProxy", + "logging", + "maxMessagesPerSecond", + "monitoredPrefixesFiles", + "monitors", + "multiProcess", + "notificationIntervalSeconds", + "pidFile", + "processMonitors", + "reports", + "volume" + ]); + expect(config.connectors[0]).to.have + .property('class') + }); + + }); + + describe("Uptime Monitor", function () { + + it("uptime config", function () { + expect(config.processMonitors[0]).to + .containSubset({ + params: { + useStatusCodes: true, + host: null, + port: 8012 + } + }); + }); + + // it("RIS connected with proxy", function (done) { + // + // const port = config.processMonitors[0].params.port; + // + // const action = () => { + // return axios({ + // method: 'get', + // responseType: 'json', + // url: `http://localhost:${port}/status` + // }) + // .then(data => { + // expect(data.status).to.equal(200); + // expect(data.data.warning).to.equal(false); + // done(); + // }); + // } + // + // + // action() + // .catch(() => { + // return action(); // Trying again + // }) + // .catch(error => { + // console.log(error); + // }); + // + // }).timeout(asyncTimeout); + }); + +}); \ No newline at end of file diff --git a/tests/reports_tests/config.kafka.test.yml b/tests/reports_tests/config.kafka.test.yml new file mode 100644 index 00000000..6c67c987 --- /dev/null +++ b/tests/reports_tests/config.kafka.test.yml @@ -0,0 +1,288 @@ +connectors: + - file: connectorRIS + name: ris + params: + carefulSubscription: true + url: ws://ris-live.ripe.net/v1/ws/ + perMessageDeflate: true + subscription: + moreSpecific: true + type: UPDATE + host: + socketOptions: + includeRaw: false + +monitors: + - file: monitorHijack + channel: hijack + name: basic-hijack-detection + params: + thresholdMinPeers: 2 + + - file: monitorNewPrefix + channel: newprefix + name: prefix-detection + params: + thresholdMinPeers: 2 + + - file: monitorPath + channel: path + name: path-matching + params: + thresholdMinPeers: 0 + + - file: monitorVisibility + channel: visibility + name: withdrawal-detection + params: + thresholdMinPeers: 20 + + - file: monitorAS + channel: misconfiguration + name: asn-monitor + params: + thresholdMinPeers: 2 + + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + thresholdMinPeers: 1 + checkUncovered: false + preCacheROAs: true + refreshVrpListMinutes: 15 + +reports: + - file: reportFile + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + persistAlertData: false + alertDataDirectory: alertdata/ + +# - file: reportEmail +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# showPaths: 5 # Amount of AS_PATHs to report in the alert +# senderEmail: bgpalerter@xxxx +# # BGPalerter uses nodemailer. +# # The smtp section can be configured with all the parameters available at https://nodemailer.com/smtp/ +# # the following are just the most useful one +# smtp: +# host: localhost +# port: 25 +# secure: false # If true the connection will use TLS when connecting to server. If false it will be still possible doing connection upgrade via STARTTLS +# ignoreTLS: false # If true TLS will be completely disabled, including STARTTLS. Set this to true if you see certificate errors in the logs. +# auth: +# user: username +# pass: password +# type: login +# tls: +# rejectUnauthorized: true # Reject unauthorized certificates +# notifiedEmails: +# default: +# - joe@example.org +# - noc@example.org + +# - file: reportSlack +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# showPaths: 0 # Amount of AS_PATHs to report in the alert +# colors: +# hijack: '#d60b1c' +# newprefix: '#fa9548' +# visibility: '#fad648' +# path: '#42cbf5' +# rpki: '#d892f0' +# hooks: +# default: _YOUR_SLACK_WEBHOOK_URL_ + +# - file: reportKafka +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# host: localhost +# port: 9092 +# topics: +# default: bgpalerter + +# - file: reportSyslog +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - asn-monitor +# - misconfiguration +# - rpki +# params: +# host: 127.0.0.1 +# port: 514 +# transport: udp +# templates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md +# default: "++BGPalerter-3-${type}: ${summary}|${earliest}|${latest}" +# hijack: "++BGPalerter-5-${type}: ${summary}|${prefix}|${description}|${asn}|${newprefix}|${neworigin}|${earliest}|${latest}|${peers}" +# newprefix: "++BGPalerter-4-${type}: ${summary}|${prefix}|${description}|${asn}|${newprefix}|${neworigin}|${earliest}|${latest}|${peers}" +# visibility: "++BGPalerter-5-${type}: ${summary}|${prefix}|${description}|${asn}|${earliest}|${latest}|${peers}" +# misconfiguration: "++BGPalerter-3-${type}: ${summary}|${asn}|${prefix}|${earliest}|${latest}" + +# - file: reportAlerta +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# severity: +# hijack: critical +# newprefix: informational +# visibility: debug +# path: trace +# resourceTemplates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md +# default: "${type}" +# hijack: "hijack::${prefix}@@${asn}" +# newprefix: "newprefix::${prefix}@@${asn}" +# visibility: "visibility::${prefix}@@${asn}" +# urls: +# default: _YOUR_ALERTA_API_URL_ + +# - file: reportWebex +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# hooks: +# default: _YOUR_WEBEX_WEBHOOK_URL_ + +# - file: reportHTTP +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# templates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md +# default: '{"text": "${summary}"}' +# headers: +# isTemplateJSON: true +# showPaths: 0 # Amount of AS_PATHs to report in the alert +# hooks: +# default: _YOUR_WEBHOOK_URL_ + +# - file: reportTelegram +# channels: +# - hijack +# - newprefix +# - visibility +# - path +# - misconfiguration +# - rpki +# params: +# showPaths: 0 # Amount of AS_PATHs to report in the alert +# botUrl: https://api.telegram.org/bot<_BOT_ID_>/sendMessage +# chatIds: +# default: _CHAT_ID_ + + +############################ +# Notification settings: +# - notificationIntervalSeconds +# Defines the amount of seconds after which an alert can be repeated. An alert is repeated only if the event that +# triggered it is not yet solved. +# - persistStatus +# Persist the status of BGPalerter. If the process is restarted, the list of alerts already sent is recovered +# and they are not repeated. The process must be able to write on disc, this option will create a file inside .cache/ + +notificationIntervalSeconds: 14400 +persistStatus: true + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: false + +checkForUpdatesAtBoot: true + +############################ +# Process monitoring settings: +# Uncomment or add classes under processMonitors if you want to monitor or send logs about the status of the BGPalerter process + +#processMonitors: +# - file: uptimeApi +# params: +# useStatusCodes: true +# host: null +# port: 8011 +# +# - file: uptimeHealthcheck +# params: +# url: url_to_poll +# intervalSeconds: 300 +# method: get +# +# - file: sentryModule +# params: +# dsn: https://@sentry.io/ + + +############################ +# The files containing the monitored prefixes. Please see prefixes.yml for an example. +# This is an array (use new lines and dashes!) + +monitoredPrefixesFiles: + - prefixes.yml + + +############################ +# HTTP proxy setting: +# Allow to run BGPalerter behind an HTTP/HTTPS proxy. +# You can also specify which module can bypass the proxy. +# More information here: https://github.com/nttgin/BGPalerter/blob/main/docs/http-proxy.md + +# httpProxy: http://username:password@127.0.0.1:9000 + + +############################ +# Advanced settings (Don't touch here!) +# Please, refer to the documentation to know the meaning of the following parameters. + +alertOnlyOnce: false +fadeOffSeconds: 360 +checkFadeOffGroupsSeconds: 30 +pidFile: bgpalerter.pid +maxMessagesPerSecond: 6000 +multiProcess: false +environment: production + diff --git a/tests/reports_tests/config.reports.test.yml b/tests/reports_tests/config.reports.test.yml new file mode 100644 index 00000000..52e7daf4 --- /dev/null +++ b/tests/reports_tests/config.reports.test.yml @@ -0,0 +1,108 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorHijack + channel: hijack + name: basic-hijack-detection + params: + thresholdMinPeers: 0 + + - file: monitorNewPrefix + channel: newprefix + name: prefix-detection + params: + thresholdMinPeers: 0 + + - file: monitorVisibility + channel: visibility + name: withdrawal-detection + params: + thresholdMinPeers: 4 + + - file: monitorPath + channel: path + name: path-matching + params: + thresholdMinPeers: 0 + + - file: monitorAS + channel: misconfiguration + name: asn-monitor + params: + thresholdMinPeers: 2 + +# - file: monitorRPKI +# channel: rpki +# name: rpki-monitor +# params: +# thresholdMinPeers: 1 +# checkUncovered: true + + +reports: + - file: reportSyslog + channels: + - hijack + - newprefix + - visibility + - path + - asn-monitor + - misconfiguration + - rpki + params: + host: 127.0.0.1 + port: 1516 + transport: udp + templates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md + default: "++BGPalerter-3-${type}: ${summary}|${earliest}|${latest}" + hijack: "++BGPalerter-5-${type}: ${summary}|${prefix}|${description}|${asn}|${newprefix}|${neworigin}|${earliest}|${latest}|${peers}" + newprefix: "++BGPalerter-4-${type}: ${summary}|${prefix}|${description}|${asn}|${newprefix}|${neworigin}|${earliest}|${latest}|${peers}" + visibility: "++BGPalerter-5-${type}: ${summary}|${prefix}|${description}|${asn}|${earliest}|${latest}|${peers}" + misconfiguration: "++BGPalerter-3-${type}: ${summary}|${asn}|${prefix}|${earliest}|${latest}" + + - file: reportHTTP + channels: + - hijack + - newprefix + - visibility + - path + - misconfiguration + - rpki + params: + templates: # See here how to write a template https://github.com/nttgin/BGPalerter/blob/main/docs/context.md + default: '{"text": "${summary}"}' + headers: + isTemplateJSON: true + showPaths: 5 # Amount of AS_PATHs to report in the alert + hooks: + default: http://localhost:8090/test + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - tests/prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: false +persistStatus: false + + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/reports_tests/testReportSyslog.js b/tests/reports_tests/testReportSyslog.js new file mode 100644 index 00000000..686e8e3e --- /dev/null +++ b/tests/reports_tests/testReportSyslog.js @@ -0,0 +1,79 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const Syslogd = require("syslogd"); +const expect = chai.expect; +const asyncTimeout = 20000; +chai.use(chaiSubset); + +global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; +global.EXTERNAL_CONFIG_FILE = "tests/reports_tests/config.reports.test.yml"; + +describe("Reports 1", function() { + const worker = require("../../index"); + const pubSub = worker.pubSub; + + it("syslog", function (done) { + let doneCalled = false; + + let expectedData = [ + "The prefix 2a00:5884::/32 (alarig fix test) has been withdrawn.", + "The prefix 165.254.225.0/24 (description 1) has been withdrawn.", + "The prefix 2001:db8:123::/48 (exact matching test) has been withdrawn." + ]; + + Syslogd(function(info) { + if (!doneCalled) { + expect(info.hostname).to.equals('127.0.0.1'); + const object = expectedData.filter(i => info.msg.startsWith(i))[0]; + expectedData = expectedData.filter(i => i !== object); + + if (object) { + if (expectedData.length === 0) { + done(); + doneCalled = true; + } + } + } + }) + .listen(1516, function(error) { + if (error) { + console.log(error) + } + }); + + pubSub.publish("test-type", "visibility"); + }).timeout(asyncTimeout); + +}); \ No newline at end of file diff --git a/tests/reports_tests/testsReportHttp.js b/tests/reports_tests/testsReportHttp.js new file mode 100644 index 00000000..b63f1b07 --- /dev/null +++ b/tests/reports_tests/testsReportHttp.js @@ -0,0 +1,73 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const restify = require("restify"); +const asyncTimeout = 120000; +chai.use(chaiSubset); +const assert = chai.assert + +global.EXTERNAL_VERSION_FOR_TEST = "0.0.1"; +global.EXTERNAL_CONFIG_FILE = "tests/reports_tests/config.reports.test.yml"; + +describe("Reports 2", function() { + const worker = require("../../index"); + const pubSub = worker.pubSub; + + it("reportHTTP", function (done) { + const server = restify.createServer(); + server.pre(restify.pre.sanitizePath()); + server.use(restify.plugins.bodyParser({ mapParams: true })); + let expectedData = [ + "The prefix 2a00:5884::/32 (alarig fix test) is announced by AS15563 instead of AS204092, and AS45. Top 1 most used AS paths: [2,3,15563].", + "A new prefix 165.254.255.0/25 is announced by AS15562, and AS4. It should be instead 165.254.255.0/24 (description 2) announced by AS15562. Top 1 most used AS paths: [2,3,[15562,4]].", + "A new prefix 2a00:5884:ffff::/48 is announced by AS208585. It should be instead 2a00:5884::/32 (alarig fix test) announced by AS204092, and AS45. Top 1 most used AS paths: [2,3,208585].", + ]; + + pubSub.publish("test-type", "hijack"); + server.post('/test', function (req, res, next) { + const text = req.body.text; + if (expectedData.includes(text)) { + expectedData = expectedData.filter(i => i !== text); + } else { + assert.fail(text, "none", "The message is not expected"); + } + + if (expectedData.length === 0) { + done(); + } + }); + server.listen(8090); + + }).timeout(asyncTimeout); +}); \ No newline at end of file diff --git a/tests/rpki_tests/config.rpki.test.api.yml b/tests/rpki_tests/config.rpki.test.api.yml new file mode 100644 index 00000000..1248657a --- /dev/null +++ b/tests/rpki_tests/config.rpki.test.api.yml @@ -0,0 +1,45 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + thresholdMinPeers: 1 + checkUncovered: true + cacheValidPrefixesSeconds: 3600 + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - tests/prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: false +persistStatus: false + +rpki: + vrpProvider: api + url: https://rpki.gin.ntt.net/api/export.json + preCacheROAs: true + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/rpki_tests/config.rpki.test.default.yml b/tests/rpki_tests/config.rpki.test.default.yml new file mode 100644 index 00000000..38ad34e7 --- /dev/null +++ b/tests/rpki_tests/config.rpki.test.default.yml @@ -0,0 +1,44 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + thresholdMinPeers: 1 + checkUncovered: true + cacheValidPrefixesSeconds: 3600 + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - tests/prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: false +persistStatus: false + +rpki: + preCacheROAs: true + refreshVrpListMinutes: 15 + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/rpki_tests/config.rpki.test.external-roas.yml b/tests/rpki_tests/config.rpki.test.external-roas.yml new file mode 100644 index 00000000..31cc77e4 --- /dev/null +++ b/tests/rpki_tests/config.rpki.test.external-roas.yml @@ -0,0 +1,51 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorROAS + channel: rpki + name: rpki-monitor + params: + enableDiffAlerts: true + enableExpirationAlerts: true + enableExpirationCheckTA: true + enableDeletedCheckTA: true + roaExpirationAlertHours: 2 + checkOnlyASns: false + toleranceDeletedRoasTA: 20 + toleranceExpiredRoasTA: 20 + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - tests/prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: false +persistStatus: false + +rpki: + preCacheROAs: true + refreshVrpListMinutes: 15 + vrpFile: tests/rpki_tests/roas.json + + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 10 +checkFadeOffGroupsSeconds: 2 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/rpki_tests/config.rpki.test.external.yml b/tests/rpki_tests/config.rpki.test.external.yml new file mode 100644 index 00000000..bffbcc3d --- /dev/null +++ b/tests/rpki_tests/config.rpki.test.external.yml @@ -0,0 +1,47 @@ +environment: test + +connectors: + - file: connectorTest + name: tes + params: + testType: withdrawal + +monitors: + - file: monitorRPKI + channel: rpki + name: rpki-monitor + params: + checkDisappearing: true + thresholdMinPeers: 1 + checkUncovered: true + cacheValidPrefixesSeconds: 3600 + + +# The file containing the monitored prefixes. Please see monitored_prefixes_test.yml for an example +# This is an array (use new lines and dashes!) +monitoredPrefixesFiles: + - tests/prefixes.test.yml + +logging: + directory: logs + logRotatePattern: YYYY-MM-DD # Whenever the pattern changes, a new file is created and the old one rotated + maxRetainedFiles: 10 + maxFileSizeMB: 15 + compressOnRotation: true + +checkForUpdatesAtBoot: false +persistStatus: false + +rpki: + preCacheROAs: true + refreshVrpListMinutes: 15 + vrpFile: tests/rpki_tests/vrp.json + + +notificationIntervalSeconds: 1800 # Repeat the same alert (which keeps being triggered) after x seconds +alertOnlyOnce: false +fadeOffSeconds: 1 +checkFadeOffGroupsSeconds: 1 +pidFile: bgpalerter.pid +multiProcess: false +maxMessagesPerSecond: 6000 \ No newline at end of file diff --git a/tests/rpki_tests/roas.after.json b/tests/rpki_tests/roas.after.json new file mode 100644 index 00000000..0ad2c7c3 --- /dev/null +++ b/tests/rpki_tests/roas.after.json @@ -0,0 +1,15 @@ +[ + { + "asn": "2914", + "prefix": "1.2.3.0/24", + "maxLength": 24, + "ta": "ripe", + "expires": 1621691602 + }, + { + "asn": 2914, + "prefix": "2001:db8:123::/48", + "maxLength": 48, + "ta": "ripe" + } +] \ No newline at end of file diff --git a/tests/rpki_tests/roas.before.json b/tests/rpki_tests/roas.before.json new file mode 100644 index 00000000..c17d9389 --- /dev/null +++ b/tests/rpki_tests/roas.before.json @@ -0,0 +1,32 @@ +[ + { + "asn": "2914", + "prefix": "1.2.3.0/24", + "maxLength": 24, + "ta": "ripe" + }, + { + "asn": "AS2914", + "prefix": "2.3.4.0/24", + "maxLength": 24, + "ta": "ripe" + }, + { + "asn": 2914, + "prefix": "2001:db8:123::/48", + "maxLength": 48, + "ta": "ripe" + }, + { + "asn": 65000, + "prefix": "2001:db8:123::/48", + "maxLength": 48, + "ta": "ripe" + }, + { + "asn": 2914, + "prefix": "94.5.4.3/22", + "maxLength": 22, + "ta": "ripe" + } +] \ No newline at end of file diff --git a/tests/rpki_tests/tests.api.js b/tests/rpki_tests/tests.api.js new file mode 100644 index 00000000..76a9084d --- /dev/null +++ b/tests/rpki_tests/tests.api.js @@ -0,0 +1,108 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const fs = require("fs"); +const chaiSubset = require('chai-subset'); +const expect = chai.expect; +const asyncTimeout = 200000; +chai.use(chaiSubset); + +const cacheFile = ".cache/seen-rpki-valid-announcements.json"; +if (fs.existsSync(cacheFile)) { + fs.unlinkSync(cacheFile); +} + +global.EXTERNAL_CONFIG_FILE = "tests/rpki_tests/config.rpki.test.api.yml"; +const worker = require("../../index"); +const pubSub = worker.pubSub; + + +describe("RPKI monitoring api", function() { + + it("api connector", function (done) { + + const expectedData = { + + "a103_21_244_0_24-13335-false": { + id: "a103_21_244_0_24-13335-false", + origin: 'rpki-monitor', + affected: '103.21.244.0/24', + message: 'The route 103.21.244.0/24 announced by AS13335 is not RPKI valid. Valid ROAs: 103.21.244.0/23|AS0|maxLength:23', + }, + + "a8_8_8_8_22-2914-null": { + id: "a8_8_8_8_22-2914-null", + origin: 'rpki-monitor', + affected: '8.8.8.8/22', + message: 'The route 8.8.8.8/22 announced by AS2914 is not covered by a ROA', + } + }; + + let rpkiTestCompleted = false; + let started = false; + pubSub.subscribe("rpki", function (message, type) { + try { + if (started && !rpkiTestCompleted) { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + + expect(message).to + .containSubset(expectedData[id]); + + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + rpkiTestCompleted = true; + done(); + }, 5000); + } + } + } catch (error) { + rpkiTestCompleted = true; + done(error); + } + }); + pubSub.publish("test-type", "rpki"); + started = true; + }).timeout(asyncTimeout); + +}); \ No newline at end of file diff --git a/tests/rpki_tests/tests.default.js b/tests/rpki_tests/tests.default.js new file mode 100644 index 00000000..1188c7ce --- /dev/null +++ b/tests/rpki_tests/tests.default.js @@ -0,0 +1,108 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const fs = require("fs"); +const chaiSubset = require('chai-subset'); +const expect = chai.expect; +const asyncTimeout = 200000; +chai.use(chaiSubset); + +const cacheFile = ".cache/seen-rpki-valid-announcements.json"; +if (fs.existsSync(cacheFile)) { + fs.unlinkSync(cacheFile); +} + +global.EXTERNAL_CONFIG_FILE = "tests/rpki_tests/config.rpki.test.default.yml"; +const worker = require("../../index"); +const pubSub = worker.pubSub; + + +describe("RPKI monitoring default", function() { + + it("default connector", function (done) { + + const expectedData = { + + "a103_21_244_0_24-13335-false": { + id: "a103_21_244_0_24-13335-false", + origin: 'rpki-monitor', + affected: '103.21.244.0/24', + message: 'The route 103.21.244.0/24 announced by AS13335 is not RPKI valid. Valid ROAs: 103.21.244.0/23|AS0|maxLength:23', + }, + + "a8_8_8_8_22-2914-null": { + id: "a8_8_8_8_22-2914-null", + origin: 'rpki-monitor', + affected: '8.8.8.8/22', + message: 'The route 8.8.8.8/22 announced by AS2914 is not covered by a ROA', + } + }; + + let rpkiTestCompleted = false; + let started = false; + pubSub.subscribe("rpki", function (message, type) { + try { + if (started && !rpkiTestCompleted) { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + + expect(message).to + .containSubset(expectedData[id]); + + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + rpkiTestCompleted = true; + done(); + }, 5000); + } + } + } catch (error) { + rpkiTestCompleted = true; + done(error); + } + }); + pubSub.publish("test-type", "rpki"); + started = true; + }).timeout(asyncTimeout); + +}); \ No newline at end of file diff --git a/tests/rpki_tests/tests.external-missing-roas.js b/tests/rpki_tests/tests.external-missing-roas.js new file mode 100644 index 00000000..474324cf --- /dev/null +++ b/tests/rpki_tests/tests.external-missing-roas.js @@ -0,0 +1,101 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const fs = require('fs'); +const expect = chai.expect; +const asyncTimeout = 200000; +chai.use(chaiSubset); + +global.EXTERNAL_CONFIG_FILE = "tests/rpki_tests/config.rpki.test.external.yml"; +fs.copyFileSync("tests/rpki_tests/vrp.missing.json", "tests/rpki_tests/vrp.json"); + +const worker = require("../../index"); +const pubSub = worker.pubSub; + +describe("RPKI monitoring external", function() { + + it("missing roas", function (done) { + + const expectedData = { + + "a82_112_100_0_24-2914-null": { + id: 'a82_112_100_0_24-2914-null', + origin: 'rpki-monitor', + affected: '82.112.100.0/24', + message: 'The route 82.112.100.0/24 announced by AS2914 is no longer covered by a ROA' + } + + }; + + let rpkiTestCompletedExternal = false; + let started = false; + + pubSub.subscribe("rpki", function (message, type) { + try { + if (started && !rpkiTestCompletedExternal) { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + expect(message).to.containSubset(expectedData[id]); + + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + rpkiTestCompletedExternal = true; + fs.unlinkSync("tests/rpki_tests/vrp.json"); + done(); + }, 5000); + } + } + } catch (error) { + rpkiTestCompletedExternal = true; + done(error); + } + }); + + setTimeout(() => { // Wait that the watcher realizes the file changed + pubSub.publish("test-type", "rpki"); + started = true; + }, 20000); + + }).timeout(asyncTimeout); +}); \ No newline at end of file diff --git a/tests/rpki_tests/tests.external-roas.js b/tests/rpki_tests/tests.external-roas.js new file mode 100644 index 00000000..12508460 --- /dev/null +++ b/tests/rpki_tests/tests.external-roas.js @@ -0,0 +1,148 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const fs = require('fs'); +const expect = chai.expect; +const asyncTimeout = 200000; +chai.use(chaiSubset); + +global.EXTERNAL_CONFIG_FILE = "tests/rpki_tests/config.rpki.test.external-roas.yml"; +global.EXTERNAL_ROA_EXPIRATION_TEST = 5000; + +// ROAs before +fs.copyFileSync("tests/rpki_tests/roas.before.json", "tests/rpki_tests/roas.json"); + +setTimeout(() => { + // ROAs after + fs.copyFileSync("tests/rpki_tests/roas.after.json", "tests/rpki_tests/roas.json"); +}, 30000); + +const worker = require("../../index"); +const pubSub = worker.pubSub; + +describe("RPKI monitoring external", function() { + + it("ROA diff and expiration - external connector", function (done) { + + const expectedData = { + "disappeared-ripe": { + id: 'disappeared-ripe', + truncated: false, + origin: 'rpki-monitor', + affected: 'ripe', + message: 'Possible TA malfunction: 60.00% of the ROAs disappeared from ripe', + data: [ + { + affected: 'ripe', + matchedMessage: 'Possible TA malfunction: 60.00% of the ROAs disappeared from ripe' + } + ] + }, + "expiring-ripe": { + id: 'expiring-ripe', + truncated: false, + origin: 'rpki-monitor', + affected: 'ripe', + message: 'Possible TA malfunction: 50.00% of the ROAs are expiring in ripe', + data: [ + { + affected: 'ripe', + matchedMessage: 'Possible TA malfunction: 50.00% of the ROAs are expiring in ripe', + extra: {} + } + ] + } + , + "28c7aa78b6286e0e3c6583797f7df47c": { + id: '28c7aa78b6286e0e3c6583797f7df47c', + truncated: false, + origin: 'rpki-monitor', + affected: 2914, + message: 'The following ROAs will expire in less than 2 hours: <1.2.3.0/24, 2914, 24, ripe>', + + }, + "47807c7558dbe001b4aad9f3a87eb427": { + id: '47807c7558dbe001b4aad9f3a87eb427', + truncated: false, + origin: 'rpki-monitor', + affected: '94.5.4.3/22', + message: 'ROAs change detected: removed <94.5.4.3/22, 2914, 22, ripe>' + }, + "de3bd9a6cdeeb05e1c2c7c04f7220485" : { + id: 'de3bd9a6cdeeb05e1c2c7c04f7220485', + truncated: false, + origin: 'rpki-monitor', + affected: '2001:db8:123::/48', + message: 'ROAs change detected: removed <2001:db8:123::/48, 65000, 48, ripe>' + }, + "129aafe3c8402fb045b71e810a73d425": { + id: '129aafe3c8402fb045b71e810a73d425', + truncated: false, + origin: 'rpki-monitor', + affected: 2914, + message: 'ROAs change detected: removed <2.3.4.0/24, 2914, 24, ripe>' + } + }; + + let rpkiTestCompletedExternal = false; + pubSub.subscribe("rpki", function (message, type) { + + try { + if (!rpkiTestCompletedExternal) { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + expect(message).to.containSubset(expectedData[id]); + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + rpkiTestCompletedExternal = true; + done(); + } + } + } catch (error) { + rpkiTestCompletedExternal = true; + done(error); + } + }); + + }).timeout(asyncTimeout); +}); \ No newline at end of file diff --git a/tests/rpki_tests/tests.external.js b/tests/rpki_tests/tests.external.js new file mode 100644 index 00000000..1e8e808b --- /dev/null +++ b/tests/rpki_tests/tests.external.js @@ -0,0 +1,106 @@ +/* + * BSD 3-Clause License + * + * Copyright (c) 2019, NTT Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +const chai = require("chai"); +const chaiSubset = require('chai-subset'); +const fs = require('fs'); +const expect = chai.expect; +const asyncTimeout = 200000; +chai.use(chaiSubset); + +global.EXTERNAL_CONFIG_FILE = "tests/rpki_tests/config.rpki.test.external.yml"; + +fs.copyFileSync("tests/rpki_tests/vrp.wrong.json", "tests/rpki_tests/vrp.json"); + +const worker = require("../../index"); +const pubSub = worker.pubSub; + +describe("RPKI monitoring external", function() { + + //Test rpki watch file reloading + fs.copyFileSync("tests/rpki_tests/vrp.correct.json", "tests/rpki_tests/vrp.json"); + + it("external connector", function (done) { + + const expectedData = { + + "a82_112_100_0_24-2914-false" : { + id: 'a82_112_100_0_24-2914-false', + origin: 'rpki-monitor', + affected: '82.112.100.0/24', + message: 'The route 82.112.100.0/24 announced by AS2914 is not RPKI valid. Valid ROAs: 82.112.100.0/24|AS1234|maxLength:24' + } + }; + + let rpkiTestCompletedExternal = false; + let started = false; + + pubSub.subscribe("rpki", function (message, type) { + try { + if (started && !rpkiTestCompletedExternal) { + message = JSON.parse(JSON.stringify(message)); + const id = message.id; + + expect(Object.keys(expectedData).includes(id)).to.equal(true); + expect(expectedData[id] != null).to.equal(true); + + expect(message).to + .containSubset(expectedData[id]); + + expect(message).to.contain + .keys([ + "latest", + "earliest" + ]); + + delete expectedData[id]; + if (Object.keys(expectedData).length === 0) { + setTimeout(() => { + rpkiTestCompletedExternal = true; + fs.unlinkSync("tests/rpki_tests/vrp.json"); + done(); + }, 8000); + } + } + } catch (error) { + rpkiTestCompletedExternal = true; + done(error); + } + }); + + setTimeout(() => { // Wait that the watcher realizes the file changed + pubSub.publish("test-type", "rpki"); + started = true; + }, 16000); + + }).timeout(asyncTimeout); +}); \ No newline at end of file diff --git a/tests/rpki_tests/vrp.correct.json b/tests/rpki_tests/vrp.correct.json new file mode 100644 index 00000000..aee5da44 --- /dev/null +++ b/tests/rpki_tests/vrp.correct.json @@ -0,0 +1,17 @@ +[ + { + "asn": "13335", + "prefix": "103.21.244.0/24", + "maxLength": 24 + }, + { + "asn": "AS2914", + "prefix": "8.8.8.0/22", + "maxLength": 24 + }, + { + "asn": 1234, + "prefix": "82.112.100.0/24", + "maxLength": 24 + } +] \ No newline at end of file diff --git a/tests/rpki_tests/vrp.missing.json b/tests/rpki_tests/vrp.missing.json new file mode 100644 index 00000000..1d628134 --- /dev/null +++ b/tests/rpki_tests/vrp.missing.json @@ -0,0 +1,12 @@ +[ + { + "asn": "13335", + "prefix": "103.21.244.0/24", + "maxLength": 24 + }, + { + "asn": "AS2914", + "prefix": "8.8.8.0/22", + "maxLength": 24 + } +] \ No newline at end of file diff --git a/tests/rpki_tests/vrp.wrong.json b/tests/rpki_tests/vrp.wrong.json new file mode 100644 index 00000000..f08818f3 --- /dev/null +++ b/tests/rpki_tests/vrp.wrong.json @@ -0,0 +1,12 @@ +[ + { + "asn": "13335", + "prefix": "103.21.244.0/24", + "maxLength": 24 + }, + { + "asn": "2914", + "prefix": "123.4.5.0/14", + "maxLength": 24 + } +] \ No newline at end of file